diff --git a/.changeset/lazy-trees-judge.md b/.changeset/lazy-trees-judge.md new file mode 100644 index 0000000..7018de4 --- /dev/null +++ b/.changeset/lazy-trees-judge.md @@ -0,0 +1,6 @@ +--- +'@powersync/cli-core': minor +'powersync': minor +--- + +Resolve organization ID and project ID from instance ID automatically. diff --git a/.gitignore b/.gitignore index 14a46e8..502a4c8 100644 --- a/.gitignore +++ b/.gitignore @@ -143,6 +143,7 @@ playground/ # PNPM .pnpm-store/ +.pnpmfile.cjs # MacOS .DS_Store diff --git a/cli/src/api/cloud/validate-cloud-link-config.ts b/cli/src/api/cloud/validate-cloud-link-config.ts index 365ad1f..87dc7f9 100644 --- a/cli/src/api/cloud/validate-cloud-link-config.ts +++ b/cli/src/api/cloud/validate-cloud-link-config.ts @@ -1,35 +1,30 @@ -import { createAccountsHubClient, OBJECT_ID_REGEX } from '@powersync/cli-core'; +import type { ResolvedCloudCLIConfig } from '@powersync/cli-schemas'; + +import { createAccountsHubClient, ensureObjectId, resolveCloudInstanceLink } from '@powersync/cli-core'; import { PowerSyncManagementClient } from '@powersync/management-client'; type InstanceConfigResponse = Awaited>; -export type CloudLinkValidationInput = { - instanceId?: string; +export type ValidateCloudProjectOptions = { + cloudClient: PowerSyncManagementClient; orgId: string; projectId: string; }; -export type ValidateCloudLinkConfigOptions = { +export type FetchCloudInstanceConfigOptions = { cloudClient: PowerSyncManagementClient; - input: CloudLinkValidationInput; - validateInstance?: boolean; + instanceId?: string; + orgId?: string; + projectId?: string; }; -export type ValidateCloudLinkConfigResult = { - instanceConfig?: InstanceConfigResponse; +export type FetchCloudInstanceConfigResult = { + instanceConfig: InstanceConfigResponse; + linked: ResolvedCloudCLIConfig; }; -function ensureObjectId(value: string, flagName: '--instance-id' | '--org-id' | '--project-id') { - if (!OBJECT_ID_REGEX.test(value)) { - throw new Error(`Invalid ${flagName} "${value}". Expected a BSON ObjectID (24 hex characters).`); - } -} - -export async function validateCloudLinkConfig( - options: ValidateCloudLinkConfigOptions -): Promise { - const { cloudClient, input, validateInstance = false } = options; - const { instanceId, orgId, projectId } = input; +export async function validateCloudProject(options: ValidateCloudProjectOptions): Promise { + const { orgId, projectId } = options; ensureObjectId(orgId, '--org-id'); ensureObjectId(projectId, '--project-id'); @@ -56,27 +51,27 @@ export async function validateCloudLinkConfig( `Project ${projectId} was not found in organization ${orgId}, or is not accessible with the current token.` ); } +} - if (!validateInstance) { - return {}; - } - - if (!instanceId) { - throw new Error('Instance validation requested but no instance ID was provided.'); - } +export async function fetchCloudInstanceConfig( + options: FetchCloudInstanceConfigOptions +): Promise { + const { cloudClient, instanceId, orgId, projectId } = options; - ensureObjectId(instanceId, '--instance-id'); + const linked = await resolveCloudInstanceLink({ client: cloudClient, instanceId, orgId, projectId }); + let instanceConfig: InstanceConfigResponse; try { - const instanceConfig = await cloudClient.getInstanceConfig({ - app_id: projectId, - id: instanceId, - org_id: orgId + instanceConfig = await cloudClient.getInstanceConfig({ + app_id: linked.project_id, + id: linked.instance_id, + org_id: linked.org_id }); - return { instanceConfig }; } catch { throw new Error( - `Instance ${instanceId} was not found in project ${projectId} in organization ${orgId}, or is not accessible with the current token.` + `Instance ${linked.instance_id} was not found in project ${linked.project_id} in organization ${linked.org_id}, or is not accessible with the current token.` ); } + + return { instanceConfig, linked }; } diff --git a/cli/src/commands/deploy/index.ts b/cli/src/commands/deploy/index.ts index e53899e..95feb9e 100644 --- a/cli/src/commands/deploy/index.ts +++ b/cli/src/commands/deploy/index.ts @@ -16,10 +16,7 @@ export default class DeployAll extends WithSyncConfigFilePath(BaseDeployCommand) `See also ${ux.colorize('blue', 'powersync deploy sync-config')} to deploy only sync config changes.`, `See also ${ux.colorize('blue', 'powersync deploy service-config')} to deploy only service config changes.` ].join('\n'); - static examples = [ - '<%= config.bin %> <%= command.id %>', - '<%= config.bin %> <%= command.id %> --instance-id= --project-id=' - ]; + static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --instance-id=']; static flags = { ...GENERAL_VALIDATION_FLAG_HELPERS.flags }; diff --git a/cli/src/commands/deploy/service-config.ts b/cli/src/commands/deploy/service-config.ts index c9c7122..e095206 100644 --- a/cli/src/commands/deploy/service-config.ts +++ b/cli/src/commands/deploy/service-config.ts @@ -12,10 +12,7 @@ const SERVICE_CONFIG_VALIDATION_FLAGS = generateValidationTestFlags({ export default class DeployServiceConfig extends BaseDeployCommand { static description = 'Deploy only service config changes (without sync config updates).'; - static examples = [ - '<%= config.bin %> <%= command.id %>', - '<%= config.bin %> <%= command.id %> --instance-id= --project-id=' - ]; + static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --instance-id=']; static flags = { ...SERVICE_CONFIG_VALIDATION_FLAGS.flags }; diff --git a/cli/src/commands/deploy/sync-config.ts b/cli/src/commands/deploy/sync-config.ts index 74c2319..de1cbb1 100644 --- a/cli/src/commands/deploy/sync-config.ts +++ b/cli/src/commands/deploy/sync-config.ts @@ -17,10 +17,7 @@ const SYNC_CONFIG_VALIDATION_FLAGS = generateValidationTestFlags({ export default class DeploySyncConfig extends WithSyncConfigFilePath(BaseDeployCommand) { static description = 'Deploy only sync config changes.'; - static examples = [ - '<%= config.bin %> <%= command.id %>', - '<%= config.bin %> <%= command.id %> --instance-id= --project-id=' - ]; + static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --instance-id=']; static flags = { ...SYNC_CONFIG_VALIDATION_FLAGS.flags }; diff --git a/cli/src/commands/fetch/status.ts b/cli/src/commands/fetch/status.ts index 02b6596..573702a 100644 --- a/cli/src/commands/fetch/status.ts +++ b/cli/src/commands/fetch/status.ts @@ -10,7 +10,7 @@ export default class FetchStatus extends SharedInstanceCommand { static examples = [ '<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --output=json', - '<%= config.bin %> <%= command.id %> --instance-id= --project-id=' + '<%= config.bin %> <%= command.id %> --instance-id=' ]; static flags = { output: Flags.string({ diff --git a/cli/src/commands/generate/schema.ts b/cli/src/commands/generate/schema.ts index e101b3e..6b05566 100644 --- a/cli/src/commands/generate/schema.ts +++ b/cli/src/commands/generate/schema.ts @@ -19,7 +19,7 @@ export default class GenerateSchema extends WithSyncConfigFilePath(SharedInstanc 'Generate a client-side schema file from the instance database schema and sync config. Supports multiple output types (e.g. type, dart). Requires a linked instance. Cloud and self-hosted.'; static examples = [ '<%= config.bin %> <%= command.id %> --output=ts --output-path=schema.ts', - '<%= config.bin %> <%= command.id %> --output=dart --output-path=lib/schema.dart --instance-id= --project-id=' + '<%= config.bin %> <%= command.id %> --output=dart --output-path=lib/schema.dart --instance-id=' ]; static flags = { output: Flags.string({ diff --git a/cli/src/commands/init/cloud.ts b/cli/src/commands/init/cloud.ts index 2f1d408..ba65f49 100644 --- a/cli/src/commands/init/cloud.ts +++ b/cli/src/commands/init/cloud.ts @@ -70,10 +70,7 @@ export default class InitCloud extends InstanceCommand { 'Create a new instance with ', ux.colorize('blue', '\tpowersync link cloud --create --org-id= --project-id='), 'or pull an existing instance with ', - ux.colorize( - 'blue', - '\tpowersync pull instance --org-id= --project-id= --instance-id=' - ), + ux.colorize('blue', '\tpowersync pull instance --instance-id='), `Tip: use ${ux.colorize('blue', 'powersync fetch instances')} to see available organizations and projects for your token.`, 'Then run', ux.colorize('blue', '\tpowersync deploy'), diff --git a/cli/src/commands/link/cloud.ts b/cli/src/commands/link/cloud.ts index 212c200..a1def68 100644 --- a/cli/src/commands/link/cloud.ts +++ b/cli/src/commands/link/cloud.ts @@ -1,3 +1,5 @@ +import type { ResolvedCloudCLIConfig } from '@powersync/cli-schemas'; + import { Flags, ux } from '@oclif/core'; import { CLI_FILENAME, @@ -10,16 +12,17 @@ import { } from '@powersync/cli-core'; import { createCloudInstance } from '../../api/cloud/create-cloud-instance.js'; -import { validateCloudLinkConfig } from '../../api/cloud/validate-cloud-link-config.js'; +import { fetchCloudInstanceConfig, validateCloudProject } from '../../api/cloud/validate-cloud-link-config.js'; import { writeCloudLink } from '../../api/cloud/write-cloud-link.js'; export default class LinkCloud extends CloudInstanceCommand { static commandHelpGroup = CommandHelpGroup.PROJECT_SETUP; static description = - 'Write or update cli.yaml with a Cloud instance (instance-id, org-id, project-id). Use --create to create a new instance from service.yaml name/region and link it; omit --instance-id when using --create. Org ID is optional when the token has a single organization.'; + 'Write or update cli.yaml with a Cloud instance. Use --create to create a new instance from service.yaml name/region and link it; omit --instance-id when using --create.'; static examples = [ - '<%= config.bin %> <%= command.id %> --project-id=', + '<%= config.bin %> <%= command.id %> --instance-id=', '<%= config.bin %> <%= command.id %> --create --project-id=', + '<%= config.bin %> <%= command.id %> --create --project-id= --org-id=', '<%= config.bin %> <%= command.id %> --instance-id= --project-id= --org-id=' ]; static flags = { @@ -36,13 +39,13 @@ export default class LinkCloud extends CloudInstanceCommand { 'org-id': Flags.string({ default: env.ORG_ID, description: - 'Organization ID. Optional when the token has a single org; required when the token has multiple orgs. Resolved: flag → ORG_ID → cli.yaml.', + 'Organization ID. Required with --create when the token has multiple orgs; optional when linking an existing instance.', required: false }), 'project-id': Flags.string({ default: env.PROJECT_ID, - description: 'Project ID. Resolved: flag → PROJECT_ID → cli.yaml.', - required: true + description: 'Project ID. Required with --create; optional assertion when linking an existing instance.', + required: false }) }; static summary = '[Cloud only] Link to a PowerSync Cloud instance (or create one with --create).'; @@ -51,10 +54,6 @@ export default class LinkCloud extends CloudInstanceCommand { const { flags } = await this.parse(LinkCloud); let { create, directory, 'instance-id': instanceId, 'org-id': orgId, 'project-id': projectId } = flags; - if (!orgId) { - orgId = await getDefaultOrgId(); - } - const projectDirectory = this.resolveProjectDir(flags); if (create) { if (instanceId) { @@ -63,12 +62,18 @@ export default class LinkCloud extends CloudInstanceCommand { }); } - try { - await validateCloudLinkConfig({ - cloudClient: this.client, - input: { orgId, projectId }, - validateInstance: false + if (!projectId) { + this.styledError({ + message: 'Creating a Cloud instance requires --project-id.' }); + } + + if (!orgId) { + orgId = await getDefaultOrgId(); + } + + try { + await validateCloudProject({ cloudClient: this.client, orgId: orgId!, projectId: projectId! }); } catch (error) { this.styledError({ message: error instanceof Error ? error.message : String(error) }); } @@ -80,8 +85,8 @@ export default class LinkCloud extends CloudInstanceCommand { try { const result = await createCloudInstance(client, { name: config.name, - orgId, - projectId, + orgId: orgId!, + projectId: projectId!, region: config.region }); newInstanceId = result.instanceId; @@ -96,7 +101,7 @@ export default class LinkCloud extends CloudInstanceCommand { expectedType: ServiceType.CLOUD, projectDir: projectDirectory }); - writeCloudLink(projectDirectory, { instanceId: newInstanceId, orgId, projectId }); + writeCloudLink(projectDirectory, { instanceId: newInstanceId, orgId: orgId!, projectId: projectId! }); this.log( ux.colorize('green', `Created Cloud instance ${newInstanceId} and updated ${directory}/${CLI_FILENAME}.`) ); @@ -110,17 +115,28 @@ export default class LinkCloud extends CloudInstanceCommand { }); } + let linked: ResolvedCloudCLIConfig | undefined; try { - await validateCloudLinkConfig({ + const validationResult = await fetchCloudInstanceConfig({ cloudClient: this.client, - input: { instanceId, orgId, projectId }, - validateInstance: true + instanceId, + orgId, + projectId }); + linked = validationResult.linked; } catch (error) { this.styledError({ message: error instanceof Error ? error.message : String(error) }); } - writeCloudLink(projectDirectory, { instanceId, orgId, projectId }); + if (!linked) { + this.styledError({ message: `Failed to resolve Cloud instance ${instanceId}.` }); + } + + writeCloudLink(projectDirectory, { + instanceId: linked.instance_id, + orgId: linked.org_id, + projectId: linked.project_id + }); ensureServiceTypeMatches({ command: this, configRequired: false, diff --git a/cli/src/commands/pull/index.ts b/cli/src/commands/pull/index.ts index d2ba6a8..2f57a37 100644 --- a/cli/src/commands/pull/index.ts +++ b/cli/src/commands/pull/index.ts @@ -2,7 +2,7 @@ import { Command } from '@oclif/core'; export default class Pull extends Command { static description = - 'Download current config from PowerSync Cloud into local YAML files. Use pull instance; pass --instance-id and --project-id when the directory is not yet linked (--org-id is optional when the token has a single organization).'; + 'Download current config from PowerSync Cloud into local YAML files. Use pull instance; pass --instance-id when the directory is not yet linked.'; static examples = ['<%= config.bin %> <%= command.id %>']; static hidden = true; static summary = '[Cloud only] Download Cloud config into local service.yaml and sync-config.yaml.'; diff --git a/cli/src/commands/pull/instance.ts b/cli/src/commands/pull/instance.ts index 51b1614..efe3d57 100644 --- a/cli/src/commands/pull/instance.ts +++ b/cli/src/commands/pull/instance.ts @@ -1,10 +1,12 @@ +import type { ResolvedCloudCLIConfig } from '@powersync/cli-schemas'; + import { Flags, ux } from '@oclif/core'; import { CLI_FILENAME, CloudInstanceCommand, CommandHelpGroup, ensureServiceTypeMatches, - getDefaultOrgId, + env, SERVICE_FILENAME, ServiceType, SYNC_FILENAME, @@ -17,7 +19,7 @@ import { join } from 'node:path'; import { buildServiceYaml } from '../../api/build-service-yaml.js'; import { CLOUD_SERVICE_TEMPLATE_PATH, writeCloudSyncConfigFile } from '../../api/cloud/create-cloud-template.js'; import { decodeFetchedCloudConfig } from '../../api/cloud/fetch-cloud-config.js'; -import { validateCloudLinkConfig } from '../../api/cloud/validate-cloud-link-config.js'; +import { fetchCloudInstanceConfig } from '../../api/cloud/validate-cloud-link-config.js'; import { writeCloudLink } from '../../api/cloud/write-cloud-link.js'; const SERVICE_FETCHED_FILENAME = 'service-fetched.yaml'; @@ -30,10 +32,10 @@ const PULL_CONFIG_HEADER = `# PowerSync Cloud config (fetched from cloud) export default class PullInstance extends CloudInstanceCommand { static commandHelpGroup = CommandHelpGroup.PROJECT_SETUP; static description = - 'Fetch an existing Cloud instance by ID: create the config directory if needed, write cli.yaml, and download service.yaml and sync-config.yaml. Pass --instance-id and --project-id when the directory is not yet linked; --org-id is optional when the token has a single organization. Cloud only.'; + 'Fetch an existing Cloud instance by ID: create the config directory if needed, write cli.yaml, and download service.yaml and sync-config.yaml. Cloud only.'; static examples = [ '<%= config.bin %> <%= command.id %>', - '<%= config.bin %> <%= command.id %> --instance-id= --project-id=', + '<%= config.bin %> <%= command.id %> --instance-id=', '<%= config.bin %> <%= command.id %> --instance-id= --project-id= --org-id=' ]; static flags = { @@ -48,20 +50,34 @@ export default class PullInstance extends CloudInstanceCommand { async run(): Promise { const { flags } = await this.parse(PullInstance); const { directory, 'instance-id': instanceId, 'org-id': _orgId, 'project-id': projectId } = flags; + const inputInstanceId = instanceId ?? env.INSTANCE_ID; + const inputOrgId = _orgId ?? env.ORG_ID; + const inputProjectId = projectId ?? env.PROJECT_ID; - const resolvedOrgId = _orgId ?? (await getDefaultOrgId().catch(() => null)); - /** - * The pull instance command can be used to create a new powersync project directory - */ + let resolvedLink: ResolvedCloudCLIConfig | undefined; + let instanceConfig; const projectDir = this.resolveProjectDir(flags); if (!existsSync(projectDir)) { - if (instanceId && resolvedOrgId && projectId) { - mkdirSync(projectDir, { recursive: true }); - } else { + if (!inputInstanceId) { this.styledError({ - message: `Directory "${directory}" not found. Pass --instance-id, and --project-id to create the config directory and link, or run this command from a directory that already contains a linked PowerSync config.` + message: `Directory "${directory}" not found. Pass --instance-id to create the config directory and link, or run this command from a directory that already contains a linked PowerSync config.` }); } + + try { + const validationResult = await fetchCloudInstanceConfig({ + cloudClient: this.client, + instanceId: inputInstanceId, + orgId: inputOrgId, + projectId: inputProjectId + }); + resolvedLink = validationResult.linked; + instanceConfig = validationResult.instanceConfig; + } catch (error) { + this.styledError({ message: error instanceof Error ? error.message : String(error) }); + } + + mkdirSync(projectDir, { recursive: true }); } ensureServiceTypeMatches({ @@ -74,32 +90,55 @@ export default class PullInstance extends CloudInstanceCommand { const linkPath = join(projectDir, CLI_FILENAME); if (!existsSync(linkPath)) { - if (!instanceId || !resolvedOrgId || !projectId) { + if (!resolvedLink) { + if (!inputInstanceId) { + this.styledError({ + message: `Linking is required. Pass --instance-id to this command, or run ${ux.colorize('blue', 'powersync link cloud --instance-id=')} first.` + }); + } + + try { + const validationResult = await fetchCloudInstanceConfig({ + cloudClient: this.client, + instanceId: inputInstanceId, + orgId: inputOrgId, + projectId: inputProjectId + }); + resolvedLink = validationResult.linked; + instanceConfig = validationResult.instanceConfig; + } catch (error) { + this.styledError({ message: error instanceof Error ? error.message : String(error) }); + } + } + + if (!resolvedLink) { this.styledError({ - message: `Linking is required. Pass --instance-id, --org-id, and --project-id to this command, or run ${ux.colorize('blue', 'powersync link cloud --instance-id= --org-id= --project-id=')} first.` + message: `Failed to resolve Cloud instance ${inputInstanceId}.` }); } - writeCloudLink(projectDir, { instanceId, orgId: resolvedOrgId, projectId }); + writeCloudLink(projectDir, { + instanceId: resolvedLink.instance_id, + orgId: resolvedLink.org_id, + projectId: resolvedLink.project_id + }); this.log(`Created ${ux.colorize('blue', `${directory}/${CLI_FILENAME}`)} with Cloud instance link.`); } const { linked } = await this.loadProject(flags); - let instanceConfig; - try { - const validationResult = await validateCloudLinkConfig({ - cloudClient: this.client, - input: { + if (!instanceConfig) { + try { + const validationResult = await fetchCloudInstanceConfig({ + cloudClient: this.client, instanceId: linked.instance_id, orgId: linked.org_id, projectId: linked.project_id - }, - validateInstance: true - }); - instanceConfig = validationResult.instanceConfig; - } catch (error) { - this.styledError({ message: error instanceof Error ? error.message : String(error) }); + }); + instanceConfig = validationResult.instanceConfig; + } catch (error) { + this.styledError({ message: error instanceof Error ? error.message : String(error) }); + } } if (!instanceConfig) { @@ -148,7 +187,6 @@ export default class PullInstance extends CloudInstanceCommand { writeFileSync(syncOutputPath, YAML_SYNC_RULES_SCHEMA + '\n' + fetched.syncRules, 'utf8'); this.log(`Wrote ${ux.colorize('blue', syncOutputName)} with sync config from the cloud.`); } else if (!fetched.syncRules && !syncExists) { - // If there is no sync config in the cloud and no existing sync config locally, we should still create an empty sync-config.yaml with the correct header and schema reference await writeCloudSyncConfigFile({ targetDir: projectDir }); this.log( `Wrote ${ux.colorize('blue', SYNC_FILENAME)} with template sync config (no sync config found in the cloud).` diff --git a/cli/test/commands/link.test.ts b/cli/test/commands/link.test.ts index 4429c05..16b452d 100644 --- a/cli/test/commands/link.test.ts +++ b/cli/test/commands/link.test.ts @@ -224,6 +224,7 @@ type: cloud it('errors when project does not exist in the organization', async () => { accountsClientMock.listProjects.mockResolvedValueOnce({ objects: [], total: 0 }); + managementClientMock.getInstanceConfig.mockRejectedValueOnce(new Error('not found')); const { error } = await runLinkCloudDirect([ `--instance-id=${INSTANCE_ID}`, @@ -231,8 +232,9 @@ type: cloud `--project-id=${PROJECT_ID}` ]); - expect(error?.message).toContain(`Project ${PROJECT_ID} was not found in organization ${ORG_ID}`); - expect(error?.message).not.toContain(', ::'); + expect(error?.message).toContain( + `Instance ${INSTANCE_ID} was not found in project ${PROJECT_ID} in organization ${ORG_ID}` + ); }); it('errors when instance does not exist and --create is not used', async () => { diff --git a/cli/test/commands/pull/instance.test.ts b/cli/test/commands/pull/instance.test.ts index 26d8629..65b697b 100644 --- a/cli/test/commands/pull/instance.test.ts +++ b/cli/test/commands/pull/instance.test.ts @@ -200,13 +200,13 @@ describe('pull instance', () => { it('errors when organization does not exist', async () => { accountsClientMock.getOrganization.mockRejectedValueOnce(new Error('not found')); const result = await runPullInstanceDirect(); - expect(result.error?.message).toContain(`Organization ${ORG_ID} was not found or is not accessible`); + expect(result.error?.message).toMatch(/Instance .* was not found in project .* in organization .*/); }); it('errors when project does not exist in the organization', async () => { accountsClientMock.listProjects.mockResolvedValueOnce({ objects: [], total: 0 }); const result = await runPullInstanceDirect(); - expect(result.error?.message).toContain(`Project ${PROJECT_ID} was not found in organization ${ORG_ID}`); + expect(result.error?.message).toMatch(/Instance .* was not found in project .* in organization .*/); }); }); }); diff --git a/cli/test/setup.ts b/cli/test/setup.ts index 695d419..ef5e301 100644 --- a/cli/test/setup.ts +++ b/cli/test/setup.ts @@ -39,6 +39,7 @@ export const managementClientMock = { deactivateInstance: vi.fn(), deployInstance: vi.fn(), destroyInstance: vi.fn(), + getInstance: vi.fn(), getInstanceConfig: vi.fn(), getInstanceStatus: vi.fn(), listRegions: vi.fn(), @@ -53,6 +54,11 @@ export function resetManagementClientMocks(): void { managementClientMock.compact.mockRejectedValue(new Error('mock compact failure')); managementClientMock.createInstance.mockResolvedValue({ id: MOCK_CLOUD_IDS.instanceId }); + managementClientMock.getInstance.mockResolvedValue({ + app_id: MOCK_CLOUD_IDS.projectId, + id: MOCK_CLOUD_IDS.instanceId, + org_id: MOCK_CLOUD_IDS.orgId + }); managementClientMock.destroyInstance.mockRejectedValue(new Error('mock destroy failure')); managementClientMock.deactivateInstance.mockRejectedValue(new Error('mock deactivate failure')); managementClientMock.deployInstance.mockRejectedValue(new Error('mock deploy failure')); diff --git a/cli/test/utils/resolve-cloud-instance-link.test.ts b/cli/test/utils/resolve-cloud-instance-link.test.ts new file mode 100644 index 0000000..8dc6d08 --- /dev/null +++ b/cli/test/utils/resolve-cloud-instance-link.test.ts @@ -0,0 +1,128 @@ +import type { PowerSyncManagementClient } from '@powersync/management-client'; + +import { resolveCloudInstanceLink } from '@powersync/cli-core'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { managementClientMock, MOCK_CLOUD_IDS, resetManagementClientMocks } from '../setup.js'; + +const { instanceId: INSTANCE_ID, orgId: ORG_ID, projectId: PROJECT_ID } = MOCK_CLOUD_IDS; +const OTHER_ORG_ID = '4ffabc821ea10f9b2a000002'; +const OTHER_PROJECT_ID = '699ef9c371c56d0007320544'; + +const mockClient = managementClientMock as unknown as PowerSyncManagementClient; + +describe('resolveCloudInstanceLink', () => { + beforeEach(() => { + resetManagementClientMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('throws when no instanceId is provided', async () => { + await expect(resolveCloudInstanceLink({ client: mockClient })).rejects.toThrow( + 'Cloud instance resolution requires an instance ID.' + ); + expect(managementClientMock.getInstance).not.toHaveBeenCalled(); + }); + + it('throws when instanceId has an invalid format', async () => { + await expect(resolveCloudInstanceLink({ client: mockClient, instanceId: 'not-a-valid-id' })).rejects.toThrow( + 'Invalid --instance-id' + ); + expect(managementClientMock.getInstance).not.toHaveBeenCalled(); + }); + + describe('when both orgId and projectId are provided', () => { + it('returns the resolved link without an API call', async () => { + const result = await resolveCloudInstanceLink({ + client: mockClient, + instanceId: INSTANCE_ID, + orgId: ORG_ID, + projectId: PROJECT_ID + }); + expect(result).toEqual({ instance_id: INSTANCE_ID, org_id: ORG_ID, project_id: PROJECT_ID, type: 'cloud' }); + expect(managementClientMock.getInstance).not.toHaveBeenCalled(); + }); + + it('throws when orgId has an invalid format', async () => { + await expect( + resolveCloudInstanceLink({ + client: mockClient, + instanceId: INSTANCE_ID, + orgId: 'bad-org', + projectId: PROJECT_ID + }) + ).rejects.toThrow('Invalid --org-id'); + expect(managementClientMock.getInstance).not.toHaveBeenCalled(); + }); + + it('throws when projectId has an invalid format', async () => { + await expect( + resolveCloudInstanceLink({ + client: mockClient, + instanceId: INSTANCE_ID, + orgId: ORG_ID, + projectId: 'bad-project' + }) + ).rejects.toThrow('Invalid --project-id'); + expect(managementClientMock.getInstance).not.toHaveBeenCalled(); + }); + }); + + describe('when org or project IDs are missing (API lookup path)', () => { + it('throws when orgId has an invalid format before calling the API', async () => { + await expect( + resolveCloudInstanceLink({ client: mockClient, instanceId: INSTANCE_ID, orgId: 'bad-org' }) + ).rejects.toThrow('Invalid --org-id'); + expect(managementClientMock.getInstance).not.toHaveBeenCalled(); + }); + + it('throws when projectId has an invalid format before calling the API', async () => { + await expect( + resolveCloudInstanceLink({ client: mockClient, instanceId: INSTANCE_ID, projectId: 'bad-project' }) + ).rejects.toThrow('Invalid --project-id'); + expect(managementClientMock.getInstance).not.toHaveBeenCalled(); + }); + + it('throws when the instance is not found', async () => { + managementClientMock.getInstance.mockRejectedValueOnce(new Error('not found')); + await expect(resolveCloudInstanceLink({ client: mockClient, instanceId: INSTANCE_ID })).rejects.toThrow( + `Instance ${INSTANCE_ID} was not found or is not accessible with the current token.` + ); + }); + + it('resolves all fields from the instance when only instanceId is provided', async () => { + const result = await resolveCloudInstanceLink({ client: mockClient, instanceId: INSTANCE_ID }); + expect(result).toEqual({ instance_id: INSTANCE_ID, org_id: ORG_ID, project_id: PROJECT_ID, type: 'cloud' }); + expect(managementClientMock.getInstance).toHaveBeenCalledWith({ id: INSTANCE_ID }); + }); + + it('throws when the provided orgId does not match the instance', async () => { + await expect( + resolveCloudInstanceLink({ client: mockClient, instanceId: INSTANCE_ID, orgId: OTHER_ORG_ID }) + ).rejects.toThrow(`Instance ${INSTANCE_ID} belongs to organization ${ORG_ID}, not ${OTHER_ORG_ID}.`); + }); + + it('throws when the provided projectId does not match the instance', async () => { + await expect( + resolveCloudInstanceLink({ client: mockClient, instanceId: INSTANCE_ID, projectId: OTHER_PROJECT_ID }) + ).rejects.toThrow(`Instance ${INSTANCE_ID} belongs to project ${PROJECT_ID}, not ${OTHER_PROJECT_ID}.`); + }); + + it('resolves correctly when instanceId and a matching orgId are provided', async () => { + const result = await resolveCloudInstanceLink({ client: mockClient, instanceId: INSTANCE_ID, orgId: ORG_ID }); + expect(result).toEqual({ instance_id: INSTANCE_ID, org_id: ORG_ID, project_id: PROJECT_ID, type: 'cloud' }); + }); + + it('resolves correctly when instanceId and a matching projectId are provided', async () => { + const result = await resolveCloudInstanceLink({ + client: mockClient, + instanceId: INSTANCE_ID, + projectId: PROJECT_ID + }); + expect(result).toEqual({ instance_id: INSTANCE_ID, org_id: ORG_ID, project_id: PROJECT_ID, type: 'cloud' }); + }); + }); +}); diff --git a/packages/cli-core/src/command-types/CloudInstanceCommand.ts b/packages/cli-core/src/command-types/CloudInstanceCommand.ts index ca728dd..4acf20c 100644 --- a/packages/cli-core/src/command-types/CloudInstanceCommand.ts +++ b/packages/cli-core/src/command-types/CloudInstanceCommand.ts @@ -9,12 +9,12 @@ import { PowerSyncManagementClient } from '@powersync/management-client'; import { existsSync } from 'node:fs'; import { join } from 'node:path'; -import { getDefaultOrgId } from '../clients/AccountsHubClientSDKClient.js'; import { createCloudClient } from '../clients/create-cloud-client.js'; import { ensureServiceTypeMatches, ServiceType } from '../utils/ensure-service-type.js'; import { env } from '../utils/env.js'; import { OBJECT_ID_REGEX } from '../utils/object-id.js'; import { CLI_FILENAME, SERVICE_FILENAME } from '../utils/project-config.js'; +import { resolveCloudInstanceLink } from '../utils/resolve-cloud-instance-link.js'; import { resolveSyncRulesContent } from '../utils/resolve-sync-rules-content.js'; import { parseYamlFile } from '../utils/yaml.js'; import { CommandHelpGroup, HelpGroup } from './HelpGroup.js'; @@ -41,36 +41,35 @@ export type CloudInstanceCommandFlags = Interfaces.InferredFlags< * 1. Command-line flags (--instance-id, --org-id, --project-id) * 2. Linked config from cli.yaml * 3. Environment variables (INSTANCE_ID, ORG_ID, PROJECT_ID) - * 4. If org_id is still missing: token's single org (via accounts API); error if multiple orgs. + * 4. If org_id or project_id is still missing: resolve it from the Cloud instance. * * @example * # Use linked project (cli.yaml) * pnpm exec powersync some-cloud-cmd * # Override with env - * INSTANCE_ID=... ORG_ID=... PROJECT_ID=... pnpm exec powersync some-cloud-cmd + * INSTANCE_ID=... pnpm exec powersync some-cloud-cmd * # Override with flags - * pnpm exec powersync some-cloud-cmd --instance-id=... --org-id=... --project-id=... + * pnpm exec powersync some-cloud-cmd --instance-id=... */ export abstract class CloudInstanceCommand extends InstanceCommand { static baseFlags = { /** * Instance ID, org ID, and project ID are resolved in order: flags → cli.yaml → env (INSTANCE_ID, ORG_ID, PROJECT_ID). + * Missing org/project IDs are resolved from the Cloud instance. */ ...InstanceCommand.baseFlags, 'instance-id': Flags.string({ - dependsOn: ['project-id'], description: 'PowerSync Cloud instance ID. Manually passed if the current context has not been linked.', helpGroup: HelpGroup.CLOUD_PROJECT, required: false }), 'org-id': Flags.string({ - description: - 'Organization ID (optional). Defaults to the token’s single org when only one is available; pass explicitly if the token has multiple orgs.', + description: 'Organization ID (optional). Resolved from the Cloud instance when omitted.', helpGroup: HelpGroup.CLOUD_PROJECT, required: false }), 'project-id': Flags.string({ - description: 'Project ID. Manually passed if the current context has not been linked.', + description: 'Project ID (optional). Resolved from the Cloud instance when omitted.', helpGroup: HelpGroup.CLOUD_PROJECT, required: false }) @@ -157,19 +156,7 @@ export abstract class CloudInstanceCommand extends InstanceCommand { const instance_id = flags['instance-id'] ?? (rawLink?.instance_id as string | undefined) ?? env.INSTANCE_ID; const project_id = flags['project-id'] ?? (rawLink?.project_id as string | undefined) ?? env.PROJECT_ID; - let org_id = flags['org-id'] ?? (rawLink?.org_id as string | undefined) ?? env.ORG_ID; - - try { - if (org_id == null && instance_id != null) { - org_id = await getDefaultOrgId(); - } - } catch (error) { - this.styledError({ - error, - message: - 'Linking is required before using this command. Provide flags, link the project (cli.yaml), or set environment variables.' - }); - } + const org_id = flags['org-id'] ?? (rawLink?.org_id as string | undefined) ?? env.ORG_ID; if (instance_id != null || project_id != null || org_id != null) { this.ensureObjectIdIfPresent(instance_id, '--instance-id'); @@ -177,17 +164,19 @@ export abstract class CloudInstanceCommand extends InstanceCommand { this.ensureObjectIdIfPresent(project_id, '--project-id'); try { - linked = ResolvedCloudCLIConfig.decode({ - instance_id: instance_id!, - org_id: org_id!, - project_id: project_id!, - type: 'cloud' - }); + linked = ResolvedCloudCLIConfig.decode( + await resolveCloudInstanceLink({ + client: this.client, + instanceId: instance_id, + orgId: org_id, + projectId: project_id + }) + ); } catch (error) { this.styledError({ error, message: - 'Linking is required before using this command. Provide flags, link the project (cli.yaml), or set environment variables.' + 'Linking is required before using this command. Provide --instance-id, link the project (cli.yaml), or set environment variables.' }); } } diff --git a/packages/cli-core/src/command-types/SharedInstanceCommand.ts b/packages/cli-core/src/command-types/SharedInstanceCommand.ts index 68b8e88..1973d6e 100644 --- a/packages/cli-core/src/command-types/SharedInstanceCommand.ts +++ b/packages/cli-core/src/command-types/SharedInstanceCommand.ts @@ -16,11 +16,11 @@ import { PowerSyncManagementClient } from '@powersync/management-client'; import { existsSync } from 'node:fs'; import { join } from 'node:path'; -import { getDefaultOrgId } from '../clients/AccountsHubClientSDKClient.js'; import { createCloudClient } from '../clients/create-cloud-client.js'; import { ensureServiceTypeMatches, ServiceType } from '../utils/ensure-service-type.js'; import { env } from '../utils/env.js'; import { CLI_FILENAME, SERVICE_FILENAME } from '../utils/project-config.js'; +import { resolveCloudInstanceLink } from '../utils/resolve-cloud-instance-link.js'; import { resolveSyncRulesContent } from '../utils/resolve-sync-rules-content.js'; import { parseYamlFile } from '../utils/yaml.js'; import { CloudProject } from './CloudInstanceCommand.js'; @@ -50,11 +50,11 @@ export type SharedInstanceCommandFlags = Interfaces.InferredFlags< * # Use linked project (cli.yaml determines cloud vs self-hosted) * pnpm exec powersync some-shared-cmd * # Force cloud with env - * INSTANCE_ID=... ORG_ID=... PROJECT_ID=... pnpm exec powersync some-shared-cmd + * INSTANCE_ID=... pnpm exec powersync some-shared-cmd * # Force self-hosted with flag * pnpm exec powersync some-shared-cmd --api-url=https://... * # Force cloud with flags - * pnpm exec powersync some-shared-cmd --instance-id=... --org-id=... --project-id=... + * pnpm exec powersync some-shared-cmd --instance-id=... */ export abstract class SharedInstanceCommand extends InstanceCommand { static baseFlags = { @@ -67,20 +67,18 @@ export abstract class SharedInstanceCommand extends InstanceCommand { required: false }), 'instance-id': Flags.string({ - dependsOn: ['project-id'], description: '[Cloud] PowerSync Cloud instance ID (BSON ObjectID). When set, context is treated as cloud (exclusive with --api-url). Resolved: flag → cli.yaml → INSTANCE_ID.', helpGroup: HelpGroup.CLOUD_PROJECT, required: false }), 'org-id': Flags.string({ - description: - '[Cloud] Organization ID (optional). Defaults to the token’s single org when only one is available; pass explicitly if the token has multiple orgs. Resolved: flag → cli.yaml → ORG_ID.', + description: '[Cloud] Organization ID (optional). Resolved from the Cloud instance when omitted.', helpGroup: HelpGroup.CLOUD_PROJECT, required: false }), 'project-id': Flags.string({ - description: '[Cloud] Project ID. Resolved: flag → cli.yaml → PROJECT_ID.', + description: '[Cloud] Project ID (optional). Resolved from the Cloud instance when omitted.', helpGroup: HelpGroup.CLOUD_PROJECT, required: false }), @@ -166,7 +164,7 @@ export abstract class SharedInstanceCommand extends InstanceCommand { const linkMissingErrorMessage = [ 'Linking is required before using this command.', - 'Provide --api-url (self-hosted) or --instance-id with --org-id and --project-id (cloud), or link the project first.' + 'Provide --api-url (self-hosted) or --instance-id (cloud), or link the project first.' ].join('\n'); // If we don't have a project type by now, we need to error @@ -190,17 +188,14 @@ export abstract class SharedInstanceCommand extends InstanceCommand { } else { const _rawCloudCLIConfig = (rawCLIConfig as CloudCLIConfig) ?? { type: 'cloud' }; try { - let org_id = flags['org-id'] ?? _rawCloudCLIConfig.org_id ?? env.ORG_ID; - if (org_id == null && (flags['instance-id'] || env.INSTANCE_ID)) { - org_id = await getDefaultOrgId(); - } - - cliConfig = ResolvedCloudCLIConfig.decode({ - ..._rawCloudCLIConfig, - instance_id: flags['instance-id'] ?? _rawCloudCLIConfig.instance_id! ?? env.INSTANCE_ID, - org_id: org_id!, - project_id: flags['project-id'] ?? _rawCloudCLIConfig.project_id! ?? env.PROJECT_ID - }); + cliConfig = ResolvedCloudCLIConfig.decode( + await resolveCloudInstanceLink({ + client: this.cloudClient, + instanceId: flags['instance-id'] ?? _rawCloudCLIConfig.instance_id ?? env.INSTANCE_ID, + orgId: flags['org-id'] ?? _rawCloudCLIConfig.org_id ?? env.ORG_ID, + projectId: flags['project-id'] ?? _rawCloudCLIConfig.project_id ?? env.PROJECT_ID + }) + ); } catch (error) { this.styledError({ error, message: linkMissingErrorMessage }); } diff --git a/packages/cli-core/src/index.ts b/packages/cli-core/src/index.ts index e2ef8c1..0a9034d 100644 --- a/packages/cli-core/src/index.ts +++ b/packages/cli-core/src/index.ts @@ -23,6 +23,7 @@ export * from './utils/ensure-service-type.js'; export * from './utils/env.js'; export * from './utils/object-id.js'; export * from './utils/project-config.js'; +export * from './utils/resolve-cloud-instance-link.js'; export * from './utils/resolve-sync-rules-content.js'; export * from './utils/sync-config-file-path-flags.js'; export * from './utils/yaml.js'; diff --git a/packages/cli-core/src/utils/object-id.ts b/packages/cli-core/src/utils/object-id.ts index 9a30632..c38b9cb 100644 --- a/packages/cli-core/src/utils/object-id.ts +++ b/packages/cli-core/src/utils/object-id.ts @@ -1 +1,7 @@ export const OBJECT_ID_REGEX = /^[0-9a-fA-F]{24}$/; + +export function ensureObjectId(value: string, label: string): void { + if (!OBJECT_ID_REGEX.test(value)) { + throw new Error(`Invalid ${label} "${value}". Expected a BSON ObjectID (24 hex characters).`); + } +} diff --git a/packages/cli-core/src/utils/resolve-cloud-instance-link.ts b/packages/cli-core/src/utils/resolve-cloud-instance-link.ts new file mode 100644 index 0000000..55e656a --- /dev/null +++ b/packages/cli-core/src/utils/resolve-cloud-instance-link.ts @@ -0,0 +1,61 @@ +import type { ResolvedCloudCLIConfig } from '@powersync/cli-schemas'; +import type { PowerSyncManagementClient } from '@powersync/management-client'; + +import { ensureObjectId } from './object-id.js'; + +export type ResolveCloudInstanceLinkInput = { + client: PowerSyncManagementClient; + instanceId?: string; + orgId?: string; + projectId?: string; +}; + +/** + * Resolves the full Cloud link from an instance ID. If org/project IDs are missing, fetches them from the instance. + */ +export async function resolveCloudInstanceLink(input: ResolveCloudInstanceLinkInput): Promise { + const { client, instanceId, orgId, projectId } = input; + + if (!instanceId) { + throw new Error('Cloud instance resolution requires an instance ID.'); + } + + ensureObjectId(instanceId, '--instance-id'); + + if (orgId && projectId) { + ensureObjectId(orgId, '--org-id'); + ensureObjectId(projectId, '--project-id'); + return { + instance_id: instanceId, + org_id: orgId, + project_id: projectId, + type: 'cloud' + }; + } + + // Fail fast on bad IDs + if (orgId) ensureObjectId(orgId, '--org-id'); + if (projectId) ensureObjectId(projectId, '--project-id'); + + let instance; + try { + instance = await client.getInstance({ id: instanceId }); + } catch { + throw new Error(`Instance ${instanceId} was not found or is not accessible with the current token.`); + } + + if (orgId && orgId !== instance.org_id) { + throw new Error(`Instance ${instanceId} belongs to organization ${instance.org_id}, not ${orgId}.`); + } + + if (projectId && projectId !== instance.app_id) { + throw new Error(`Instance ${instanceId} belongs to project ${instance.app_id}, not ${projectId}.`); + } + + return { + instance_id: instance.id, + org_id: instance.org_id, + project_id: instance.app_id, + type: 'cloud' + }; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5bd6b85..7b13277 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,11 +7,11 @@ settings: catalogs: default: '@powersync/management-client': - specifier: ^0.0.6 - version: 0.0.6 + specifier: ^0.1.0 + version: 0.1.0 '@powersync/management-types': - specifier: ^0.0.5 - version: 0.0.5 + specifier: ^0.1.0 + version: 0.1.0 '@powersync/service-client': specifier: ^0.0.3 version: 0.0.3 @@ -102,10 +102,10 @@ importers: version: link:../packages/schemas '@powersync/management-client': specifier: 'catalog:' - version: 0.0.6(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) + version: 0.1.0(@types/json-schema@7.0.15) '@powersync/management-types': specifier: 'catalog:' - version: 0.0.5 + version: 0.1.0 '@powersync/service-sync-rules': specifier: 'catalog:' version: 0.34.0 @@ -187,10 +187,10 @@ importers: version: link:../schemas '@powersync/management-client': specifier: 'catalog:' - version: 0.0.6(@types/debug@4.1.12)(@types/json-schema@7.0.15)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2) + version: 0.1.0(@types/json-schema@7.0.15) '@powersync/management-types': specifier: 'catalog:' - version: 0.0.5 + version: 0.1.0 '@powersync/service-client': specifier: 'catalog:' version: 0.0.3(@types/debug@4.1.12)(@types/json-schema@7.0.15)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2) @@ -339,7 +339,7 @@ importers: dependencies: '@powersync/management-types': specifier: 'catalog:' - version: 0.0.5 + version: 0.1.0 '@powersync/service-schema': specifier: 'catalog:' version: 1.20.2 @@ -1448,27 +1448,33 @@ packages: '@journeyapps-labs/common-sdk@1.0.2': resolution: {integrity: sha512-bPFrrSGdVnaVFe6ymc/desjZk02gQ8f0EKggrU1/BQIeFJvGeQ804iOJsIYmUVU1fv4qNmJD0mD0GUjxu93Z8g==} - '@journeyapps-labs/common-sdk@1.0.3': - resolution: {integrity: sha512-FjmLBhy6VjT+jrQc/uKKpHetFS+qW/s8rAZd4i+4T5Iv5kNs0kAkDBdK0ArmryMDvdrDmFZukkziGIv1Sgw3jA==} + '@journeyapps-labs/common-sdk@1.0.4': + resolution: {integrity: sha512-KNzIuYftc8+7qHGWkUnhJKfIBGx7Stbmk66vdhuCkZyIGKrmaqTQzbVDHUyBS/SO+2+K5T2W2QfSVT5zHij99A==} '@journeyapps-labs/common-utils@1.0.1': resolution: {integrity: sha512-9P1f++wHSveqMQIWulLdGnDgvMdEqXP+j8eIscDm2bAuiMCIbij856AQgdrG0mQPPhL5SlUNlSGrfdKfhul2QA==} - '@journeyapps-labs/micro-codecs@1.0.1': - resolution: {integrity: sha512-Iy2sElOvk6C9NpzLHi0M2CYmVHVSRYK0y+BP/wRzaJ45z/3OUuXdwp9TqCEfVlqDIdDafoia7ES6X7oBWEBODw==} - '@journeyapps-labs/micro-codecs@1.0.2': resolution: {integrity: sha512-RytuC7bFhSChlDYIxPQPEog9o6Qvb7+oMsjN72LI5A0w82HBshCy8/v+OHMEL8UiI8nsHk4KQQP9LBXbuDuBOg==} + '@journeyapps-labs/micro-codecs@1.0.3': + resolution: {integrity: sha512-n1Du6wzfrumzUpjSeAl54jHEJYt4Tb1w12L9zA6hiibvOJAhwEfetzmbFEdD8gnS1mQhcHXzSZFImlBRZe+prA==} + '@journeyapps-labs/micro-errors@1.0.1': resolution: {integrity: sha512-L7a6AC0mX+AWw1q7RfFYNRrfxWrMlXMnNnFPysHBuZFrtLh4bT/4cnGwXANlm5OBNQC2urhTmtpdOXZVlxC7yw==} '@journeyapps-labs/micro-schema@1.0.1': resolution: {integrity: sha512-laimOhtrByxRb52yi880nVkPAHfqPZ4AzefmG161fto7IPoyhnSEwdWkou4byWS8II1d4R62Jhz01+wBTMHzdQ==} + '@journeyapps-labs/micro-schema@1.0.3': + resolution: {integrity: sha512-3n8d64jgDc/XJwGD8QRarfZs+JqEmuIbT9hEyY8nZR0IKsBkZEe6G9QYKm0tur6tisKs0YIcDcWjfzYcxRfmag==} + '@journeyapps-labs/micro-streaming@1.0.1': resolution: {integrity: sha512-woWFNtPwKZwTz5NQ4RUecGOPXXO/W/KLkx8YOx9mvZ7XY3Lx1bDRpI1i6ZSBPeix+IBbl+bs394R0qlFliI1cg==} + '@journeyapps-labs/micro-streaming@1.0.2': + resolution: {integrity: sha512-CdgWC817TmbTof7IL6Oy0hCF1KCa6RXogh2uEbOYRNz+XwC1q3KQeChp38qQXvaZZtqdIsiMlZGiGGa638eTFw==} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1605,11 +1611,11 @@ packages: resolution: {integrity: sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==} engines: {node: '>=12'} - '@powersync/management-client@0.0.6': - resolution: {integrity: sha512-s5tvk/3JUw9B1ry0X8YH30sMscqf5xkbGK0XNSZ4X6o0ATtJWxxe97Fgg1HoNTfN2W7lMSyTyZnBdf9jsVOluQ==} + '@powersync/management-client@0.1.0': + resolution: {integrity: sha512-YGJPNZHrSwUF3ofhz8VhbU1/QCF4KUKWGTyfzsDkmGJ9p8DESF5tZ1r0O7Nnmt1o2C0JOaQ0pV8z2pgnE1qb0g==} - '@powersync/management-types@0.0.5': - resolution: {integrity: sha512-sh1vRo1pq0D2oxijiyy0bHz1fIVkuiZkW+hTSyDSumNtbmT18ELe7+Mto29xlCzQ10oJ1YeyskUwpi+ZWzfuIQ==} + '@powersync/management-types@0.1.0': + resolution: {integrity: sha512-Ypv1UCu2rOlTKS8HjGl21aMd1NQnsG9isrs7OyIamjlMM1n2wLjbGuZNacu7Wzqc9CPeNZisUO4QXKyfuXa2kw==} '@powersync/service-client@0.0.3': resolution: {integrity: sha512-K83tOFdjRyFh76c4CKViRFyGJfBH9wSvtOvF2PxtbajAIPMzZEeunpnFj3vphx8CQyrDFWkR8/1Qa0BiCo30xQ==} @@ -1626,6 +1632,9 @@ packages: '@powersync/service-types@0.15.0': resolution: {integrity: sha512-wIdHCT+vhwoJAMZ414/dwG8fd51+fEWnPbwShkwNE5XTwA0UGOT8aGyVZqzT8PT0sVos5famYYqG203Mjb7q6g==} + '@powersync/service-types@0.15.2': + resolution: {integrity: sha512-GXQqCuTME+S1nV9CLbxtgYuAtMGFFLkDubm+DRTNGQqur8wBVzJp3haHCHFBVJ03Bhec+K7AIrB6CTii7b/HJg==} + '@powersync/sync-config-tools@0.1.1': resolution: {integrity: sha512-+lSmE6uGDUmUFc+XnYIatp746LMMLCw0DK+VyWnheuk4LRxENSqaZwLYySekWTDg9fBsXQJakAQ6sHHVMSTsbg==} @@ -6299,6 +6308,7 @@ packages: uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true v8-compile-cache-lib@3.0.1: @@ -8434,123 +8444,29 @@ snapshots: - tsx - yaml - '@journeyapps-labs/common-sdk@1.0.3(@types/debug@4.1.12)(@types/json-schema@7.0.15)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2)': + '@journeyapps-labs/common-sdk@1.0.4(@types/json-schema@7.0.15)': dependencies: '@journeyapps-labs/micro-errors': 1.0.1 - '@journeyapps-labs/micro-streaming': 1.0.1(@types/debug@4.1.12)(@types/json-schema@7.0.15)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2) + '@journeyapps-labs/micro-streaming': 1.0.2(@types/json-schema@7.0.15) '@types/node': 25.5.0 agentkeepalive: 4.6.0 bson: 7.2.0 lodash: 4.17.23 uuid: 13.0.0 transitivePeerDependencies: - - '@edge-runtime/vm' - - '@types/debug' - '@types/json-schema' - - '@vitest/browser' - - '@vitest/ui' - - happy-dom - - jiti - - jsdom - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - '@journeyapps-labs/common-sdk@1.0.3(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)': - dependencies: - '@journeyapps-labs/micro-errors': 1.0.1 - '@journeyapps-labs/micro-streaming': 1.0.1(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - '@types/node': 25.5.0 - agentkeepalive: 4.6.0 - bson: 7.2.0 - lodash: 4.17.23 - uuid: 13.0.0 - transitivePeerDependencies: - - '@edge-runtime/vm' - - '@types/debug' - - '@types/json-schema' - - '@vitest/browser' - - '@vitest/ui' - - happy-dom - - jiti - - jsdom - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml '@journeyapps-labs/common-utils@1.0.1': dependencies: uuid: 13.0.0 - '@journeyapps-labs/micro-codecs@1.0.1(@types/debug@4.1.12)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2)': - dependencies: - '@types/node': 24.10.10 - bson: 6.10.4 - ts-codec: 1.3.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.10)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2) - transitivePeerDependencies: - - '@edge-runtime/vm' - - '@types/debug' - - '@vitest/browser' - - '@vitest/ui' - - happy-dom - - jiti - - jsdom - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - '@journeyapps-labs/micro-codecs@1.0.1(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)': + '@journeyapps-labs/micro-codecs@1.0.2': dependencies: '@types/node': 24.10.10 bson: 6.10.4 ts-codec: 1.3.0 - vitest: 3.2.4(@types/node@24.10.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - transitivePeerDependencies: - - '@edge-runtime/vm' - - '@types/debug' - - '@vitest/browser' - - '@vitest/ui' - - happy-dom - - jiti - - jsdom - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - '@journeyapps-labs/micro-codecs@1.0.2': + '@journeyapps-labs/micro-codecs@1.0.3': dependencies: '@types/node': 24.10.10 bson: 6.10.4 @@ -8561,7 +8477,7 @@ snapshots: '@journeyapps-labs/micro-schema@1.0.1(@types/debug@4.1.12)(@types/json-schema@7.0.15)(@types/node@24.10.10)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2)': dependencies: '@apidevtools/json-schema-ref-parser': 14.2.1(@types/json-schema@7.0.15) - '@journeyapps-labs/micro-codecs': 1.0.1(@types/debug@4.1.12)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2) + '@journeyapps-labs/micro-codecs': 1.0.2 '@journeyapps-labs/micro-errors': 1.0.1 ajv: 8.17.1 better-ajv-errors: 2.0.3(ajv@8.17.1) @@ -8590,69 +8506,18 @@ snapshots: - tsx - yaml - '@journeyapps-labs/micro-schema@1.0.1(@types/debug@4.1.12)(@types/json-schema@7.0.15)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2)': - dependencies: - '@apidevtools/json-schema-ref-parser': 14.2.1(@types/json-schema@7.0.15) - '@journeyapps-labs/micro-codecs': 1.0.1(@types/debug@4.1.12)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2) - '@journeyapps-labs/micro-errors': 1.0.1 - ajv: 8.17.1 - better-ajv-errors: 2.0.3(ajv@8.17.1) - ts-codec: 1.3.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2) - zod: 4.3.6 - transitivePeerDependencies: - - '@edge-runtime/vm' - - '@types/debug' - - '@types/json-schema' - - '@types/node' - - '@vitest/browser' - - '@vitest/ui' - - happy-dom - - jiti - - jsdom - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - '@journeyapps-labs/micro-schema@1.0.1(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)': + '@journeyapps-labs/micro-schema@1.0.3(@types/json-schema@7.0.15)': dependencies: '@apidevtools/json-schema-ref-parser': 14.2.1(@types/json-schema@7.0.15) - '@journeyapps-labs/micro-codecs': 1.0.1(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) + '@journeyapps-labs/micro-codecs': 1.0.3 '@journeyapps-labs/micro-errors': 1.0.1 ajv: 8.17.1 better-ajv-errors: 2.0.3(ajv@8.17.1) + bson: 6.10.4 ts-codec: 1.3.0 - vitest: 3.2.4(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) zod: 4.3.6 transitivePeerDependencies: - - '@edge-runtime/vm' - - '@types/debug' - '@types/json-schema' - - '@types/node' - - '@vitest/browser' - - '@vitest/ui' - - happy-dom - - jiti - - jsdom - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml '@journeyapps-labs/micro-streaming@1.0.1(@types/debug@4.1.12)(@types/json-schema@7.0.15)(@types/node@24.10.10)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2)': dependencies: @@ -8682,61 +8547,14 @@ snapshots: - tsx - yaml - '@journeyapps-labs/micro-streaming@1.0.1(@types/debug@4.1.12)(@types/json-schema@7.0.15)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2)': - dependencies: - '@journeyapps-labs/micro-errors': 1.0.1 - '@journeyapps-labs/micro-schema': 1.0.1(@types/debug@4.1.12)(@types/json-schema@7.0.15)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2) - '@types/express': 5.0.6 - bson: 6.10.4 - transitivePeerDependencies: - - '@edge-runtime/vm' - - '@types/debug' - - '@types/json-schema' - - '@types/node' - - '@vitest/browser' - - '@vitest/ui' - - happy-dom - - jiti - - jsdom - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - '@journeyapps-labs/micro-streaming@1.0.1(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)': + '@journeyapps-labs/micro-streaming@1.0.2(@types/json-schema@7.0.15)': dependencies: '@journeyapps-labs/micro-errors': 1.0.1 - '@journeyapps-labs/micro-schema': 1.0.1(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) + '@journeyapps-labs/micro-schema': 1.0.3(@types/json-schema@7.0.15) '@types/express': 5.0.6 bson: 6.10.4 transitivePeerDependencies: - - '@edge-runtime/vm' - - '@types/debug' - '@types/json-schema' - - '@types/node' - - '@vitest/browser' - - '@vitest/ui' - - happy-dom - - jiti - - jsdom - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -8960,62 +8778,18 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@powersync/management-client@0.0.6(@types/debug@4.1.12)(@types/json-schema@7.0.15)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2)': + '@powersync/management-client@0.1.0(@types/json-schema@7.0.15)': dependencies: - '@journeyapps-labs/common-sdk': 1.0.3(@types/debug@4.1.12)(@types/json-schema@7.0.15)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2) - '@powersync/management-types': 0.0.5 + '@journeyapps-labs/common-sdk': 1.0.4(@types/json-schema@7.0.15) + '@powersync/management-types': 0.1.0 transitivePeerDependencies: - - '@edge-runtime/vm' - - '@types/debug' - '@types/json-schema' - - '@vitest/browser' - - '@vitest/ui' - babel-plugin-macros - - happy-dom - - jiti - - jsdom - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - '@powersync/management-client@0.0.6(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)': - dependencies: - '@journeyapps-labs/common-sdk': 1.0.3(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - '@powersync/management-types': 0.0.5 - transitivePeerDependencies: - - '@edge-runtime/vm' - - '@types/debug' - - '@types/json-schema' - - '@vitest/browser' - - '@vitest/ui' - - babel-plugin-macros - - happy-dom - - jiti - - jsdom - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - '@powersync/management-types@0.0.5': + '@powersync/management-types@0.1.0': dependencies: - '@journeyapps-labs/micro-codecs': 1.0.2 - '@powersync/service-types': 0.15.0 + '@journeyapps-labs/micro-codecs': 1.0.3 + '@powersync/service-types': 0.15.2 bson: 6.10.4 ts-codec: 1.3.0 transitivePeerDependencies: @@ -9070,6 +8844,14 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros + '@powersync/service-types@0.15.2': + dependencies: + dedent: 1.7.1 + ts-codec: 1.3.0 + uri-js: 4.4.1 + transitivePeerDependencies: + - babel-plugin-macros + '@powersync/sync-config-tools@0.1.1': {} '@radix-ui/primitive@1.1.3': {} @@ -10473,7 +10255,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 24.10.10 + '@types/node': 22.19.7 '@types/chai@5.2.3': dependencies: @@ -10482,7 +10264,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 24.10.10 + '@types/node': 22.19.7 '@types/debug@4.1.12': dependencies: @@ -10495,7 +10277,7 @@ snapshots: '@types/express-serve-static-core@5.1.1': dependencies: - '@types/node': 24.10.10 + '@types/node': 22.19.7 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -10523,7 +10305,7 @@ snapshots: '@types/mute-stream@0.0.4': dependencies: - '@types/node': 24.10.10 + '@types/node': 22.19.7 '@types/node@12.20.55': {} @@ -10564,12 +10346,12 @@ snapshots: '@types/send@1.2.1': dependencies: - '@types/node': 24.10.10 + '@types/node': 22.19.7 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 24.10.10 + '@types/node': 22.19.7 '@types/wrap-ansi@3.0.0': {} @@ -10769,14 +10551,6 @@ snapshots: optionalDependencies: vite: 7.3.1(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))': - dependencies: - '@vitest/spy': 3.2.4 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 @@ -10785,14 +10559,6 @@ snapshots: optionalDependencies: vite: 7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': - dependencies: - '@vitest/spy': 3.2.4 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 @@ -14178,27 +13944,6 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - cac: 6.7.14 - debug: 4.4.3(supports-color@8.1.1) - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - vite-node@3.2.4(@types/node@24.10.10)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 @@ -14220,48 +13965,6 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - cac: 6.7.14 - debug: 4.4.3(supports-color@8.1.1) - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vite-node@3.2.4(@types/node@25.5.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - cac: 6.7.14 - debug: 4.4.3(supports-color@8.1.1) - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: debug: 4.4.3(supports-color@8.1.1) @@ -14305,22 +14008,6 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 - vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - esbuild: 0.27.2 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.57.1 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 25.5.0 - fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.31.1 - tsx: 4.21.0 - yaml: 2.8.2 - vitefu@1.1.1(vite@7.3.1(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)): optionalDependencies: vite: 7.3.1(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) @@ -14411,133 +14098,6 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - '@types/chai': 5.2.3 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - debug: 4.4.3(supports-color@8.1.1) - expect-type: 1.3.0 - magic-string: 0.30.21 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@25.5.0)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/debug': 4.1.12 - '@types/node': 25.5.0 - jsdom: 27.4.0 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vitest@3.2.4(@types/node@24.10.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - '@types/chai': 5.2.3 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - debug: 4.4.3(supports-color@8.1.1) - expect-type: 1.3.0 - magic-string: 0.30.21 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 24.10.10 - jsdom: 27.4.0 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vitest@3.2.4(@types/node@25.5.0)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - '@types/chai': 5.2.3 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - debug: 4.4.3(supports-color@8.1.1) - expect-type: 1.3.0 - magic-string: 0.30.21 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 25.5.0 - jsdom: 27.4.0 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 739ac31..58995e0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,8 +4,8 @@ packages: - plugins/* catalog: - '@powersync/management-client': ^0.0.6 - '@powersync/management-types': ^0.0.5 + '@powersync/management-client': ^0.1.0 + '@powersync/management-types': ^0.1.0 '@powersync/service-client': ^0.0.3 '@powersync/service-schema': ^1.20.2 '@powersync/service-sync-rules': ^0.34.0