diff --git a/.github/workflows/release-desktop-platform.yml b/.github/workflows/release-desktop-platform.yml index 93ab1fe365..91b8e761bd 100644 --- a/.github/workflows/release-desktop-platform.yml +++ b/.github/workflows/release-desktop-platform.yml @@ -174,7 +174,7 @@ jobs: run: | mkdir -p builds mv packages/frontend/apps/electron/out/*/make/zip/linux/${{ inputs.arch }}/*.zip ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.zip - mv packages/frontend/apps/electron/out/*/make/*.AppImage ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.appimage + mv packages/frontend/apps/electron/out/*/make/AppImage/${{ inputs.arch }}/*.AppImage ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.appimage mv packages/frontend/apps/electron/out/*/make/deb/${{ inputs.arch }}/*.deb ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.deb mv packages/frontend/apps/electron/out/*/make/flatpak/*/*.flatpak ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.flatpak diff --git a/package.json b/package.json index 2789f6b867..71ffb21f79 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "oxlint": "^1.47.0", "prettier": "^3.7.4", "semver": "^7.7.3", - "typescript": "^5.7.2", + "typescript": "^5.9.3", "typescript-eslint": "^8.55.0", "unplugin-swc": "^1.5.9", "vite": "^7.2.7", diff --git a/packages/backend/server/package.json b/packages/backend/server/package.json index 5b95a59619..60f9343ae5 100644 --- a/packages/backend/server/package.json +++ b/packages/backend/server/package.json @@ -96,9 +96,7 @@ "is-mobile": "^5.0.0", "jose": "^6.1.3", "jsonwebtoken": "^9.0.3", - "keyv": "^5.2.2", "lodash-es": "^4.17.23", - "mixpanel": "^0.18.0", "mustache": "^4.2.0", "nanoid": "^5.1.6", "nest-commander": "^3.15.0", @@ -136,7 +134,6 @@ "@types/http-errors": "^2.0.4", "@types/jsonwebtoken": "^9.0.9", "@types/lodash-es": "^4.17.12", - "@types/mixpanel": "^2.14.9", "@types/mustache": "^4.2.5", "@types/node": "^22.0.0", "@types/nodemailer": "^7.0.0", @@ -153,8 +150,7 @@ "sinon": "^21.0.1", "socket.io-client": "^4.8.3", "supertest": "^7.1.4", - "ts-node": "^10.9.2", - "typescript": "^5.7.2", + "typescript": "^5.9.3", "why-is-node-running": "^3.2.2" }, "nodemonConfig": { diff --git a/packages/backend/server/scripts/docker-clean.mjs b/packages/backend/server/scripts/docker-clean.mjs index 883d6d1897..cb92094543 100644 --- a/packages/backend/server/scripts/docker-clean.mjs +++ b/packages/backend/server/scripts/docker-clean.mjs @@ -342,7 +342,6 @@ await prunePrismaEngines(APP_ROOT, targetKey); await Promise.all([ rmrf(path.join(APP_ROOT, 'node_modules', 'typescript')).catch(() => {}), - rmrf(path.join(APP_ROOT, 'node_modules', 'ts-node')).catch(() => {}), rmrf(path.join(APP_ROOT, 'node_modules', '@types')).catch(() => {}), rmrf(path.join(APP_ROOT, 'src')).catch(() => {}), rmrf(path.join(APP_ROOT, '.gitignore')).catch(() => {}), diff --git a/packages/backend/server/src/base/config/__tests__/config.spec.ts b/packages/backend/server/src/base/config/__tests__/config.spec.ts index 0ae9c88d1e..fe5322d2cf 100644 --- a/packages/backend/server/src/base/config/__tests__/config.spec.ts +++ b/packages/backend/server/src/base/config/__tests__/config.spec.ts @@ -2,8 +2,8 @@ import test from 'ava'; import { createModule } from '../../../__tests__/create-module'; import { InvalidAppConfig } from '../../error'; -import { ConfigFactory, ConfigModule } from '..'; import { Config } from '../config'; +import { ConfigFactory, ConfigModule } from '../index'; import { override } from '../register'; const module = await createModule(); diff --git a/packages/backend/server/src/base/job/queue/__tests__/queue.spec.ts b/packages/backend/server/src/base/job/queue/__tests__/queue.spec.ts index 27e15d3b1c..1d770fac14 100644 --- a/packages/backend/server/src/base/job/queue/__tests__/queue.spec.ts +++ b/packages/backend/server/src/base/job/queue/__tests__/queue.spec.ts @@ -8,8 +8,8 @@ import Sinon from 'sinon'; import { createTestingModule } from '../../../../__tests__/utils'; import { ConfigModule } from '../../../config'; import { metrics } from '../../../metrics'; -import { JobModule, JobQueue, OnJob } from '..'; import { JobExecutor } from '../executor'; +import { JobModule, JobQueue, OnJob } from '../index'; import { JobHandlerScanner } from '../scanner'; let module: TestingModule; diff --git a/packages/backend/server/src/core/comment/__tests__/service.spec.ts b/packages/backend/server/src/core/comment/__tests__/service.spec.ts index 0816299d4d..58987f477e 100644 --- a/packages/backend/server/src/core/comment/__tests__/service.spec.ts +++ b/packages/backend/server/src/core/comment/__tests__/service.spec.ts @@ -5,7 +5,7 @@ import test from 'ava'; import { createModule } from '../../../__tests__/create-module'; import { Mockers } from '../../../__tests__/mocks'; import { Comment, CommentChangeAction } from '../../../models'; -import { CommentModule } from '..'; +import { CommentModule } from '../index'; import { CommentService } from '../service'; const module = await createModule({ diff --git a/packages/backend/server/src/core/doc/__tests__/event.spec.ts b/packages/backend/server/src/core/doc/__tests__/event.spec.ts index 5a91f194c9..d1483b90c1 100644 --- a/packages/backend/server/src/core/doc/__tests__/event.spec.ts +++ b/packages/backend/server/src/core/doc/__tests__/event.spec.ts @@ -10,8 +10,8 @@ import { type TestingModule, } from '../../../__tests__/utils'; import { Models, User, Workspace } from '../../../models'; -import { DocReader, PgWorkspaceDocStorageAdapter as Adapter } from '..'; import { DocEventsListener } from '../event'; +import { DocReader, PgWorkspaceDocStorageAdapter as Adapter } from '../index'; interface Context { module: TestingModule; diff --git a/packages/backend/server/src/core/doc/__tests__/reader-from-database.spec.ts b/packages/backend/server/src/core/doc/__tests__/reader-from-database.spec.ts index 4294c7a7bd..dfdd005f18 100644 --- a/packages/backend/server/src/core/doc/__tests__/reader-from-database.spec.ts +++ b/packages/backend/server/src/core/doc/__tests__/reader-from-database.spec.ts @@ -7,7 +7,11 @@ import { applyUpdate, Doc as YDoc } from 'yjs'; import { createModule } from '../../../__tests__/create-module'; import { Mockers } from '../../../__tests__/mocks'; import { Models } from '../../../models'; -import { DocReader, DocStorageModule, PgWorkspaceDocStorageAdapter } from '..'; +import { + DocReader, + DocStorageModule, + PgWorkspaceDocStorageAdapter, +} from '../index'; import { DatabaseDocReader } from '../reader'; const module = await createModule({ diff --git a/packages/backend/server/src/core/doc/__tests__/reader-from-rpc.spec.ts b/packages/backend/server/src/core/doc/__tests__/reader-from-rpc.spec.ts index d43488d36f..73bba76078 100644 --- a/packages/backend/server/src/core/doc/__tests__/reader-from-rpc.spec.ts +++ b/packages/backend/server/src/core/doc/__tests__/reader-from-rpc.spec.ts @@ -16,7 +16,7 @@ import { DocReader, DocStorageModule, PgWorkspaceDocStorageAdapter, -} from '..'; +} from '../index'; import { RpcDocReader } from '../reader'; const module = await createModule({ diff --git a/packages/backend/server/src/core/notification/__tests__/service.spec.ts b/packages/backend/server/src/core/notification/__tests__/service.spec.ts index 3aa70896d6..dd1f4fd540 100644 --- a/packages/backend/server/src/core/notification/__tests__/service.spec.ts +++ b/packages/backend/server/src/core/notification/__tests__/service.spec.ts @@ -20,7 +20,7 @@ import { FeatureModule } from '../../features'; import { MailModule } from '../../mail'; import { PermissionModule } from '../../permission'; import { StorageModule } from '../../storage'; -import { NotificationModule } from '..'; +import { NotificationModule } from '../index'; import { NotificationService } from '../service'; const module = await createModule({ diff --git a/packages/backend/server/src/core/permission/__tests__/doc.spec.ts b/packages/backend/server/src/core/permission/__tests__/doc.spec.ts index 85f9881aa6..e3b710c5eb 100644 --- a/packages/backend/server/src/core/permission/__tests__/doc.spec.ts +++ b/packages/backend/server/src/core/permission/__tests__/doc.spec.ts @@ -8,8 +8,8 @@ import { WorkspaceMemberStatus, WorkspaceRole, } from '../../../models'; -import { PermissionModule } from '..'; import { DocAccessController } from '../doc'; +import { PermissionModule } from '../index'; import { DocRole, mapDocRoleToPermissions } from '../types'; let module: TestingModule; diff --git a/packages/backend/server/src/core/permission/__tests__/docs.spec.ts b/packages/backend/server/src/core/permission/__tests__/docs.spec.ts index 5ba1431ffd..d10bed8583 100644 --- a/packages/backend/server/src/core/permission/__tests__/docs.spec.ts +++ b/packages/backend/server/src/core/permission/__tests__/docs.spec.ts @@ -2,8 +2,8 @@ import test from 'ava'; import { createModule } from '../../../__tests__/create-module'; import { Mockers } from '../../../__tests__/mocks'; -import { DocRole, PermissionModule, WorkspaceRole } from '..'; import { AccessControllerBuilder } from '../builder'; +import { DocRole, PermissionModule, WorkspaceRole } from '../index'; const module = await createModule({ imports: [PermissionModule], diff --git a/packages/backend/server/src/core/permission/__tests__/workspace.spec.ts b/packages/backend/server/src/core/permission/__tests__/workspace.spec.ts index 1839cb9979..d359572d2a 100644 --- a/packages/backend/server/src/core/permission/__tests__/workspace.spec.ts +++ b/packages/backend/server/src/core/permission/__tests__/workspace.spec.ts @@ -8,7 +8,7 @@ import { WorkspaceMemberStatus, WorkspaceRole, } from '../../../models'; -import { PermissionModule } from '..'; +import { PermissionModule } from '../index'; import { mapWorkspaceRoleToPermissions } from '../types'; import { WorkspaceAccessController } from '../workspace'; diff --git a/packages/backend/server/src/core/storage/__tests__/comment-attachment.spec.ts b/packages/backend/server/src/core/storage/__tests__/comment-attachment.spec.ts index edb52d5f02..d99729fceb 100644 --- a/packages/backend/server/src/core/storage/__tests__/comment-attachment.spec.ts +++ b/packages/backend/server/src/core/storage/__tests__/comment-attachment.spec.ts @@ -6,7 +6,7 @@ import test from 'ava'; import { createModule } from '../../../__tests__/create-module'; import { Mockers } from '../../../__tests__/mocks'; import { Models } from '../../../models'; -import { CommentAttachmentStorage, StorageModule } from '..'; +import { CommentAttachmentStorage, StorageModule } from '../index'; const module = await createModule({ imports: [StorageModule], diff --git a/packages/backend/server/src/mails/index.tsx b/packages/backend/server/src/mails/index.tsx index 40e9a34db0..be72c08aef 100644 --- a/packages/backend/server/src/mails/index.tsx +++ b/packages/backend/server/src/mails/index.tsx @@ -1,4 +1,5 @@ import { render as rawRender } from '@react-email/components'; +import { type ComponentType, createElement, type ReactElement } from 'react'; import { Comment, CommentMention, Mention } from './docs'; import { @@ -40,14 +41,14 @@ type EmailContent = { html: string; }; -function render(component: React.ReactElement) { +function render(component: ReactElement) { return rawRender(component, { pretty: env.testing }); } -type Props = T extends React.ComponentType ? P : never; +type Props = T extends ComponentType ? P : never; export type EmailRenderer = (props: Props) => Promise; -function make>( +function make>( Component: T, subject: string | ((props: Props) => string) ): EmailRenderer> { @@ -58,7 +59,7 @@ function make>( } return { subject: typeof subject === 'function' ? subject(props) : subject, - html: await render(), + html: await render(createElement(Component, props)), }; }; } diff --git a/packages/backend/server/src/models/__tests__/access-token.spec.ts b/packages/backend/server/src/models/__tests__/access-token.spec.ts index 3e7ee27a43..7bd6ac931c 100644 --- a/packages/backend/server/src/models/__tests__/access-token.spec.ts +++ b/packages/backend/server/src/models/__tests__/access-token.spec.ts @@ -4,7 +4,7 @@ import test from 'ava'; import { createModule } from '../../__tests__/create-module'; import { Mockers } from '../../__tests__/mocks'; import { Due } from '../../base'; -import { Models } from '..'; +import { Models } from '../index'; const module = await createModule(); const models = module.get(Models); diff --git a/packages/backend/server/src/models/__tests__/blob.spec.ts b/packages/backend/server/src/models/__tests__/blob.spec.ts index 98156d966e..04697f87a1 100644 --- a/packages/backend/server/src/models/__tests__/blob.spec.ts +++ b/packages/backend/server/src/models/__tests__/blob.spec.ts @@ -2,7 +2,7 @@ import test from 'ava'; import { createModule } from '../../__tests__/create-module'; import { Mockers } from '../../__tests__/mocks'; -import { Models } from '..'; +import { Models } from '../index'; const module = await createModule(); const models = module.get(Models); diff --git a/packages/backend/server/src/models/__tests__/comment-attachment.spec.ts b/packages/backend/server/src/models/__tests__/comment-attachment.spec.ts index ade6ecabce..7d22c775f4 100644 --- a/packages/backend/server/src/models/__tests__/comment-attachment.spec.ts +++ b/packages/backend/server/src/models/__tests__/comment-attachment.spec.ts @@ -2,7 +2,7 @@ import test from 'ava'; import { createModule } from '../../__tests__/create-module'; import { Mockers } from '../../__tests__/mocks'; -import { Models } from '..'; +import { Models } from '../index'; const module = await createModule(); const models = module.get(Models); diff --git a/packages/backend/server/src/models/__tests__/comment.spec.ts b/packages/backend/server/src/models/__tests__/comment.spec.ts index 0ad1baeb27..c55e81caa5 100644 --- a/packages/backend/server/src/models/__tests__/comment.spec.ts +++ b/packages/backend/server/src/models/__tests__/comment.spec.ts @@ -4,8 +4,8 @@ import test from 'ava'; import { createModule } from '../../__tests__/create-module'; import { Mockers } from '../../__tests__/mocks'; -import { Models } from '..'; import { CommentChangeAction, Reply } from '../comment'; +import { Models } from '../index'; const module = await createModule({}); diff --git a/packages/backend/server/src/models/__tests__/doc.spec.ts b/packages/backend/server/src/models/__tests__/doc.spec.ts index 4e9ec935a9..90ab3297fc 100644 --- a/packages/backend/server/src/models/__tests__/doc.spec.ts +++ b/packages/backend/server/src/models/__tests__/doc.spec.ts @@ -4,7 +4,7 @@ import test from 'ava'; import { createModule } from '../../__tests__/create-module'; import { Mockers } from '../../__tests__/mocks'; -import { Models } from '..'; +import { Models } from '../index'; const module = await createModule({}); diff --git a/packages/backend/server/src/models/__tests__/user-settings.spec.ts b/packages/backend/server/src/models/__tests__/user-settings.spec.ts index 27e2d8e4c3..bec95e1179 100644 --- a/packages/backend/server/src/models/__tests__/user-settings.spec.ts +++ b/packages/backend/server/src/models/__tests__/user-settings.spec.ts @@ -3,7 +3,7 @@ import { ZodError } from 'zod'; import { createModule } from '../../__tests__/create-module'; import { Mockers } from '../../__tests__/mocks'; -import { Models } from '..'; +import { Models } from '../index'; const module = await createModule(); const models = module.get(Models); diff --git a/packages/backend/server/src/models/calendar-account.ts b/packages/backend/server/src/models/calendar-account.ts index b526545bff..d92e2ee4ca 100644 --- a/packages/backend/server/src/models/calendar-account.ts +++ b/packages/backend/server/src/models/calendar-account.ts @@ -1,4 +1,5 @@ import { Injectable } from '@nestjs/common'; +import { Transactional } from '@nestjs-cls/transactional'; import type { CalendarAccount, Prisma } from '@prisma/client'; import { CryptoHelper } from '../base'; @@ -174,6 +175,18 @@ export class CalendarAccountModel extends BaseModel { }); } + @Transactional() + async invalidateAndPurge(id: string, lastError?: string | null) { + await this.updateStatus(id, 'invalid', lastError ?? null); + const subscriptions = + await this.models.calendarSubscription.listByAccount(id); + const subscriptionIds = subscriptions.map(subscription => subscription.id); + if (subscriptionIds.length > 0) { + await this.models.calendarEvent.deleteBySubscriptionIds(subscriptionIds); + } + await this.models.calendarSubscription.clearSyncTokensByAccount(id); + } + async delete(id: string) { return await this.db.calendarAccount.delete({ where: { id }, diff --git a/packages/backend/server/src/models/calendar-subscription.ts b/packages/backend/server/src/models/calendar-subscription.ts index 982fa02358..5a1f413678 100644 --- a/packages/backend/server/src/models/calendar-subscription.ts +++ b/packages/backend/server/src/models/calendar-subscription.ts @@ -1,4 +1,5 @@ import { Injectable } from '@nestjs/common'; +import { Transactional } from '@nestjs-cls/transactional'; import type { CalendarSubscription, Prisma } from '@prisma/client'; import { BaseModel } from './base'; @@ -191,4 +192,20 @@ export class CalendarSubscriptionModel extends BaseModel { data, }); } + + @Transactional() + async disableAndPurge(subscriptionId: string) { + await this.db.calendarSubscription.update({ + where: { id: subscriptionId }, + data: { + enabled: false, + syncToken: null, + customChannelId: null, + customResourceId: null, + channelExpiration: null, + }, + }); + + await this.models.calendarEvent.deleteBySubscriptionIds([subscriptionId]); + } } diff --git a/packages/backend/server/src/plugins/calendar/__tests__/caldav.spec.ts b/packages/backend/server/src/plugins/calendar/__tests__/caldav.spec.ts index 8ef84c1704..8e2e636143 100644 --- a/packages/backend/server/src/plugins/calendar/__tests__/caldav.spec.ts +++ b/packages/backend/server/src/plugins/calendar/__tests__/caldav.spec.ts @@ -9,7 +9,7 @@ import { CryptoHelper, GraphqlBadRequest, Mutex } from '../../../base'; import { ConfigModule } from '../../../base/config'; import { ServerConfigModule } from '../../../core/config'; import { Models } from '../../../models'; -import { CalendarModule } from '..'; +import { CalendarModule } from '../index'; import { CalDAVProvider, CalendarProviderFactory, diff --git a/packages/backend/server/src/plugins/calendar/__tests__/service.spec.ts b/packages/backend/server/src/plugins/calendar/__tests__/service.spec.ts index b08f266cce..f0a09993c2 100644 --- a/packages/backend/server/src/plugins/calendar/__tests__/service.spec.ts +++ b/packages/backend/server/src/plugins/calendar/__tests__/service.spec.ts @@ -5,7 +5,7 @@ import test from 'ava'; import { createModule } from '../../../__tests__/create-module'; import { Mockers } from '../../../__tests__/mocks'; -import { CryptoHelper } from '../../../base'; +import { CalendarProviderRequestError, CryptoHelper } from '../../../base'; import { ConfigModule } from '../../../base/config'; import { ServerConfigModule } from '../../../core/config'; import type { @@ -13,7 +13,7 @@ import type { UpsertCalendarSubscriptionInput, } from '../../../models'; import { Models } from '../../../models'; -import { CalendarModule } from '..'; +import { CalendarModule } from '../index'; import { CalendarProvider, CalendarProviderFactory, @@ -319,6 +319,227 @@ test('syncSubscription invalidates account on invalid grant', async t => { t.is(events.length, 0); }); +test('syncSubscription invalidates account when refresh token is invalid', async t => { + const user = await module.create(Mockers.User); + const account = await createAccount(user.id, { + accessToken: 'expired-access-token', + expiresAt: new Date(Date.now() - 5 * 60 * 1000), + }); + const subscription = await createSubscription(account.id, { + syncToken: 'sync-token', + }); + + await models.calendarEvent.upsert({ + subscriptionId: subscription.id, + externalEventId: randomUUID(), + recurrenceId: null, + etag: null, + status: 'confirmed', + title: 'existing', + description: null, + location: null, + startAtUtc: new Date('2024-01-02T00:00:00.000Z'), + endAtUtc: new Date('2024-01-02T01:00:00.000Z'), + originalTimezone: 'UTC', + allDay: false, + providerUpdatedAt: null, + raw: {}, + }); + + const provider = new MockCalendarProvider(); + const refreshMock = mock.method(provider, 'refreshTokens', async () => { + throw new Error('invalid_grant'); + }); + const listEventsMock = mock.method(provider, 'listEvents', async () => ({ + events: [], + })); + mock.method(providerFactory, 'get', () => provider); + + await calendarService.syncSubscription(subscription.id); + + t.is(refreshMock.mock.callCount(), 1); + t.is(listEventsMock.mock.callCount(), 0); + + const updatedAccount = await models.calendarAccount.get(account.id); + t.is(updatedAccount?.status, 'invalid'); + t.truthy(updatedAccount?.lastError); + + const updatedSubscription = await models.calendarSubscription.get( + subscription.id + ); + t.is(updatedSubscription?.syncToken, null); + + const events = await models.calendarEvent.listBySubscriptionsInRange( + [subscription.id], + new Date('2024-01-01T00:00:00.000Z'), + new Date('2024-01-03T00:00:00.000Z') + ); + t.is(events.length, 0); +}); + +test('syncSubscription disables subscription on provider 404', async t => { + const user = await module.create(Mockers.User); + const account = await createAccount(user.id); + const subscription = await createSubscription(account.id, { + syncToken: 'sync-token', + }); + + await models.calendarEvent.upsert({ + subscriptionId: subscription.id, + externalEventId: randomUUID(), + recurrenceId: null, + etag: null, + status: 'confirmed', + title: 'to remove', + description: null, + location: null, + startAtUtc: new Date('2026-01-02T00:00:00.000Z'), + endAtUtc: new Date('2026-01-02T01:00:00.000Z'), + originalTimezone: 'UTC', + allDay: false, + providerUpdatedAt: null, + raw: {}, + }); + + const provider = new MockCalendarProvider(); + const listEventsMock = mock.method(provider, 'listEvents', async () => { + throw new CalendarProviderRequestError({ + status: 404, + message: JSON.stringify({ + error: { + code: 404, + message: 'Not Found', + errors: [{ reason: 'notFound' }], + }, + }), + }); + }); + mock.method(providerFactory, 'get', () => provider); + + await calendarService.syncSubscription(subscription.id); + await calendarService.syncSubscription(subscription.id); + + t.is(listEventsMock.mock.callCount(), 1); + + const updatedSubscription = await models.calendarSubscription.get( + subscription.id + ); + t.truthy(updatedSubscription); + t.is(updatedSubscription?.enabled, false); + t.is(updatedSubscription?.syncToken, null); + + const events = await models.calendarEvent.listBySubscriptionsInRange( + [subscription.id], + new Date('2024-01-01T00:00:00.000Z'), + new Date('2026-12-31T00:00:00.000Z') + ); + t.is(events.length, 0); +}); + +test('syncSubscription rolls back disable when event cleanup fails', async t => { + const user = await module.create(Mockers.User); + const account = await createAccount(user.id); + const subscription = await createSubscription(account.id, { + syncToken: 'sync-token', + }); + + const provider = new MockCalendarProvider(); + mock.method(provider, 'listEvents', async () => { + throw new CalendarProviderRequestError({ + status: 404, + message: JSON.stringify({ + error: { + code: 404, + message: 'Not Found', + errors: [{ reason: 'notFound' }], + }, + }), + }); + }); + mock.method(providerFactory, 'get', () => provider); + mock.method(models.calendarEvent, 'deleteBySubscriptionIds', async () => { + throw new Error('delete events failed'); + }); + + await t.throwsAsync(calendarService.syncSubscription(subscription.id), { + message: 'delete events failed', + }); + + const updatedSubscription = await models.calendarSubscription.get( + subscription.id + ); + t.truthy(updatedSubscription); + t.is(updatedSubscription?.enabled, true); + t.is(updatedSubscription?.syncToken, 'sync-token'); +}); + +test('syncSubscription applies exponential backoff for repeated failures', async t => { + const user = await module.create(Mockers.User); + const account = await createAccount(user.id, { + refreshIntervalMinutes: 1, + }); + const subscription = await createSubscription(account.id, { + syncToken: 'sync-token', + }); + + const provider = new MockCalendarProvider(); + const listEventsMock = mock.method(provider, 'listEvents', async () => { + throw new Error('upstream timeout'); + }); + mock.method(providerFactory, 'get', () => provider); + + const baseDelayMs = 5 * 60 * 1000; + let now = new Date('2026-01-01T00:00:00.000Z').getTime(); + mock.method(Date, 'now', () => now); + + await calendarService.syncSubscription(subscription.id); + await calendarService.syncSubscription(subscription.id); + t.is(listEventsMock.mock.callCount(), 1); + + now += baseDelayMs + 1000; + await calendarService.syncSubscription(subscription.id); + t.is(listEventsMock.mock.callCount(), 2); + + now += baseDelayMs + 1000; + await calendarService.syncSubscription(subscription.id); + t.is(listEventsMock.mock.callCount(), 2); +}); + +test('syncSubscription skips token refresh while in backoff window', async t => { + let now = new Date('2026-01-01T00:00:00.000Z').getTime(); + mock.method(Date, 'now', () => now); + + const user = await module.create(Mockers.User); + const account = await createAccount(user.id, { + accessToken: 'expired-access-token', + expiresAt: new Date(now - 5 * 60 * 1000), + }); + const subscription = await createSubscription(account.id, { + syncToken: 'sync-token', + }); + + const provider = new MockCalendarProvider(); + const refreshMock = mock.method(provider, 'refreshTokens', async () => ({ + accessToken: `refreshed-${randomUUID()}`, + })); + const listEventsMock = mock.method(provider, 'listEvents', async () => { + throw new Error('upstream timeout'); + }); + mock.method(providerFactory, 'get', () => provider); + + const baseDelayMs = 5 * 60 * 1000; + + await calendarService.syncSubscription(subscription.id); + await calendarService.syncSubscription(subscription.id); + t.is(refreshMock.mock.callCount(), 1); + t.is(listEventsMock.mock.callCount(), 1); + + now += baseDelayMs + 1000; + await calendarService.syncSubscription(subscription.id); + t.is(refreshMock.mock.callCount(), 2); + t.is(listEventsMock.mock.callCount(), 2); +}); + test('syncSubscription renews webhook channel when expiring', async t => { const user = await module.create(Mockers.User); const account = await createAccount(user.id); diff --git a/packages/backend/server/src/plugins/calendar/service.ts b/packages/backend/server/src/plugins/calendar/service.ts index f73746114d..dce5877758 100644 --- a/packages/backend/server/src/plugins/calendar/service.ts +++ b/packages/backend/server/src/plugins/calendar/service.ts @@ -12,6 +12,7 @@ import { Mutex, URLHelper, } from '../../base'; +import { SessionRedis } from '../../base/redis'; import { Models } from '../../models'; import type { CalendarCalDAVProviderPreset } from './config'; import { @@ -27,6 +28,10 @@ import type { LinkCalDAVAccountInput } from './types'; const TOKEN_REFRESH_SKEW_MS = 60 * 1000; const DEFAULT_PAST_DAYS = 90; const DEFAULT_FUTURE_DAYS = 180; +const SYNC_FAILURE_BACKOFF_KEY_PREFIX = 'calendar:sync:backoff:'; +const SYNC_FAILURE_BACKOFF_BASE_MS = 5 * 60 * 1000; +const SYNC_FAILURE_BACKOFF_MAX_MS = 6 * 60 * 60 * 1000; +const SYNC_FAILURE_BACKOFF_TTL_SECONDS = 24 * 60 * 60; @Injectable() export class CalendarService { @@ -37,6 +42,7 @@ export class CalendarService { private readonly models: Models, private readonly providerFactory: CalendarProviderFactory, private readonly mutex: Mutex, + private readonly redis: SessionRedis, private readonly config: Config, private readonly url: URLHelper ) {} @@ -307,6 +313,12 @@ export class CalendarService { return; } + const now = Date.now(); + const backoff = await this.getSyncFailureBackoff(subscription.id); + if (backoff && now < backoff.nextRetryAt.getTime()) { + return; + } + await using lock = await this.mutex.acquire( `calendar:subscription:${subscriptionId}` ); @@ -314,6 +326,12 @@ export class CalendarService { return; } + const lockedNow = Date.now(); + const lockedBackoff = await this.getSyncFailureBackoff(subscription.id); + if (lockedBackoff && lockedNow < lockedBackoff.nextRetryAt.getTime()) { + return; + } + const provider = this.providerFactory.get( account.provider as CalendarProviderName ); @@ -321,8 +339,18 @@ export class CalendarService { return; } - const { accessToken } = await this.ensureAccessToken(account); - if (!accessToken) { + let accessToken: string | null = null; + try { + const tokens = await this.ensureAccessToken(account); + if (!tokens.accessToken) return; + accessToken = tokens.accessToken; + } catch (error) { + await this.handleSubscriptionSyncFailure({ + error, + subscription, + account, + provider, + }); return; } @@ -352,31 +380,42 @@ export class CalendarService { await this.models.calendarSubscription.updateSync(subscription.id, { syncToken: null, }); - await this.syncWithProvider({ - provider, - subscriptionId: subscription.id, - calendarId: subscription.externalCalendarId, - accessToken, - account, - timeMin, - timeMax, - subscriptionTimezone: subscription.timezone ?? undefined, - }); - synced = true; - } else { - if (this.isTokenInvalidError(error)) { - await this.invalidateAccount(account.id, (error as Error).message); - } else { - this.logger.warn( - `Calendar sync failed for subscription ${subscription.id}`, - error as Error - ); + try { + await this.syncWithProvider({ + provider, + subscriptionId: subscription.id, + calendarId: subscription.externalCalendarId, + accessToken, + account, + timeMin, + timeMax, + subscriptionTimezone: subscription.timezone ?? undefined, + }); + synced = true; + } catch (syncTokenRetryError) { + await this.handleSubscriptionSyncFailure({ + error: syncTokenRetryError, + subscription, + account, + provider, + accessToken, + }); + return; } + } else { + await this.handleSubscriptionSyncFailure({ + error, + subscription, + account, + provider, + accessToken, + }); return; } } if (synced) { + await this.clearSyncFailureBackoff(subscription.id); await this.ensureWebhookChannel(subscription, provider, accessToken); } @@ -764,19 +803,6 @@ export class CalendarService { return false; } - private async invalidateAccount(accountId: string, lastError?: string) { - await this.models.calendarAccount.updateStatus( - accountId, - 'invalid', - lastError ?? null - ); - const subscriptions = - await this.models.calendarSubscription.listByAccount(accountId); - const subscriptionIds = subscriptions.map(s => s.id); - await this.models.calendarEvent.deleteBySubscriptionIds(subscriptionIds); - await this.models.calendarSubscription.clearSyncTokensByAccount(accountId); - } - private requireProvider(name: CalendarProviderName) { const provider = this.providerFactory.get(name); if (!provider) { @@ -841,4 +867,180 @@ export class CalendarService { channelExpiration: result.expiration ?? null, }); } + + private async handleSubscriptionSyncFailure(params: { + error: unknown; + subscription: { + id: string; + externalCalendarId: string; + customChannelId: string | null; + customResourceId: string | null; + }; + account: CalendarAccount; + provider: CalendarProvider; + accessToken?: string; + }) { + if (this.isSubscriptionMissingError(params.error)) { + await this.disableSubscription({ + subscriptionId: params.subscription.id, + provider: params.provider, + accessToken: params.accessToken, + customChannelId: params.subscription.customChannelId, + customResourceId: params.subscription.customResourceId, + }); + this.logger.warn( + `Calendar subscription ${params.subscription.id} was disabled because provider returned 404 for calendar ${params.subscription.externalCalendarId}` + ); + return; + } + + if (this.isTokenInvalidError(params.error)) { + await this.clearSyncFailureBackoff(params.subscription.id); + await this.models.calendarAccount.invalidateAndPurge( + params.account.id, + this.formatSyncError(params.error) + ); + return; + } + + const backoff = await this.bumpSyncFailureBackoff(params.subscription.id); + const interval = params.account.refreshIntervalMinutes ?? 60; + const lastSyncAt = this.calculateLastSyncAtForRetry( + backoff.nextRetryAt, + interval + ); + await this.models.calendarSubscription.updateLastSyncAt( + params.subscription.id, + lastSyncAt + ); + this.logger.warn( + `Calendar sync failed for subscription ${params.subscription.id}, attempt ${backoff.attempt}, next retry at ${backoff.nextRetryAt.toISOString()}`, + this.toError(params.error) + ); + } + + private isSubscriptionMissingError(error: unknown) { + if (!(error instanceof CalendarProviderRequestError)) { + return false; + } + const status = error.data?.status ?? error.status; + return status === 404; + } + + private calculateLastSyncAtForRetry( + nextRetryAt: Date, + refreshIntervalMinutes: number + ) { + // Cron schedules by `now - lastSyncAt >= refreshInterval`, so back-calculate + // a synthetic lastSyncAt to defer the next attempt to `nextRetryAt`. + return new Date(nextRetryAt.getTime() - refreshIntervalMinutes * 60 * 1000); + } + + private async disableSubscription(params: { + subscriptionId: string; + provider: CalendarProvider; + accessToken?: string; + customChannelId: string | null; + customResourceId: string | null; + }) { + if ( + params.provider.stopChannel && + params.accessToken && + params.customChannelId && + params.customResourceId + ) { + try { + await params.provider.stopChannel({ + accessToken: params.accessToken, + channelId: params.customChannelId, + resourceId: params.customResourceId, + }); + } catch (error) { + this.logger.warn( + `Failed to stop webhook channel for disabled calendar subscription ${params.subscriptionId}`, + this.toError(error) + ); + } + } + + await this.models.calendarSubscription.disableAndPurge( + params.subscriptionId + ); + await this.clearSyncFailureBackoff(params.subscriptionId); + } + + private getSyncFailureBackoffKey(subscriptionId: string) { + return `${SYNC_FAILURE_BACKOFF_KEY_PREFIX}${subscriptionId}`; + } + + private async getSyncFailureBackoff(subscriptionId: string) { + const key = this.getSyncFailureBackoffKey(subscriptionId); + const value = await this.redis.get(key); + if (!value) { + return null; + } + + try { + const parsed = JSON.parse(value) as { + attempt?: number; + nextRetryAt?: string; + }; + if (!parsed.attempt || !parsed.nextRetryAt) { + return null; + } + const nextRetryAt = new Date(parsed.nextRetryAt); + if (Number.isNaN(nextRetryAt.getTime())) { + return null; + } + return { + attempt: parsed.attempt, + nextRetryAt, + }; + } catch { + return null; + } + } + + private async bumpSyncFailureBackoff(subscriptionId: string) { + const state = await this.getSyncFailureBackoff(subscriptionId); + const attempt = (state?.attempt ?? 0) + 1; + const delay = Math.min( + SYNC_FAILURE_BACKOFF_BASE_MS * 2 ** (attempt - 1), + SYNC_FAILURE_BACKOFF_MAX_MS + ); + const nextRetryAt = new Date(Date.now() + delay); + const key = this.getSyncFailureBackoffKey(subscriptionId); + await this.redis.set( + key, + JSON.stringify({ + attempt, + nextRetryAt: nextRetryAt.toISOString(), + }), + 'EX', + SYNC_FAILURE_BACKOFF_TTL_SECONDS + ); + return { + attempt, + nextRetryAt, + }; + } + + private async clearSyncFailureBackoff(subscriptionId: string) { + const key = this.getSyncFailureBackoffKey(subscriptionId); + await this.redis.del(key); + } + + private formatSyncError(error: unknown) { + if (error instanceof Error && error.message) { + return error.message; + } + return String(error); + } + + private toError(error: unknown) { + if (error instanceof Error) { + return error; + } + return new Error(this.formatSyncError(error)); + } } diff --git a/packages/backend/server/src/plugins/indexer/__tests__/event.spec.ts b/packages/backend/server/src/plugins/indexer/__tests__/event.spec.ts index 4ca0353232..c16f0f25d5 100644 --- a/packages/backend/server/src/plugins/indexer/__tests__/event.spec.ts +++ b/packages/backend/server/src/plugins/indexer/__tests__/event.spec.ts @@ -4,8 +4,8 @@ import Sinon from 'sinon'; import { createModule } from '../../../__tests__/create-module'; import { Config } from '../../../base'; import { ConfigModule } from '../../../base/config'; -import { IndexerModule } from '..'; import { IndexerEvent } from '../event'; +import { IndexerModule } from '../index'; const module = await createModule({ imports: [ diff --git a/packages/backend/server/src/plugins/indexer/__tests__/job.spec.ts b/packages/backend/server/src/plugins/indexer/__tests__/job.spec.ts index 8977879711..eacadd7805 100644 --- a/packages/backend/server/src/plugins/indexer/__tests__/job.spec.ts +++ b/packages/backend/server/src/plugins/indexer/__tests__/job.spec.ts @@ -10,8 +10,8 @@ import { JOB_SIGNAL } from '../../../base'; import { ConfigModule } from '../../../base/config'; import { ServerConfigModule } from '../../../core/config'; import { Models } from '../../../models'; -import { IndexerModule, IndexerService } from '..'; import { SearchProviderFactory } from '../factory'; +import { IndexerModule, IndexerService } from '../index'; import { IndexerJob } from '../job'; import { ManticoresearchProvider } from '../providers'; diff --git a/packages/backend/server/src/plugins/indexer/__tests__/providers/elasticsearch.spec.ts b/packages/backend/server/src/plugins/indexer/__tests__/providers/elasticsearch.spec.ts index 4124b70d15..9d63629e23 100644 --- a/packages/backend/server/src/plugins/indexer/__tests__/providers/elasticsearch.spec.ts +++ b/packages/backend/server/src/plugins/indexer/__tests__/providers/elasticsearch.spec.ts @@ -12,8 +12,8 @@ import { import { Mockers } from '../../../../__tests__/mocks'; import { ConfigModule } from '../../../../base/config'; import { User, Workspace } from '../../../../models'; -import { IndexerModule } from '../../'; import { SearchProviderType } from '../../config'; +import { IndexerModule } from '../../index'; import { AggregateQueryDSL, ElasticsearchProvider } from '../../providers'; import { blockMapping, docMapping, SearchTable } from '../../tables'; diff --git a/packages/backend/server/src/plugins/indexer/__tests__/providers/manticoresearch.spec.ts b/packages/backend/server/src/plugins/indexer/__tests__/providers/manticoresearch.spec.ts index ae16c76ac7..b68919554e 100644 --- a/packages/backend/server/src/plugins/indexer/__tests__/providers/manticoresearch.spec.ts +++ b/packages/backend/server/src/plugins/indexer/__tests__/providers/manticoresearch.spec.ts @@ -8,8 +8,8 @@ import { omit } from 'lodash-es'; import { createModule } from '../../../../__tests__/create-module'; import { Mockers } from '../../../../__tests__/mocks'; import { ConfigModule } from '../../../../base/config'; -import { IndexerModule } from '../../'; import { SearchProviderType } from '../../config'; +import { IndexerModule } from '../../index'; import { ManticoresearchProvider } from '../../providers'; import { blockSQL, docSQL, SearchTable } from '../../tables'; diff --git a/packages/backend/server/src/plugins/indexer/__tests__/service.spec.ts b/packages/backend/server/src/plugins/indexer/__tests__/service.spec.ts index 84782dca9d..cd8ff47d1f 100644 --- a/packages/backend/server/src/plugins/indexer/__tests__/service.spec.ts +++ b/packages/backend/server/src/plugins/indexer/__tests__/service.spec.ts @@ -8,8 +8,8 @@ import { createModule } from '../../../__tests__/create-module'; import { Mockers } from '../../../__tests__/mocks'; import { ConfigModule } from '../../../base/config'; import { ServerConfigModule } from '../../../core/config'; -import { IndexerModule, IndexerService } from '..'; import { SearchProviderFactory } from '../factory'; +import { IndexerModule, IndexerService } from '../index'; import { ManticoresearchProvider } from '../providers'; import { UpsertDoc } from '../service'; import { SearchTable } from '../tables'; diff --git a/packages/frontend/apps/android/package.json b/packages/frontend/apps/android/package.json index 43334d8962..c00807ba31 100644 --- a/packages/frontend/apps/android/package.json +++ b/packages/frontend/apps/android/package.json @@ -40,6 +40,6 @@ "@types/react": "^19.0.1", "@types/react-dom": "^19.0.2", "cross-env": "^10.1.0", - "typescript": "^5.7.2" + "typescript": "^5.9.3" } } diff --git a/packages/frontend/apps/electron-renderer/package.json b/packages/frontend/apps/electron-renderer/package.json index 37af8ac423..fbfefb7154 100644 --- a/packages/frontend/apps/electron-renderer/package.json +++ b/packages/frontend/apps/electron-renderer/package.json @@ -34,6 +34,6 @@ "@types/react": "^19.0.1", "@types/react-dom": "^19.0.2", "cross-env": "^10.1.0", - "typescript": "^5.7.2" + "typescript": "^5.9.3" } } diff --git a/packages/frontend/apps/electron/package.json b/packages/frontend/apps/electron/package.json index 7d29b9b69d..94961b2d52 100644 --- a/packages/frontend/apps/electron/package.json +++ b/packages/frontend/apps/electron/package.json @@ -72,7 +72,7 @@ "rxjs": "^7.8.2", "semver": "^7.7.3", "tree-kill": "^1.2.2", - "ts-node": "^10.9.2", + "typescript": "^5.9.3", "uuid": "^13.0.0", "vitest": "^3.2.4", "zod": "^3.25.76" @@ -94,8 +94,5 @@ ] } ] - }, - "peerDependencies": { - "ts-node": "*" } } diff --git a/packages/frontend/apps/ios/package.json b/packages/frontend/apps/ios/package.json index ad40031e26..31b1dc93a3 100644 --- a/packages/frontend/apps/ios/package.json +++ b/packages/frontend/apps/ios/package.json @@ -49,6 +49,6 @@ "@types/react": "^19.0.1", "@types/react-dom": "^19.0.2", "cross-env": "^10.1.0", - "typescript": "^5.7.2" + "typescript": "^5.9.3" } } diff --git a/packages/frontend/apps/mobile-shared/package.json b/packages/frontend/apps/mobile-shared/package.json index 9265d74569..75e5081b01 100644 --- a/packages/frontend/apps/mobile-shared/package.json +++ b/packages/frontend/apps/mobile-shared/package.json @@ -13,7 +13,7 @@ "@capacitor/core": "^7.0.0" }, "devDependencies": { - "typescript": "^5.7.2", + "typescript": "^5.9.3", "vitest": "^3.2.4" } } diff --git a/packages/frontend/apps/mobile/package.json b/packages/frontend/apps/mobile/package.json index 86e0b2cb74..1bc49f8615 100644 --- a/packages/frontend/apps/mobile/package.json +++ b/packages/frontend/apps/mobile/package.json @@ -27,6 +27,6 @@ "@types/react": "^19.0.1", "@types/react-dom": "^19.0.2", "cross-env": "^10.1.0", - "typescript": "^5.7.2" + "typescript": "^5.9.3" } } diff --git a/packages/frontend/apps/web/package.json b/packages/frontend/apps/web/package.json index f4b56aeb17..ebd677b86c 100644 --- a/packages/frontend/apps/web/package.json +++ b/packages/frontend/apps/web/package.json @@ -26,6 +26,6 @@ "@types/react": "^19.0.1", "@types/react-dom": "^19.0.2", "cross-env": "^10.1.0", - "typescript": "^5.7.2" + "typescript": "^5.9.3" } } diff --git a/packages/frontend/component/package.json b/packages/frontend/component/package.json index 00a10c4adb..5ac573b037 100644 --- a/packages/frontend/component/package.json +++ b/packages/frontend/component/package.json @@ -83,7 +83,7 @@ "@types/react-dom": "^19.0.2", "@vanilla-extract/css": "^1.17.0", "storybook": "^10.1.5", - "typescript": "^5.7.2", + "typescript": "^5.9.3", "unplugin-swc": "^1.5.9", "vite": "^7.2.7", "vitest": "^3.2.4" diff --git a/packages/frontend/media-capture-playground/package.json b/packages/frontend/media-capture-playground/package.json index 9dd9b5c5c0..032eb2f33d 100644 --- a/packages/frontend/media-capture-playground/package.json +++ b/packages/frontend/media-capture-playground/package.json @@ -5,7 +5,7 @@ "version": "0.26.3", "scripts": { "dev:web": "vite", - "dev:server": "tsx --env-file=.env --watch server/main.ts" + "dev:server": "node --env-file-if-exists=.env --watch server/main.ts" }, "dependencies": { "@affine/native": "workspace:*", @@ -30,7 +30,6 @@ "socket.io-client": "^4.8.3", "swr": "^2.3.7", "tailwindcss": "^4.1.17", - "tsx": "^4.19.2", "vite": "^7.2.7" }, "devDependencies": { diff --git a/packages/frontend/native/package.json b/packages/frontend/native/package.json index 3991d78879..4ef2ad62f7 100644 --- a/packages/frontend/native/package.json +++ b/packages/frontend/native/package.json @@ -30,8 +30,7 @@ "@types/node": "^22.0.0", "ava": "^6.4.1", "rxjs": "^7.8.2", - "ts-node": "^10.9.2", - "typescript": "^5.7.2" + "typescript": "^5.9.3" }, "engines": { "node": ">= 10" diff --git a/tools/cli/README.md b/tools/cli/README.md index 49c7e311c2..95d0798ade 100644 --- a/tools/cli/README.md +++ b/tools/cli/README.md @@ -40,9 +40,9 @@ yarn affine init ## Tricks -### Define scripts to run a .ts files without `--loader ts-node/esm/transpile-only` +### Define scripts to run a .ts files without manually wiring a TypeScript loader -`affine run` will automatically inject `ts-node`'s transpile service(swc used) for your scripts +`affine run` will automatically inject `tsx` for your scripts ```json { diff --git a/tools/cli/bin/runner.js b/tools/cli/bin/runner.js index 94f22947e3..12d10295b5 100755 --- a/tools/cli/bin/runner.js +++ b/tools/cli/bin/runner.js @@ -7,7 +7,9 @@ import { fileURLToPath, pathToFileURL } from 'node:url'; const scriptsFolder = join(fileURLToPath(import.meta.url), '..', '..'); const scriptsSrcFolder = join(scriptsFolder, 'src'); const projectRoot = join(scriptsFolder, '..', '..'); -const loader = join(scriptsFolder, 'register.js'); +const serverRoot = join(projectRoot, 'packages', 'backend', 'server'); +const tsRuntimeRegister = join(scriptsFolder, 'register.js'); +const tsxRuntimeRegister = join(scriptsFolder, 'tsx-register.js'); const [node, _self, file, ...options] = process.argv; @@ -60,7 +62,11 @@ if ( scriptLocation.endsWith('.ts') || scriptLocation.startsWith(scriptsFolder) ) { - nodeOptions.unshift(`--import=${pathToFileURL(loader)}`); + if (scriptLocation.startsWith(serverRoot)) { + nodeOptions.unshift(`--import=${pathToFileURL(tsRuntimeRegister)}`); + } else { + nodeOptions.unshift(`--import=${pathToFileURL(tsxRuntimeRegister)}`); + } } else { nodeOptions.unshift('--experimental-specifier-resolution=node'); } diff --git a/tools/cli/hooks.js b/tools/cli/hooks.js index afd704db6d..e94534389d 100644 --- a/tools/cli/hooks.js +++ b/tools/cli/hooks.js @@ -1,13 +1,165 @@ -import { create, createEsmHooks, register } from 'ts-node'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; -const service = create({ - experimentalSpecifierResolution: 'node', - esm: true, - transpileOnly: true, -}); +import { transform } from '@swc/core'; -register(service); -const hooks = createEsmHooks(service); +const TS_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts']); +const JS_EXTENSIONS = ['.js', '.mjs', '.cjs']; +const ALL_EXTENSIONS = [...TS_EXTENSIONS, ...JS_EXTENSIONS]; -export const resolve = hooks.resolve; -export const load = hooks.load; +const JS_EXTENSION_TO_TS = { + '.js': ['.ts', '.tsx', '.js'], + '.mjs': ['.mts', '.mjs'], + '.cjs': ['.cts', '.cjs'], +}; + +const transformCache = new Map(); + +function createCandidates(basePath) { + const parsedExt = path.extname(basePath); + const hasKnownExtension = + parsedExt in JS_EXTENSION_TO_TS || ALL_EXTENSIONS.includes(parsedExt); + const ext = hasKnownExtension ? parsedExt : ''; + const stem = ext ? basePath.slice(0, -ext.length) : basePath; + const candidates = new Set(); + + const extensions = ext ? (JS_EXTENSION_TO_TS[ext] ?? [ext]) : ALL_EXTENSIONS; + + for (const candidateExt of extensions) { + candidates.add(`${stem}${candidateExt}`); + } + + if (!ext) { + for (const candidateExt of ALL_EXTENSIONS) { + candidates.add(path.join(basePath, `index${candidateExt}`)); + } + } + + return candidates; +} + +function isPathLike(specifier) { + return ( + specifier.startsWith('./') || + specifier.startsWith('../') || + specifier.startsWith('/') || + specifier.startsWith('file:') + ); +} + +function resolvePathLikeSpecifier(specifier, parentURL) { + if (!isPathLike(specifier)) { + return undefined; + } + + const [specifierWithoutQuery, queryString = ''] = specifier.split('?'); + const querySuffix = queryString ? `?${queryString}` : ''; + + const parentPath = parentURL?.startsWith('file:') + ? fileURLToPath(parentURL) + : path.join(process.cwd(), 'index.js'); + + const basePath = specifierWithoutQuery.startsWith('file:') + ? fileURLToPath(specifierWithoutQuery) + : path.isAbsolute(specifierWithoutQuery) + ? specifierWithoutQuery + : path.resolve(path.dirname(parentPath), specifierWithoutQuery); + + for (const candidate of createCandidates(basePath)) { + try { + if (fs.statSync(candidate).isFile()) { + return `${pathToFileURL(candidate).href}${querySuffix}`; + } + } catch { + // ignore missing candidates + } + } + + return undefined; +} + +export async function resolve(specifier, context, nextResolve) { + try { + return await nextResolve(specifier, context); + } catch (error) { + const resolvedUrl = resolvePathLikeSpecifier(specifier, context.parentURL); + if (resolvedUrl) { + return { + url: resolvedUrl, + shortCircuit: true, + }; + } + + throw error; + } +} + +export async function load(url, context, nextLoad) { + const [urlWithoutQuery] = url.split('?'); + + if (!urlWithoutQuery.startsWith('file:')) { + return nextLoad(url, context); + } + + const filePath = fileURLToPath(urlWithoutQuery); + if (!TS_EXTENSIONS.has(path.extname(filePath))) { + return nextLoad(url, context); + } + + const stat = await fs.promises.stat(filePath); + const cached = transformCache.get(filePath); + if (cached?.mtimeMs === stat.mtimeMs) { + return { + format: cached.format, + source: cached.source, + shortCircuit: true, + }; + } + + const sourceText = await fs.promises.readFile(filePath, 'utf8'); + const isCommonJs = filePath.endsWith('.cts'); + const moduleType = isCommonJs ? 'commonjs' : 'es6'; + const tsx = filePath.endsWith('.tsx'); + + let output; + try { + output = await transform(sourceText, { + filename: filePath, + sourceMaps: 'inline', + module: { type: moduleType }, + jsc: { + target: 'es2022', + keepClassNames: true, + experimental: { keepImportAttributes: true }, + parser: { + syntax: 'typescript', + tsx, + decorators: true, + dynamicImport: true, + }, + transform: { + legacyDecorator: true, + decoratorMetadata: true, + useDefineForClassFields: false, + react: tsx + ? { runtime: 'automatic', importSource: 'react' } + : undefined, + }, + }, + }); + } catch (error) { + const detail = error instanceof Error ? error.message : String(error); + throw new Error(`[swc-loader] Failed to compile ${filePath}\n${detail}`); + } + + const source = output.code ?? ''; + const format = isCommonJs ? 'commonjs' : 'module'; + transformCache.set(filePath, { mtimeMs: stat.mtimeMs, source, format }); + + return { + format, + source, + shortCircuit: true, + }; +} diff --git a/tools/cli/package.json b/tools/cli/package.json index 021f523e11..a60997b383 100644 --- a/tools/cli/package.json +++ b/tools/cli/package.json @@ -48,9 +48,9 @@ "swc-loader": "^0.2.6", "tailwindcss": "^4.1.17", "terser-webpack-plugin": "^5.3.10", - "ts-node": "^10.9.2", + "tsx": "^4.21.0", "typanion": "^3.14.0", - "typescript": "^5.5.4", + "typescript": "^5.9.3", "webpack": "^5.102.1", "webpack-dev-server": "^5.2.0", "webpack-merge": "^6.0.1" diff --git a/tools/cli/src/run.ts b/tools/cli/src/run.ts index cab5bd1b84..731b97cc74 100644 --- a/tools/cli/src/run.ts +++ b/tools/cli/src/run.ts @@ -11,11 +11,19 @@ interface RunScriptOptions { } const currentDir = Path.dir(import.meta.url); +const serverRuntimeLoader = currentDir + .join('../register.js') + .toFileUrl() + .toString(); +const tsxRuntimeLoader = currentDir + .join('../tsx-register.js') + .toFileUrl() + .toString(); const ignoreLoaderScripts = [ 'vitest', 'vite', - 'ts-node', + 'tsx', 'prisma', 'cap', 'tsc', @@ -161,13 +169,17 @@ export class RunCommand extends PackageCommand { args = extractedArgs; const bin = args[0] === 'yarn' ? args[1] : args[0]; - - const loader = currentDir.join('../register.js').toFileUrl().toString(); + const loader = + pkg.name === '@affine/server' ? serverRuntimeLoader : tsxRuntimeLoader; + const hasKnownLoader = + process.env.NODE_OPTIONS?.includes('tsx') || + process.env.NODE_OPTIONS?.includes(tsxRuntimeLoader) || + process.env.NODE_OPTIONS?.includes(serverRuntimeLoader); // very simple test for auto ts/mjs scripts const isLoaderRequired = !ignoreLoaderScripts.some(ignore => new RegExp(ignore).test(bin)) || - process.env.NODE_OPTIONS?.includes('ts-node/esm') || + hasKnownLoader || process.env.NODE_OPTIONS?.includes(loader); let NODE_OPTIONS = process.env.NODE_OPTIONS diff --git a/tools/cli/tsx-register.js b/tools/cli/tsx-register.js new file mode 100644 index 0000000000..eed089470e --- /dev/null +++ b/tools/cli/tsx-register.js @@ -0,0 +1 @@ +import 'tsx'; diff --git a/tools/doc-diff/package.json b/tools/doc-diff/package.json index 8f59772bf7..59a2d498c9 100644 --- a/tools/doc-diff/package.json +++ b/tools/doc-diff/package.json @@ -13,10 +13,10 @@ }, "dependencies": { "@affine-tools/cli": "workspace:*", - "typescript": "^5.7.2", "yjs": "^13.6.27" }, "devDependencies": { - "@types/node": "^22.0.0" + "@types/node": "^22.0.0", + "typescript": "^5.9.3" } } diff --git a/tools/playstore-auto-bump/package.json b/tools/playstore-auto-bump/package.json index d49c93d450..3639b680a2 100644 --- a/tools/playstore-auto-bump/package.json +++ b/tools/playstore-auto-bump/package.json @@ -12,7 +12,7 @@ "@affine-tools/cli": "workspace:*", "@affine-tools/utils": "workspace:*", "@googleapis/androidpublisher": "^35.0.0", - "typescript": "^5.7.2" + "typescript": "^5.9.3" }, "devDependencies": { "@types/node": "^22.0.0" diff --git a/tools/revert-update/package.json b/tools/revert-update/package.json index 11ff2ec63f..a49b78620e 100644 --- a/tools/revert-update/package.json +++ b/tools/revert-update/package.json @@ -10,10 +10,10 @@ }, "dependencies": { "@affine-tools/cli": "workspace:*", - "typescript": "^5.7.2", "yjs": "^13.6.27" }, "devDependencies": { - "@types/node": "^22.0.0" + "@types/node": "^22.0.0", + "typescript": "^5.9.3" } } diff --git a/tools/utils/package.json b/tools/utils/package.json index 27ddf660c0..9e96396ac6 100644 --- a/tools/utils/package.json +++ b/tools/utils/package.json @@ -18,6 +18,6 @@ "chalk": "^5.3.0", "lodash-es": "^4.17.23", "prettier": "^3.7.4", - "typescript": "^5.5.4" + "typescript": "^5.9.3" } } diff --git a/yarn.lock b/yarn.lock index 456874605f..c918f866fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -153,9 +153,9 @@ __metadata: swc-loader: "npm:^0.2.6" tailwindcss: "npm:^4.1.17" terser-webpack-plugin: "npm:^5.3.10" - ts-node: "npm:^10.9.2" + tsx: "npm:^4.21.0" typanion: "npm:^3.14.0" - typescript: "npm:^5.5.4" + typescript: "npm:^5.9.3" webpack: "npm:^5.102.1" webpack-dev-server: "npm:^5.2.0" webpack-merge: "npm:^6.0.1" @@ -174,7 +174,7 @@ __metadata: chalk: "npm:^5.3.0" lodash-es: "npm:^4.17.23" prettier: "npm:^3.7.4" - typescript: "npm:^5.5.4" + typescript: "npm:^5.9.3" languageName: unknown linkType: soft @@ -281,7 +281,7 @@ __metadata: react: "npm:^19.2.1" react-dom: "npm:^19.2.1" react-router-dom: "npm:^6.30.3" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" languageName: unknown linkType: soft @@ -372,7 +372,7 @@ __metadata: sonner: "npm:^2.0.7" storybook: "npm:^10.1.5" swr: "npm:^2.3.7" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" unplugin-swc: "npm:^1.5.9" vite: "npm:^7.2.7" vitest: "npm:^3.2.4" @@ -521,7 +521,7 @@ __metadata: dependencies: "@affine-tools/cli": "workspace:*" "@types/node": "npm:^22.0.0" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" yjs: "npm:^13.6.27" languageName: unknown linkType: soft @@ -571,7 +571,7 @@ __metadata: react: "npm:^19.2.1" react-dom: "npm:^19.2.1" react-router-dom: "npm:^6.30.3" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" uuid: "npm:^13.0.0" webm-muxer: "npm:^5.0.3" languageName: unknown @@ -626,13 +626,11 @@ __metadata: semver: "npm:^7.7.3" set-cookie-parser: "npm:^2.7.1" tree-kill: "npm:^1.2.2" - ts-node: "npm:^10.9.2" + typescript: "npm:^5.9.3" uuid: "npm:^13.0.0" vitest: "npm:^3.2.4" yjs: "npm:^13.6.27" zod: "npm:^3.25.76" - peerDependencies: - ts-node: "*" languageName: unknown linkType: soft @@ -732,7 +730,7 @@ __metadata: react: "npm:^19.2.1" react-dom: "npm:^19.2.1" react-router-dom: "npm:^6.30.3" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" yjs: "npm:^13.6.27" languageName: unknown linkType: soft @@ -764,7 +762,6 @@ __metadata: socket.io-client: "npm:^4.8.3" swr: "npm:^2.3.7" tailwindcss: "npm:^4.1.17" - tsx: "npm:^4.19.2" vite: "npm:^7.2.7" languageName: unknown linkType: soft @@ -775,7 +772,7 @@ __metadata: dependencies: "@affine/core": "workspace:*" "@capacitor/core": "npm:^7.0.0" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" vitest: "npm:^3.2.4" languageName: unknown linkType: soft @@ -800,7 +797,7 @@ __metadata: react: "npm:^19.2.1" react-dom: "npm:^19.2.1" react-router-dom: "npm:^6.30.3" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" languageName: unknown linkType: soft @@ -844,7 +841,7 @@ __metadata: oxlint: "npm:^1.47.0" prettier: "npm:^3.7.4" semver: "npm:^7.7.3" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" typescript-eslint: "npm:^8.55.0" unplugin-swc: "npm:^1.5.9" vite: "npm:^7.2.7" @@ -861,8 +858,7 @@ __metadata: "@types/node": "npm:^22.0.0" ava: "npm:^6.4.1" rxjs: "npm:^7.8.2" - ts-node: "npm:^10.9.2" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" languageName: unknown linkType: soft @@ -905,7 +901,7 @@ __metadata: "@affine-tools/utils": "workspace:*" "@googleapis/androidpublisher": "npm:^35.0.0" "@types/node": "npm:^22.0.0" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" languageName: unknown linkType: soft @@ -928,7 +924,7 @@ __metadata: dependencies: "@affine-tools/cli": "workspace:*" "@types/node": "npm:^22.0.0" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" yjs: "npm:^13.6.27" languageName: unknown linkType: soft @@ -1035,7 +1031,6 @@ __metadata: "@types/http-errors": "npm:^2.0.4" "@types/jsonwebtoken": "npm:^9.0.9" "@types/lodash-es": "npm:^4.17.12" - "@types/mixpanel": "npm:^2.14.9" "@types/mustache": "npm:^4.2.5" "@types/node": "npm:^22.0.0" "@types/nodemailer": "npm:^7.0.0" @@ -1069,9 +1064,7 @@ __metadata: is-mobile: "npm:^5.0.0" jose: "npm:^6.1.3" jsonwebtoken: "npm:^9.0.3" - keyv: "npm:^5.2.2" lodash-es: "npm:^4.17.23" - mixpanel: "npm:^0.18.0" mustache: "npm:^4.2.0" nanoid: "npm:^5.1.6" nest-commander: "npm:^3.15.0" @@ -1095,8 +1088,7 @@ __metadata: stripe: "npm:^17.7.0" supertest: "npm:^7.1.4" tldts: "npm:^7.0.19" - ts-node: "npm:^10.9.2" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" why-is-node-running: "npm:^3.2.2" winston: "npm:^3.17.0" yjs: "npm:^13.6.27" @@ -1146,7 +1138,7 @@ __metadata: react: "npm:^19.2.1" react-dom: "npm:^19.2.1" react-router-dom: "npm:^6.30.3" - typescript: "npm:^5.7.2" + typescript: "npm:^5.9.3" languageName: unknown linkType: soft @@ -4324,15 +4316,6 @@ __metadata: languageName: node linkType: hard -"@cspotcode/source-map-support@npm:^0.8.0": - version: 0.8.1 - resolution: "@cspotcode/source-map-support@npm:0.8.1" - dependencies: - "@jridgewell/trace-mapping": "npm:0.3.9" - checksum: 10/b6e38a1712fab242c86a241c229cf562195aad985d0564bd352ac404be583029e89e93028ffd2c251d2c407ecac5fb0cbdca94a2d5c10f29ac806ede0508b3ff - languageName: node - linkType: hard - "@ctrl/tinycolor@npm:^4.1.0": version: 4.1.0 resolution: "@ctrl/tinycolor@npm:4.1.0" @@ -5209,9 +5192,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/aix-ppc64@npm:0.27.2" +"@esbuild/aix-ppc64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/aix-ppc64@npm:0.27.3" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard @@ -5223,9 +5206,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/android-arm64@npm:0.27.2" +"@esbuild/android-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/android-arm64@npm:0.27.3" conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -5237,9 +5220,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/android-arm@npm:0.27.2" +"@esbuild/android-arm@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/android-arm@npm:0.27.3" conditions: os=android & cpu=arm languageName: node linkType: hard @@ -5251,9 +5234,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/android-x64@npm:0.27.2" +"@esbuild/android-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/android-x64@npm:0.27.3" conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -5265,9 +5248,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/darwin-arm64@npm:0.27.2" +"@esbuild/darwin-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/darwin-arm64@npm:0.27.3" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -5279,9 +5262,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/darwin-x64@npm:0.27.2" +"@esbuild/darwin-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/darwin-x64@npm:0.27.3" conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -5293,9 +5276,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/freebsd-arm64@npm:0.27.2" +"@esbuild/freebsd-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/freebsd-arm64@npm:0.27.3" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -5307,9 +5290,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/freebsd-x64@npm:0.27.2" +"@esbuild/freebsd-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/freebsd-x64@npm:0.27.3" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -5321,9 +5304,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-arm64@npm:0.27.2" +"@esbuild/linux-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-arm64@npm:0.27.3" conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -5335,9 +5318,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-arm@npm:0.27.2" +"@esbuild/linux-arm@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-arm@npm:0.27.3" conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -5349,9 +5332,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-ia32@npm:0.27.2" +"@esbuild/linux-ia32@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-ia32@npm:0.27.3" conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -5363,9 +5346,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-loong64@npm:0.27.2" +"@esbuild/linux-loong64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-loong64@npm:0.27.3" conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -5377,9 +5360,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-mips64el@npm:0.27.2" +"@esbuild/linux-mips64el@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-mips64el@npm:0.27.3" conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -5391,9 +5374,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-ppc64@npm:0.27.2" +"@esbuild/linux-ppc64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-ppc64@npm:0.27.3" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -5405,9 +5388,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-riscv64@npm:0.27.2" +"@esbuild/linux-riscv64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-riscv64@npm:0.27.3" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -5419,9 +5402,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-s390x@npm:0.27.2" +"@esbuild/linux-s390x@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-s390x@npm:0.27.3" conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -5433,9 +5416,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-x64@npm:0.27.2" +"@esbuild/linux-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/linux-x64@npm:0.27.3" conditions: os=linux & cpu=x64 languageName: node linkType: hard @@ -5447,9 +5430,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/netbsd-arm64@npm:0.27.2" +"@esbuild/netbsd-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/netbsd-arm64@npm:0.27.3" conditions: os=netbsd & cpu=arm64 languageName: node linkType: hard @@ -5461,9 +5444,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/netbsd-x64@npm:0.27.2" +"@esbuild/netbsd-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/netbsd-x64@npm:0.27.3" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard @@ -5475,9 +5458,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/openbsd-arm64@npm:0.27.2" +"@esbuild/openbsd-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/openbsd-arm64@npm:0.27.3" conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard @@ -5489,9 +5472,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/openbsd-x64@npm:0.27.2" +"@esbuild/openbsd-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/openbsd-x64@npm:0.27.3" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -5503,9 +5486,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/openharmony-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/openharmony-arm64@npm:0.27.2" +"@esbuild/openharmony-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/openharmony-arm64@npm:0.27.3" conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard @@ -5517,9 +5500,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/sunos-x64@npm:0.27.2" +"@esbuild/sunos-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/sunos-x64@npm:0.27.3" conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -5531,9 +5514,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/win32-arm64@npm:0.27.2" +"@esbuild/win32-arm64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/win32-arm64@npm:0.27.3" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -5545,9 +5528,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/win32-ia32@npm:0.27.2" +"@esbuild/win32-ia32@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/win32-ia32@npm:0.27.3" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -5559,9 +5542,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/win32-x64@npm:0.27.2" +"@esbuild/win32-x64@npm:0.27.3": + version: 0.27.3 + resolution: "@esbuild/win32-x64@npm:0.27.3" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -8181,7 +8164,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": +"@jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.2 resolution: "@jridgewell/resolve-uri@npm:3.1.2" checksum: 10/97106439d750a409c22c8bff822d648f6a71f3aa9bc8e5129efdc36343cd3096ddc4eeb1c62d2fe48e9bdd4db37b05d4646a17114ecebd3bbcacfa2de51c3c1d @@ -8198,23 +8181,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15, @jridgewell/sourcemap-codec@npm:^1.5.0, @jridgewell/sourcemap-codec@npm:^1.5.5": +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15, @jridgewell/sourcemap-codec@npm:^1.5.0, @jridgewell/sourcemap-codec@npm:^1.5.5": version: 1.5.5 resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" checksum: 10/5d9d207b462c11e322d71911e55e21a4e2772f71ffe8d6f1221b8eb5ae6774458c1d242f897fb0814e8714ca9a6b498abfa74dfe4f434493342902b1a48b33a5 languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:0.3.9": - version: 0.3.9 - resolution: "@jridgewell/trace-mapping@npm:0.3.9" - dependencies: - "@jridgewell/resolve-uri": "npm:^3.0.3" - "@jridgewell/sourcemap-codec": "npm:^1.4.10" - checksum: 10/83deafb8e7a5ca98993c2c6eeaa93c270f6f647a4c0dc00deb38c9cf9b2d3b7bf15e8839540155247ef034a052c0ec4466f980bf0c9e2ab63b97d16c0cedd3ff - languageName: node - linkType: hard - "@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28": version: 0.3.31 resolution: "@jridgewell/trace-mapping@npm:0.3.31" @@ -8271,15 +8244,6 @@ __metadata: languageName: node linkType: hard -"@keyv/serialize@npm:^1.0.3": - version: 1.0.3 - resolution: "@keyv/serialize@npm:1.0.3" - dependencies: - buffer: "npm:^6.0.3" - checksum: 10/d6a9194dd781bc26cc4d55f392d843810c1fdc0da81e69203e633cb289fc0a8edc8bc6466f66c4cbb55da0a5b405e89f14a68b48d6e73919ae82f8249fb5e444 - languageName: node - linkType: hard - "@kwsites/file-exists@npm:^1.1.1": version: 1.1.1 resolution: "@kwsites/file-exists@npm:1.1.1" @@ -17085,34 +17049,6 @@ __metadata: languageName: node linkType: hard -"@tsconfig/node10@npm:^1.0.7": - version: 1.0.11 - resolution: "@tsconfig/node10@npm:1.0.11" - checksum: 10/51fe47d55fe1b80ec35e6e5ed30a13665fd3a531945350aa74a14a1e82875fb60b350c2f2a5e72a64831b1b6bc02acb6760c30b3738b54954ec2dea82db7a267 - languageName: node - linkType: hard - -"@tsconfig/node12@npm:^1.0.7": - version: 1.0.11 - resolution: "@tsconfig/node12@npm:1.0.11" - checksum: 10/5ce29a41b13e7897a58b8e2df11269c5395999e588b9a467386f99d1d26f6c77d1af2719e407621412520ea30517d718d5192a32403b8dfcc163bf33e40a338a - languageName: node - linkType: hard - -"@tsconfig/node14@npm:^1.0.0": - version: 1.0.3 - resolution: "@tsconfig/node14@npm:1.0.3" - checksum: 10/19275fe80c4c8d0ad0abed6a96dbf00642e88b220b090418609c4376e1cef81bf16237bf170ad1b341452feddb8115d8dd2e5acdfdea1b27422071163dc9ba9d - languageName: node - linkType: hard - -"@tsconfig/node16@npm:^1.0.2": - version: 1.0.4 - resolution: "@tsconfig/node16@npm:1.0.4" - checksum: 10/202319785901f942a6e1e476b872d421baec20cf09f4b266a1854060efbf78cde16a4d256e8bc949d31e6cd9a90f1e8ef8fb06af96a65e98338a2b6b0de0a0ff - languageName: node - linkType: hard - "@tweakpane/core@npm:^2.0.4": version: 2.0.5 resolution: "@tweakpane/core@npm:2.0.5" @@ -18063,13 +17999,6 @@ __metadata: languageName: node linkType: hard -"@types/mixpanel@npm:^2.14.9": - version: 2.14.9 - resolution: "@types/mixpanel@npm:2.14.9" - checksum: 10/647e90c1141b6a2b65c17e143eda5df937e6810b6834a17084365efc2150e42e5234f584afef581e17763a91bea1cc0d54ce3cd1ee40b0f6726101375e2eab51 - languageName: node - linkType: hard - "@types/ms@npm:*": version: 2.1.0 resolution: "@types/ms@npm:2.1.0" @@ -19444,7 +19373,7 @@ __metadata: languageName: node linkType: hard -"acorn-walk@npm:^8.1.1, acorn-walk@npm:^8.2.0, acorn-walk@npm:^8.3.4": +"acorn-walk@npm:^8.2.0, acorn-walk@npm:^8.3.4": version: 8.3.4 resolution: "acorn-walk@npm:8.3.4" dependencies: @@ -19453,7 +19382,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.11.0, acorn@npm:^8.14.0, acorn@npm:^8.15.0, acorn@npm:^8.4.1, acorn@npm:^8.6.0, acorn@npm:^8.7.1, acorn@npm:^8.8.1, acorn@npm:^8.8.2": +"acorn@npm:^8.11.0, acorn@npm:^8.14.0, acorn@npm:^8.15.0, acorn@npm:^8.6.0, acorn@npm:^8.7.1, acorn@npm:^8.8.1, acorn@npm:^8.8.2": version: 8.15.0 resolution: "acorn@npm:8.15.0" bin: @@ -19804,13 +19733,6 @@ __metadata: languageName: node linkType: hard -"arg@npm:^4.1.0": - version: 4.1.3 - resolution: "arg@npm:4.1.3" - checksum: 10/969b491082f20cad166649fa4d2073ea9e974a4e5ac36247ca23d2e5a8b3cb12d60e9ff70a8acfe26d76566c71fd351ee5e6a9a6595157eb36f92b1fd64e1599 - languageName: node - linkType: hard - "argparse@npm:^1.0.7, argparse@npm:~1.0.3": version: 1.0.10 resolution: "argparse@npm:1.0.10" @@ -20521,16 +20443,6 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^6.0.3": - version: 6.0.3 - resolution: "buffer@npm:6.0.3" - dependencies: - base64-js: "npm:^1.3.1" - ieee754: "npm:^1.2.1" - checksum: 10/b6bc68237ebf29bdacae48ce60e5e28fc53ae886301f2ad9496618efac49427ed79096750033e7eab1897a4f26ae374ace49106a5758f38fb70c78c9fda2c3b1 - languageName: node - linkType: hard - "builder-util-runtime@npm:9.5.1, builder-util-runtime@npm:^9.5.1": version: 9.5.1 resolution: "builder-util-runtime@npm:9.5.1" @@ -22137,13 +22049,6 @@ __metadata: languageName: node linkType: hard -"create-require@npm:^1.1.0": - version: 1.1.1 - resolution: "create-require@npm:1.1.1" - checksum: 10/a9a1503d4390d8b59ad86f4607de7870b39cad43d929813599a23714831e81c520bddf61bcdd1f8e30f05fd3a2b71ae8538e946eb2786dc65c2bbc520f692eff - languageName: node - linkType: hard - "cron-parser@npm:^4.9.0": version: 4.9.0 resolution: "cron-parser@npm:4.9.0" @@ -23263,13 +23168,6 @@ __metadata: languageName: node linkType: hard -"diff@npm:^4.0.1": - version: 4.0.4 - resolution: "diff@npm:4.0.4" - checksum: 10/5019b3f5ae124ea9e95137119e1a83a59c252c75ddac873cc967832fd7a834570a58a4d58b941bdbd07832ebf98dcb232b27c561b7f5584357da6dae59bcac62 - languageName: node - linkType: hard - "diff@npm:^8.0.2": version: 8.0.3 resolution: "diff@npm:8.0.3" @@ -24018,7 +23916,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:0.25.10, esbuild@npm:^0.25.0, esbuild@npm:~0.25.0": +"esbuild@npm:0.25.10, esbuild@npm:^0.25.0": version: 0.25.10 resolution: "esbuild@npm:0.25.10" dependencies: @@ -24107,36 +24005,36 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0, esbuild@npm:^0.27.0, esbuild@npm:esbuild@>=0.17.6 <0.28.0": - version: 0.27.2 - resolution: "esbuild@npm:0.27.2" +"esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0, esbuild@npm:^0.27.0, esbuild@npm:esbuild@>=0.17.6 <0.28.0, esbuild@npm:~0.27.0": + version: 0.27.3 + resolution: "esbuild@npm:0.27.3" dependencies: - "@esbuild/aix-ppc64": "npm:0.27.2" - "@esbuild/android-arm": "npm:0.27.2" - "@esbuild/android-arm64": "npm:0.27.2" - "@esbuild/android-x64": "npm:0.27.2" - "@esbuild/darwin-arm64": "npm:0.27.2" - "@esbuild/darwin-x64": "npm:0.27.2" - "@esbuild/freebsd-arm64": "npm:0.27.2" - "@esbuild/freebsd-x64": "npm:0.27.2" - "@esbuild/linux-arm": "npm:0.27.2" - "@esbuild/linux-arm64": "npm:0.27.2" - "@esbuild/linux-ia32": "npm:0.27.2" - "@esbuild/linux-loong64": "npm:0.27.2" - "@esbuild/linux-mips64el": "npm:0.27.2" - "@esbuild/linux-ppc64": "npm:0.27.2" - "@esbuild/linux-riscv64": "npm:0.27.2" - "@esbuild/linux-s390x": "npm:0.27.2" - "@esbuild/linux-x64": "npm:0.27.2" - "@esbuild/netbsd-arm64": "npm:0.27.2" - "@esbuild/netbsd-x64": "npm:0.27.2" - "@esbuild/openbsd-arm64": "npm:0.27.2" - "@esbuild/openbsd-x64": "npm:0.27.2" - "@esbuild/openharmony-arm64": "npm:0.27.2" - "@esbuild/sunos-x64": "npm:0.27.2" - "@esbuild/win32-arm64": "npm:0.27.2" - "@esbuild/win32-ia32": "npm:0.27.2" - "@esbuild/win32-x64": "npm:0.27.2" + "@esbuild/aix-ppc64": "npm:0.27.3" + "@esbuild/android-arm": "npm:0.27.3" + "@esbuild/android-arm64": "npm:0.27.3" + "@esbuild/android-x64": "npm:0.27.3" + "@esbuild/darwin-arm64": "npm:0.27.3" + "@esbuild/darwin-x64": "npm:0.27.3" + "@esbuild/freebsd-arm64": "npm:0.27.3" + "@esbuild/freebsd-x64": "npm:0.27.3" + "@esbuild/linux-arm": "npm:0.27.3" + "@esbuild/linux-arm64": "npm:0.27.3" + "@esbuild/linux-ia32": "npm:0.27.3" + "@esbuild/linux-loong64": "npm:0.27.3" + "@esbuild/linux-mips64el": "npm:0.27.3" + "@esbuild/linux-ppc64": "npm:0.27.3" + "@esbuild/linux-riscv64": "npm:0.27.3" + "@esbuild/linux-s390x": "npm:0.27.3" + "@esbuild/linux-x64": "npm:0.27.3" + "@esbuild/netbsd-arm64": "npm:0.27.3" + "@esbuild/netbsd-x64": "npm:0.27.3" + "@esbuild/openbsd-arm64": "npm:0.27.3" + "@esbuild/openbsd-x64": "npm:0.27.3" + "@esbuild/openharmony-arm64": "npm:0.27.3" + "@esbuild/sunos-x64": "npm:0.27.3" + "@esbuild/win32-arm64": "npm:0.27.3" + "@esbuild/win32-ia32": "npm:0.27.3" + "@esbuild/win32-x64": "npm:0.27.3" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -24192,7 +24090,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10/7f1229328b0efc63c4184a61a7eb303df1e99818cc1d9e309fb92600703008e69821e8e984e9e9f54a627da14e0960d561db3a93029482ef96dc82dd267a60c2 + checksum: 10/aa74b8d8a3ed8e2eea4d8421737b322f4d21215244e8fa2156c6402d49b5bda01343c220196f1e3f830a7ce92b54ef653c6c723a8cc2e912bb4d17b7398b51ae languageName: node linkType: hard @@ -26839,16 +26737,6 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:5.0.0": - version: 5.0.0 - resolution: "https-proxy-agent@npm:5.0.0" - dependencies: - agent-base: "npm:6" - debug: "npm:4" - checksum: 10/517037badcbbe30757a9a88aaf5e8c198d31aa0b1e9c0a49a0053ab8e812809242218cc9ea1929171f74d95ae1ec89782ba471ffc3709b8910e91d1761f5f1a6 - languageName: node - linkType: hard - "https-proxy-agent@npm:^5.0.0": version: 5.0.1 resolution: "https-proxy-agent@npm:5.0.1" @@ -28327,15 +28215,6 @@ __metadata: languageName: node linkType: hard -"keyv@npm:^5.2.2": - version: 5.3.3 - resolution: "keyv@npm:5.3.3" - dependencies: - "@keyv/serialize": "npm:^1.0.3" - checksum: 10/979ed90d14584c87e9093d85426bc4ad336263b8b1523dd91a70a1ec3642e2650d12ac72f85454948996c330d6dfa71e5d4a4f55900c3a4d689d895a3e51dcf0 - languageName: node - linkType: hard - "khroma@npm:^2.1.0": version: 2.1.0 resolution: "khroma@npm:2.1.0" @@ -29390,13 +29269,6 @@ __metadata: languageName: node linkType: hard -"make-error@npm:^1.1.1": - version: 1.3.6 - resolution: "make-error@npm:1.3.6" - checksum: 10/b86e5e0e25f7f777b77fabd8e2cbf15737972869d852a22b7e73c17623928fccb826d8e46b9951501d3f20e51ad74ba8c59ed584f610526a48f8ccf88aaec402 - languageName: node - linkType: hard - "make-fetch-happen@npm:^10.2.1": version: 10.2.1 resolution: "make-fetch-happen@npm:10.2.1" @@ -30561,15 +30433,6 @@ __metadata: languageName: node linkType: hard -"mixpanel@npm:^0.18.0": - version: 0.18.1 - resolution: "mixpanel@npm:0.18.1" - dependencies: - https-proxy-agent: "npm:5.0.0" - checksum: 10/640ed982b2ee69638d1cefc9257977e9f131010f2a324169326af112e59cbd5f9159bd4e3fc7600cad9f5a211457b2d9f2723fc5f4f0c20ae1bb894c8683dd08 - languageName: node - linkType: hard - "mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.6": version: 0.5.6 resolution: "mkdirp@npm:0.5.6" @@ -37416,44 +37279,6 @@ __metadata: languageName: node linkType: hard -"ts-node@npm:^10.9.2": - version: 10.9.2 - resolution: "ts-node@npm:10.9.2" - dependencies: - "@cspotcode/source-map-support": "npm:^0.8.0" - "@tsconfig/node10": "npm:^1.0.7" - "@tsconfig/node12": "npm:^1.0.7" - "@tsconfig/node14": "npm:^1.0.0" - "@tsconfig/node16": "npm:^1.0.2" - acorn: "npm:^8.4.1" - acorn-walk: "npm:^8.1.1" - arg: "npm:^4.1.0" - create-require: "npm:^1.1.0" - diff: "npm:^4.0.1" - make-error: "npm:^1.1.1" - v8-compile-cache-lib: "npm:^3.0.1" - yn: "npm:3.1.1" - peerDependencies: - "@swc/core": ">=1.2.50" - "@swc/wasm": ">=1.2.50" - "@types/node": "*" - typescript: ">=2.7" - peerDependenciesMeta: - "@swc/core": - optional: true - "@swc/wasm": - optional: true - bin: - ts-node: dist/bin.js - ts-node-cwd: dist/bin-cwd.js - ts-node-esm: dist/bin-esm.js - ts-node-script: dist/bin-script.js - ts-node-transpile-only: dist/bin-transpile.js - ts-script: dist/bin-script-deprecated.js - checksum: 10/a91a15b3c9f76ac462f006fa88b6bfa528130dcfb849dd7ef7f9d640832ab681e235b8a2bc58ecde42f72851cc1d5d4e22c901b0c11aa51001ea1d395074b794 - languageName: node - linkType: hard - "ts-pattern@npm:^5.1.0": version: 5.7.1 resolution: "ts-pattern@npm:5.7.1" @@ -37493,11 +37318,11 @@ __metadata: languageName: node linkType: hard -"tsx@npm:^4.19.2": - version: 4.19.4 - resolution: "tsx@npm:4.19.4" +"tsx@npm:^4.21.0": + version: 4.21.0 + resolution: "tsx@npm:4.21.0" dependencies: - esbuild: "npm:~0.25.0" + esbuild: "npm:~0.27.0" fsevents: "npm:~2.3.3" get-tsconfig: "npm:^4.7.5" dependenciesMeta: @@ -37505,7 +37330,7 @@ __metadata: optional: true bin: tsx: dist/cli.mjs - checksum: 10/4dde315aeda70b9cadfecbc8d05b1625f5831018b9cb2db25cbbd03c5f5ee9c59cdc6652a0fd8492176b50944a5af1d5af352b944d024f4a719f58d6f2ac3a7f + checksum: 10/7afedeff855ba98c47dc28b33d7e8e253c4dc1f791938db402d79c174bdf806b897c1a5f91e5b1259c112520c816f826b4c5d98f0bad7e95b02dec66fedb64d2 languageName: node linkType: hard @@ -37668,7 +37493,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:>=5, typescript@npm:^5.5.4, typescript@npm:^5.7.2": +"typescript@npm:>=5, typescript@npm:^5.9.3": version: 5.9.3 resolution: "typescript@npm:5.9.3" bin: @@ -37698,7 +37523,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A>=5#optional!builtin, typescript@patch:typescript@npm%3A^5.5.4#optional!builtin, typescript@patch:typescript@npm%3A^5.7.2#optional!builtin": +"typescript@patch:typescript@npm%3A>=5#optional!builtin, typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": version: 5.9.3 resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" bin: @@ -38330,13 +38155,6 @@ __metadata: languageName: node linkType: hard -"v8-compile-cache-lib@npm:^3.0.1": - version: 3.0.1 - resolution: "v8-compile-cache-lib@npm:3.0.1" - checksum: 10/88d3423a52b6aaf1836be779cab12f7016d47ad8430dffba6edf766695e6d90ad4adaa3d8eeb512cc05924f3e246c4a4ca51e089dccf4402caa536b5e5be8961 - languageName: node - linkType: hard - "v8-to-istanbul@npm:^9.0.0": version: 9.3.0 resolution: "v8-to-istanbul@npm:9.3.0" @@ -39404,13 +39222,6 @@ __metadata: languageName: node linkType: hard -"yn@npm:3.1.1": - version: 3.1.1 - resolution: "yn@npm:3.1.1" - checksum: 10/2c487b0e149e746ef48cda9f8bad10fc83693cd69d7f9dcd8be4214e985de33a29c9e24f3c0d6bcf2288427040a8947406ab27f7af67ee9456e6b84854f02dd6 - languageName: node - linkType: hard - "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0"