primitive-admin 1.2.0-alpha.0 → 1.2.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -136
- package/assets/skill/skills/primitive-platform/SKILL.md +37 -4
- package/dist/bin/primitive.js +30 -35
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/agent-sessions.d.ts +133 -0
- package/dist/src/commands/agent-sessions.js +792 -0
- package/dist/src/commands/agent-sessions.js.map +1 -0
- package/dist/src/commands/analytics.js +0 -148
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/apps-children.d.ts +54 -0
- package/dist/src/commands/apps-children.js +631 -0
- package/dist/src/commands/apps-children.js.map +1 -0
- package/dist/src/commands/apps.d.ts +6 -0
- package/dist/src/commands/apps.js +131 -9
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/commands/auth-sessions.d.ts +3 -3
- package/dist/src/commands/auth-sessions.js +117 -23
- package/dist/src/commands/auth-sessions.js.map +1 -1
- package/dist/src/commands/auth.js +460 -36
- package/dist/src/commands/auth.js.map +1 -1
- package/dist/src/commands/collections.js +316 -256
- package/dist/src/commands/collections.js.map +1 -1
- package/dist/src/commands/config.d.ts +1 -1
- package/dist/src/commands/config.js +60 -28
- package/dist/src/commands/config.js.map +1 -1
- package/dist/src/commands/connections.js +12 -17
- package/dist/src/commands/connections.js.map +1 -1
- package/dist/src/commands/database-type-configs.js +2 -76
- package/dist/src/commands/database-type-configs.js.map +1 -1
- package/dist/src/commands/databases.d.ts +9 -0
- package/dist/src/commands/databases.js +228 -566
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/db3-types.d.ts +15 -0
- package/dist/src/commands/db3-types.js +157 -0
- package/dist/src/commands/db3-types.js.map +1 -0
- package/dist/src/commands/db3.d.ts +12 -0
- package/dist/src/commands/db3.js +1052 -0
- package/dist/src/commands/db3.js.map +1 -0
- package/dist/src/commands/documents.d.ts +121 -2
- package/dist/src/commands/documents.js +1601 -1251
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/env.d.ts +9 -1
- package/dist/src/commands/env.js +164 -49
- package/dist/src/commands/env.js.map +1 -1
- package/dist/src/commands/function-trigger-commands.d.ts +28 -0
- package/dist/src/commands/function-trigger-commands.js +341 -0
- package/dist/src/commands/function-trigger-commands.js.map +1 -0
- package/dist/src/commands/functions.js +297 -24
- package/dist/src/commands/functions.js.map +1 -1
- package/dist/src/commands/groups.js +150 -49
- package/dist/src/commands/groups.js.map +1 -1
- package/dist/src/commands/guides.js +5 -5
- package/dist/src/commands/init.d.ts +7 -0
- package/dist/src/commands/init.js +23 -5
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/integrations.js +14 -8
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/metadata-category-configs.d.ts +2 -2
- package/dist/src/commands/metadata-category-configs.js +2 -2
- package/dist/src/commands/prompts.d.ts +7 -0
- package/dist/src/commands/prompts.js +48 -5
- package/dist/src/commands/prompts.js.map +1 -1
- package/dist/src/commands/scripts.d.ts +9 -17
- package/dist/src/commands/scripts.js +24 -479
- package/dist/src/commands/scripts.js.map +1 -1
- package/dist/src/commands/sessions.js +3 -8
- package/dist/src/commands/sessions.js.map +1 -1
- package/dist/src/commands/sync-app-settings.d.ts +36 -8
- package/dist/src/commands/sync-app-settings.js +109 -23
- package/dist/src/commands/sync-app-settings.js.map +1 -1
- package/dist/src/commands/sync.d.ts +436 -613
- package/dist/src/commands/sync.js +2604 -5977
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/tokens.js +4 -9
- package/dist/src/commands/tokens.js.map +1 -1
- package/dist/src/commands/users.js +77 -2
- package/dist/src/commands/users.js.map +1 -1
- package/dist/src/commands/workflows.d.ts +5 -83
- package/dist/src/commands/workflows.js +57 -2282
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/aggregate-output.d.ts +25 -0
- package/dist/src/lib/aggregate-output.js +53 -0
- package/dist/src/lib/aggregate-output.js.map +1 -0
- package/dist/src/lib/api-client.d.ts +681 -581
- package/dist/src/lib/api-client.js +880 -851
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/app-settings-descriptor.d.ts +47 -1
- package/dist/src/lib/app-settings-descriptor.js +114 -10
- package/dist/src/lib/app-settings-descriptor.js.map +1 -1
- package/dist/src/lib/auth-fixtures.d.ts +44 -0
- package/dist/src/lib/auth-fixtures.js +61 -0
- package/dist/src/lib/auth-fixtures.js.map +1 -0
- package/dist/src/lib/auth-flow.d.ts +41 -1
- package/dist/src/lib/auth-flow.js +70 -16
- package/dist/src/lib/auth-flow.js.map +1 -1
- package/dist/src/lib/block-layout.d.ts +12 -23
- package/dist/src/lib/block-layout.js +26 -92
- package/dist/src/lib/block-layout.js.map +1 -1
- package/dist/src/lib/block-selector.d.ts +2 -2
- package/dist/src/lib/block-selector.js +2 -2
- package/dist/src/lib/child-apps-local.d.ts +164 -0
- package/dist/src/lib/child-apps-local.js +258 -0
- package/dist/src/lib/child-apps-local.js.map +1 -0
- package/dist/src/lib/ci-session-request.d.ts +22 -0
- package/dist/src/lib/ci-session-request.js +51 -0
- package/dist/src/lib/ci-session-request.js.map +1 -0
- package/dist/src/lib/codegen-shared/generatedFiles.d.ts +10 -8
- package/dist/src/lib/codegen-shared/generatedFiles.js +21 -15
- package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -1
- package/dist/src/lib/codegen-shared/prettierStable.d.ts +1 -1
- package/dist/src/lib/codegen-shared/prettierStable.js +1 -1
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +2 -2
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +2 -2
- package/dist/src/lib/collection-export.d.ts +76 -15
- package/dist/src/lib/collection-export.js +116 -23
- package/dist/src/lib/collection-export.js.map +1 -1
- package/dist/src/lib/collection-principal.d.ts +22 -0
- package/dist/src/lib/collection-principal.js +35 -0
- package/dist/src/lib/collection-principal.js.map +1 -0
- package/dist/src/lib/config-object-descriptor.d.ts +3 -22
- package/dist/src/lib/config-object-descriptor.js +56 -291
- package/dist/src/lib/config-object-descriptor.js.map +1 -1
- package/dist/src/lib/config-payload.js +2 -3
- package/dist/src/lib/config-payload.js.map +1 -1
- package/dist/src/lib/config-surface.d.ts +5 -7
- package/dist/src/lib/config-surface.js +18 -40
- package/dist/src/lib/config-surface.js.map +1 -1
- package/dist/src/lib/connections-list.d.ts +10 -0
- package/dist/src/lib/connections-list.js +18 -0
- package/dist/src/lib/connections-list.js.map +1 -0
- package/dist/src/lib/credentials-home.d.ts +58 -0
- package/dist/src/lib/credentials-home.js +204 -0
- package/dist/src/lib/credentials-home.js.map +1 -0
- package/dist/src/lib/credentials-lock.d.ts +88 -0
- package/dist/src/lib/credentials-lock.js +302 -0
- package/dist/src/lib/credentials-lock.js.map +1 -0
- package/dist/src/lib/credentials-store.d.ts +179 -24
- package/dist/src/lib/credentials-store.js +712 -77
- package/dist/src/lib/credentials-store.js.map +1 -1
- package/dist/src/lib/csv.d.ts +8 -9
- package/dist/src/lib/csv.js +3 -4
- package/dist/src/lib/csv.js.map +1 -1
- package/dist/src/lib/custodian.d.ts +8 -0
- package/dist/src/lib/custodian.js +18 -0
- package/dist/src/lib/custodian.js.map +1 -0
- package/dist/src/lib/db-codegen/dbGenerator.js +5 -16
- package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -1
- package/dist/src/lib/db-codegen/dbNaming.d.ts +5 -13
- package/dist/src/lib/db-codegen/dbNaming.js +5 -17
- package/dist/src/lib/db-codegen/dbNaming.js.map +1 -1
- package/dist/src/lib/db-codegen/dbTemplates.d.ts +7 -56
- package/dist/src/lib/db-codegen/dbTemplates.js +25 -125
- package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -1
- package/dist/src/lib/db-codegen/dbTsTypes.d.ts +2 -2
- package/dist/src/lib/db-codegen/dbTsTypes.js +2 -2
- package/dist/src/lib/db-codegen/dbTypeIR.d.ts +67 -2
- package/dist/src/lib/db-codegen/dbTypeIR.js +13 -2
- package/dist/src/lib/db-codegen/dbTypeIR.js.map +1 -1
- package/dist/src/lib/db3-export.d.ts +231 -0
- package/dist/src/lib/db3-export.js +510 -0
- package/dist/src/lib/db3-export.js.map +1 -0
- package/dist/src/lib/db3-index-output.d.ts +27 -0
- package/dist/src/lib/db3-index-output.js +49 -0
- package/dist/src/lib/db3-index-output.js.map +1 -0
- package/dist/src/lib/document-export-lookups.d.ts +31 -0
- package/dist/src/lib/document-export-lookups.js +53 -0
- package/dist/src/lib/document-export-lookups.js.map +1 -0
- package/dist/src/lib/document-export-permissions.d.ts +7 -5
- package/dist/src/lib/document-export-permissions.js +7 -5
- package/dist/src/lib/document-export-permissions.js.map +1 -1
- package/dist/src/lib/document-export-record-log.d.ts +57 -0
- package/dist/src/lib/document-export-record-log.js +215 -0
- package/dist/src/lib/document-export-record-log.js.map +1 -0
- package/dist/src/lib/document-ingest-artifact.d.ts +26 -2
- package/dist/src/lib/document-ingest-artifact.js +42 -8
- package/dist/src/lib/document-ingest-artifact.js.map +1 -1
- package/dist/src/lib/document-ingest-format2.d.ts +36 -0
- package/dist/src/lib/document-ingest-format2.js +78 -0
- package/dist/src/lib/document-ingest-format2.js.map +1 -0
- package/dist/src/lib/document-ingest-input.d.ts +9 -0
- package/dist/src/lib/document-ingest-input.js +28 -1
- package/dist/src/lib/document-ingest-input.js.map +1 -1
- package/dist/src/lib/document-ingest-rows.d.ts +16 -0
- package/dist/src/lib/document-ingest-rows.js +17 -0
- package/dist/src/lib/document-ingest-rows.js.map +1 -1
- package/dist/src/lib/document-ingest.d.ts +33 -1
- package/dist/src/lib/document-ingest.js +49 -0
- package/dist/src/lib/document-ingest.js.map +1 -1
- package/dist/src/lib/document-schema-rows.d.ts +42 -0
- package/dist/src/lib/document-schema-rows.js +53 -0
- package/dist/src/lib/document-schema-rows.js.map +1 -0
- package/dist/src/lib/env-resolver-core.d.ts +70 -2
- package/dist/src/lib/env-resolver-core.js +137 -20
- package/dist/src/lib/env-resolver-core.js.map +1 -1
- package/dist/src/lib/env-resolver.d.ts +14 -2
- package/dist/src/lib/env-resolver.js +32 -10
- package/dist/src/lib/env-resolver.js.map +1 -1
- package/dist/src/lib/function-authorize-check.d.ts +142 -0
- package/dist/src/lib/function-authorize-check.js +310 -0
- package/dist/src/lib/function-authorize-check.js.map +1 -0
- package/dist/src/lib/function-authorize-view.d.ts +15 -0
- package/dist/src/lib/function-authorize-view.js +50 -0
- package/dist/src/lib/function-authorize-view.js.map +1 -0
- package/dist/src/lib/function-bundle.d.ts +38 -18
- package/dist/src/lib/function-bundle.js +96 -35
- package/dist/src/lib/function-bundle.js.map +1 -1
- package/dist/src/lib/function-collect.d.ts +75 -12
- package/dist/src/lib/function-collect.js +427 -12
- package/dist/src/lib/function-collect.js.map +1 -1
- package/dist/src/lib/function-db-types.d.ts +29 -0
- package/dist/src/lib/function-db-types.js +117 -7
- package/dist/src/lib/function-db-types.js.map +1 -1
- package/dist/src/lib/function-document-types.d.ts +5 -3
- package/dist/src/lib/function-document-types.js +10 -14
- package/dist/src/lib/function-document-types.js.map +1 -1
- package/dist/src/lib/function-lint.d.ts +34 -0
- package/dist/src/lib/function-lint.js +112 -0
- package/dist/src/lib/function-lint.js.map +1 -0
- package/dist/src/lib/function-log-row.js +21 -2
- package/dist/src/lib/function-log-row.js.map +1 -1
- package/dist/src/lib/function-run.d.ts +16 -21
- package/dist/src/lib/function-run.js +37 -30
- package/dist/src/lib/function-run.js.map +1 -1
- package/dist/src/lib/function-schema-codegen.d.ts +1 -1
- package/dist/src/lib/function-schema-codegen.js +5 -5
- package/dist/src/lib/function-schema-codegen.js.map +1 -1
- package/dist/src/lib/function-source-roots.d.ts +67 -0
- package/dist/src/lib/function-source-roots.js +148 -0
- package/dist/src/lib/function-source-roots.js.map +1 -0
- package/dist/src/lib/function-sync.d.ts +108 -22
- package/dist/src/lib/function-sync.js +127 -39
- package/dist/src/lib/function-sync.js.map +1 -1
- package/dist/src/lib/function-trigger-listing.d.ts +11 -6
- package/dist/src/lib/function-trigger-listing.js +32 -11
- package/dist/src/lib/function-trigger-listing.js.map +1 -1
- package/dist/src/lib/function-typecheck.d.ts +37 -2
- package/dist/src/lib/function-typecheck.js +210 -28
- package/dist/src/lib/function-typecheck.js.map +1 -1
- package/dist/src/lib/function-versions.d.ts +2 -0
- package/dist/src/lib/function-versions.js +1 -0
- package/dist/src/lib/function-versions.js.map +1 -1
- package/dist/src/lib/generated-allowlist.js +0 -20
- package/dist/src/lib/generated-allowlist.js.map +1 -1
- package/dist/src/lib/generated-config-surfaces.d.ts +703 -201
- package/dist/src/lib/generated-config-surfaces.js +2335 -2033
- package/dist/src/lib/generated-config-surfaces.js.map +1 -1
- package/dist/src/lib/generated-sdk-types.d.ts +1 -1
- package/dist/src/lib/generated-sdk-types.js +1 -1
- package/dist/src/lib/generated-sdk-types.js.map +1 -1
- package/dist/src/lib/init-adopt.js +12 -3
- package/dist/src/lib/init-adopt.js.map +1 -1
- package/dist/src/lib/init-login-note.d.ts +13 -0
- package/dist/src/lib/init-login-note.js +22 -0
- package/dist/src/lib/init-login-note.js.map +1 -0
- package/dist/src/lib/json-value.d.ts +6 -0
- package/dist/src/lib/json-value.js +13 -0
- package/dist/src/lib/json-value.js.map +1 -0
- package/dist/src/lib/load-routes.d.ts +33 -0
- package/dist/src/lib/load-routes.js +35 -0
- package/dist/src/lib/load-routes.js.map +1 -0
- package/dist/src/lib/local-state.d.ts +38 -7
- package/dist/src/lib/local-state.js +172 -49
- package/dist/src/lib/local-state.js.map +1 -1
- package/dist/src/lib/local-test-cases.d.ts +5 -2
- package/dist/src/lib/local-test-cases.js +7 -4
- package/dist/src/lib/local-test-cases.js.map +1 -1
- package/dist/src/lib/log-inspection.d.ts +42 -4
- package/dist/src/lib/log-inspection.js +37 -5
- package/dist/src/lib/log-inspection.js.map +1 -1
- package/dist/src/lib/login-exchange-check.d.ts +18 -0
- package/dist/src/lib/login-exchange-check.js +27 -0
- package/dist/src/lib/login-exchange-check.js.map +1 -0
- package/dist/src/lib/login-scope-lines.d.ts +25 -0
- package/dist/src/lib/login-scope-lines.js +45 -0
- package/dist/src/lib/login-scope-lines.js.map +1 -0
- package/dist/src/lib/logout-admin-session.d.ts +2 -1
- package/dist/src/lib/logout-admin-session.js +7 -1
- package/dist/src/lib/logout-admin-session.js.map +1 -1
- package/dist/src/lib/object-status-filter.d.ts +9 -1
- package/dist/src/lib/object-status-filter.js +9 -1
- package/dist/src/lib/object-status-filter.js.map +1 -1
- package/dist/src/lib/output.js +2 -0
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/paginate.d.ts +0 -5
- package/dist/src/lib/paginate.js +0 -5
- package/dist/src/lib/paginate.js.map +1 -1
- package/dist/src/lib/pin-refusal-hint.d.ts +12 -0
- package/dist/src/lib/pin-refusal-hint.js +35 -0
- package/dist/src/lib/pin-refusal-hint.js.map +1 -0
- package/dist/src/lib/primitive-env-plugin.d.ts +99 -0
- package/dist/src/lib/primitive-env-plugin.js +408 -0
- package/dist/src/lib/primitive-env-plugin.js.map +1 -0
- package/dist/src/lib/project-config.d.ts +7 -0
- package/dist/src/lib/project-config.js +22 -1
- package/dist/src/lib/project-config.js.map +1 -1
- package/dist/src/lib/project-login-apps.d.ts +8 -0
- package/dist/src/lib/project-login-apps.js +27 -0
- package/dist/src/lib/project-login-apps.js.map +1 -0
- package/dist/src/lib/prompt-schema-codegen.d.ts +10 -3
- package/dist/src/lib/prompt-schema-codegen.js +52 -11
- package/dist/src/lib/prompt-schema-codegen.js.map +1 -1
- package/dist/src/lib/pull-write.d.ts +31 -0
- package/dist/src/lib/pull-write.js +49 -0
- package/dist/src/lib/pull-write.js.map +1 -0
- package/dist/src/lib/push-failure-report.d.ts +45 -0
- package/dist/src/lib/push-failure-report.js +57 -0
- package/dist/src/lib/push-failure-report.js.map +1 -0
- package/dist/src/lib/records-verbs.d.ts +69 -0
- package/dist/src/lib/records-verbs.js +40 -0
- package/dist/src/lib/records-verbs.js.map +1 -0
- package/dist/src/lib/refresh-admin-credentials.d.ts +7 -0
- package/dist/src/lib/refresh-admin-credentials.js +16 -4
- package/dist/src/lib/refresh-admin-credentials.js.map +1 -1
- package/dist/src/lib/root-import-decision.d.ts +65 -0
- package/dist/src/lib/root-import-decision.js +109 -0
- package/dist/src/lib/root-import-decision.js.map +1 -0
- package/dist/src/lib/run-status.d.ts +1 -1
- package/dist/src/lib/run-status.generated.d.ts +2 -2
- package/dist/src/lib/run-status.generated.js +3 -1
- package/dist/src/lib/run-status.generated.js.map +1 -1
- package/dist/src/lib/run-status.js +1 -1
- package/dist/src/lib/schema-codegen/generated-schema-descriptor.d.ts +129 -0
- package/dist/src/lib/schema-codegen/generated-schema-descriptor.js +269 -0
- package/dist/src/lib/schema-codegen/generated-schema-descriptor.js.map +1 -0
- package/dist/src/lib/schema-codegen/naming.d.ts +35 -0
- package/dist/src/lib/schema-codegen/naming.js +83 -0
- package/dist/src/lib/schema-codegen/naming.js.map +1 -0
- package/dist/src/lib/schema-codegen/schemaToTs.d.ts +82 -0
- package/dist/src/lib/schema-codegen/schemaToTs.js +305 -0
- package/dist/src/lib/schema-codegen/schemaToTs.js.map +1 -0
- package/dist/src/lib/scope-request.d.ts +84 -0
- package/dist/src/lib/scope-request.js +235 -0
- package/dist/src/lib/scope-request.js.map +1 -0
- package/dist/src/lib/session-id.d.ts +9 -0
- package/dist/src/lib/session-id.js +28 -0
- package/dist/src/lib/session-id.js.map +1 -0
- package/dist/src/lib/snapshot-build-rows.d.ts +21 -0
- package/dist/src/lib/snapshot-build-rows.js +22 -0
- package/dist/src/lib/snapshot-build-rows.js.map +1 -1
- package/dist/src/lib/snapshot-build.js +8 -3
- package/dist/src/lib/snapshot-build.js.map +1 -1
- package/dist/src/lib/snapshots.d.ts +17 -1
- package/dist/src/lib/snapshots.js +50 -2
- package/dist/src/lib/snapshots.js.map +1 -1
- package/dist/src/lib/swift-codegen/agentGenerator.d.ts +6 -2
- package/dist/src/lib/swift-codegen/agentGenerator.js +57 -5
- package/dist/src/lib/swift-codegen/agentGenerator.js.map +1 -1
- package/dist/src/lib/swift-codegen/banners.d.ts +34 -7
- package/dist/src/lib/swift-codegen/banners.js +37 -7
- package/dist/src/lib/swift-codegen/banners.js.map +1 -1
- package/dist/src/lib/swift-codegen/dbGenerator.d.ts +4 -3
- package/dist/src/lib/swift-codegen/dbGenerator.js +14 -117
- package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
- package/dist/src/lib/swift-codegen/functionGenerator.d.ts +3 -3
- package/dist/src/lib/swift-codegen/functionGenerator.js +23 -18
- package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -1
- package/dist/src/lib/swift-codegen/generator.d.ts +5 -5
- package/dist/src/lib/swift-codegen/generator.js +35 -40
- package/dist/src/lib/swift-codegen/generator.js.map +1 -1
- package/dist/src/lib/swift-codegen/schemaToSwift.d.ts +3 -3
- package/dist/src/lib/swift-codegen/schemaToSwift.js +4 -4
- package/dist/src/lib/swift-codegen/schemaToSwift.js.map +1 -1
- package/dist/src/lib/swift-codegen/swiftKeys.d.ts +19 -0
- package/dist/src/lib/swift-codegen/swiftKeys.js +31 -0
- package/dist/src/lib/swift-codegen/swiftKeys.js.map +1 -0
- package/dist/src/lib/sync-paths.d.ts +35 -0
- package/dist/src/lib/sync-paths.js +57 -5
- package/dist/src/lib/sync-paths.js.map +1 -1
- package/dist/src/lib/sync-resource-types.d.ts +26 -26
- package/dist/src/lib/sync-resource-types.js +61 -189
- package/dist/src/lib/sync-resource-types.js.map +1 -1
- package/dist/src/lib/sync-selectors.d.ts +5 -1
- package/dist/src/lib/sync-selectors.js +23 -13
- package/dist/src/lib/sync-selectors.js.map +1 -1
- package/dist/src/lib/test-case-file-names.d.ts +3 -5
- package/dist/src/lib/test-case-file-names.js +3 -5
- package/dist/src/lib/test-case-file-names.js.map +1 -1
- package/dist/src/lib/toml-database-config.d.ts +15 -75
- package/dist/src/lib/toml-database-config.js +17 -306
- package/dist/src/lib/toml-database-config.js.map +1 -1
- package/dist/src/lib/transient-retry.d.ts +43 -0
- package/dist/src/lib/transient-retry.js +94 -0
- package/dist/src/lib/transient-retry.js.map +1 -0
- package/dist/src/lib/vars-override.d.ts +105 -0
- package/dist/src/lib/vars-override.js +143 -0
- package/dist/src/lib/vars-override.js.map +1 -0
- package/dist/src/lib/webhook-capture.d.ts +43 -0
- package/dist/src/lib/webhook-capture.js +91 -0
- package/dist/src/lib/webhook-capture.js.map +1 -0
- package/dist/src/lib/webhook-deliver.d.ts +18 -20
- package/dist/src/lib/webhook-deliver.js +34 -89
- package/dist/src/lib/webhook-deliver.js.map +1 -1
- package/dist/src/lib/webhook-test-run.d.ts +32 -0
- package/dist/src/lib/webhook-test-run.js +108 -0
- package/dist/src/lib/webhook-test-run.js.map +1 -0
- package/dist/src/lib/workflow-codegen/generator.d.ts +7 -7
- package/dist/src/lib/workflow-codegen/generator.js +16 -23
- package/dist/src/lib/workflow-codegen/generator.js.map +1 -1
- package/dist/src/lib/workflow-codegen/invokerIR.d.ts +6 -12
- package/dist/src/lib/workflow-codegen/invokerIR.js +4 -5
- package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -1
- package/dist/src/lib/workflow-toml-validator.d.ts +2 -0
- package/dist/src/lib/workflow-toml-validator.js +28 -1
- package/dist/src/lib/workflow-toml-validator.js.map +1 -1
- package/dist/src/types/index.d.ts +30 -37
- package/dist/src/validators.d.ts +7 -55
- package/dist/src/validators.js +7 -53
- package/dist/src/validators.js.map +1 -1
- package/dist/src/vite.d.ts +17 -0
- package/dist/src/vite.js +18 -0
- package/dist/src/vite.js.map +1 -0
- package/package.json +20 -12
|
@@ -2,17 +2,12 @@ import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, rea
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { ApiClient } from "../lib/api-client.js";
|
|
4
4
|
import { getCurrentAppId, resolveAppId } from "../lib/config.js";
|
|
5
|
-
import { resolveCodegenSourceDir } from "../lib/codegen-shared/resolveCodegenSourceDir.js";
|
|
6
5
|
import { resolveSyncDir } from "../lib/sync-paths.js";
|
|
7
6
|
import { parseCsv, applyColumnMap, buildRows } from "../lib/csv.js";
|
|
8
7
|
import { chunk, parseBatchSize } from "../lib/batch.js";
|
|
9
|
-
import { generateDbTypes, } from "../lib/db-codegen/dbGenerator.js";
|
|
10
|
-
import { generateDbSwiftTypes } from "../lib/swift-codegen/dbGenerator.js";
|
|
11
8
|
import { success, error, info, warn, keyValue, result as printResult, formatTable, formatId, formatDate, json, } from "../lib/output.js";
|
|
9
|
+
import { renderAggregateResult } from "../lib/aggregate-output.js";
|
|
12
10
|
import { confirmPrompt } from "../lib/confirm-prompt.js";
|
|
13
|
-
import { deprecateCommandGroup, warnDeprecated, } from "../lib/deprecation.js";
|
|
14
|
-
/** Successor for the deprecated database CEL context commands and flags. */
|
|
15
|
-
const CEL_CONTEXT_SUCCESSOR = "resource metadata categories (`primitive metadata get|set database <database-id> <category>`; define categories with `primitive config push`)";
|
|
16
11
|
import { parseFilterOptions } from "../lib/record-filter.js";
|
|
17
12
|
import { parseDataOption } from "../lib/data-input.js";
|
|
18
13
|
import { ulid } from "ulid";
|
|
@@ -55,49 +50,6 @@ function assertDataIdMatches(data, recordId, writtenId, commandPath) {
|
|
|
55
50
|
: `Pass the record id as the argument, not inside --data: ${commandPath} ${dataId} --data ...`);
|
|
56
51
|
process.exit(1);
|
|
57
52
|
}
|
|
58
|
-
/**
|
|
59
|
-
* Validate that the named operation is an import-compatible, save-like
|
|
60
|
-
* mutation before importing (issue #145, Codex finding (a)).
|
|
61
|
-
*
|
|
62
|
-
* The importer always builds `params: { modelName, id, data }`, and the batch
|
|
63
|
-
* endpoint rejects undeclared params per item. So we require the op to be a
|
|
64
|
-
* `mutation` whose declared `params` cover `modelName`, `id`, and `data`. This
|
|
65
|
-
* surfaces a clear up-front error instead of letting every row fail
|
|
66
|
-
* server-side. Exits non-zero on any mismatch.
|
|
67
|
-
*/
|
|
68
|
-
async function validateImportOperation(client, appId, databaseId, operationName) {
|
|
69
|
-
let ops;
|
|
70
|
-
try {
|
|
71
|
-
ops = await client.listDatabaseOperations(appId, databaseId);
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
error(`Could not list operations for this database: ${err?.message || err}`);
|
|
75
|
-
process.exit(1);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
const op = (ops || []).find((o) => o?.name === operationName);
|
|
79
|
-
if (!op) {
|
|
80
|
-
error(`Operation "${operationName}" not found — create a mutation op (e.g. ` +
|
|
81
|
-
`\`seed_save\`) on this database type first.`);
|
|
82
|
-
process.exit(1);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
if (op.type !== "mutation") {
|
|
86
|
-
error(`Operation "${operationName}" is a "${op.type}" op, not an import-compatible ` +
|
|
87
|
-
`mutation. CSV import needs a save-like mutation taking {modelName, id, data}.`);
|
|
88
|
-
process.exit(1);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const params = op.params || {};
|
|
92
|
-
const missing = ["modelName", "id", "data"].filter((p) => !(p in params));
|
|
93
|
-
if (missing.length > 0) {
|
|
94
|
-
error(`Operation "${operationName}" is not import-compatible: its params are missing ` +
|
|
95
|
-
`${missing.join(", ")}. CSV import builds {modelName, id, data} per item, so the ` +
|
|
96
|
-
`op must declare those params (like \`seed_save\`).`);
|
|
97
|
-
process.exit(1);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
53
|
/**
|
|
102
54
|
* The full `databases records bulk` precondition value space, audited in one
|
|
103
55
|
* pass (issue #2437, Codex cycle 3). A precondition is field-equality only, so
|
|
@@ -283,33 +235,14 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
283
235
|
.argument("<title>", "Database title")
|
|
284
236
|
.option("--app <app-id>", "App ID")
|
|
285
237
|
.requiredOption("--type <database-type>", "Database type (required)")
|
|
286
|
-
.option("--
|
|
287
|
-
.option("--metadata <json>", "[deprecated] Initial CEL context as JSON (legacy alias for --cel-context); prefer --initial-metadata")
|
|
288
|
-
.option("--initial-metadata <json>", "Create-time resource metadata as JSON, keyed by category name (issue #1420). The recommended replacement for --cel-context: read from rules as md.self.<category>.<key>.")
|
|
238
|
+
.option("--initial-metadata <json>", "Create-time resource metadata as JSON, keyed by category name, e.g. '{\"settings\":{\"teamId\":\"t1\"}}'; rules read it as md.self.<category>.<key>")
|
|
289
239
|
.option("--json", "Output as JSON")
|
|
290
240
|
.action(async (title, options) => {
|
|
291
241
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
292
242
|
const client = new ApiClient();
|
|
293
243
|
try {
|
|
294
244
|
const params = { title, databaseType: options.type };
|
|
295
|
-
//
|
|
296
|
-
// supported for backwards compatibility. When both are provided,
|
|
297
|
-
// --cel-context wins.
|
|
298
|
-
if (options.celContext !== undefined || options.metadata !== undefined) {
|
|
299
|
-
warnDeprecated(`primitive databases create ${options.celContext !== undefined ? "--cel-context" : "--metadata"}`, "--initial-metadata (resource metadata categories)", 1815);
|
|
300
|
-
}
|
|
301
|
-
const contextSource = options.celContext ?? options.metadata;
|
|
302
|
-
if (contextSource) {
|
|
303
|
-
try {
|
|
304
|
-
params.metadata = JSON.parse(contextSource);
|
|
305
|
-
}
|
|
306
|
-
catch {
|
|
307
|
-
error(`Invalid ${options.celContext ? "--cel-context" : "--metadata"} JSON.`);
|
|
308
|
-
process.exit(1);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
// `--initial-metadata` is a separate concept: create-time per-category
|
|
312
|
-
// ResourceMetadata rows (issue #1420), not the database CEL context.
|
|
245
|
+
// Create-time per-category ResourceMetadata rows (issue #1420).
|
|
313
246
|
if (options.initialMetadata) {
|
|
314
247
|
try {
|
|
315
248
|
params.initialMetadata = JSON.parse(options.initialMetadata);
|
|
@@ -782,43 +715,18 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
782
715
|
json(result);
|
|
783
716
|
return;
|
|
784
717
|
}
|
|
785
|
-
|
|
786
|
-
//
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
const scalarLabel = options.op === "count"
|
|
795
|
-
? "count"
|
|
796
|
-
: `${options.op.toLowerCase()}(${options.field})`;
|
|
797
|
-
if (groupBy.length === 0) {
|
|
798
|
-
// Ungrouped: one scalar per operation, keyed by the endpoint's
|
|
799
|
-
// operation name (`count`, `sum_<field>`, …). Render it as a
|
|
800
|
-
// key/value line — there is no group column to table.
|
|
801
|
-
const value = (aggregated ?? {})[options.op === "count" ? "count" : `${options.op}_${options.field}`];
|
|
802
|
-
keyValue(scalarLabel, value === null || value === undefined ? "—" : String(value));
|
|
803
|
-
return;
|
|
804
|
-
}
|
|
805
|
-
if (aggregated &&
|
|
806
|
-
typeof aggregated === "object" &&
|
|
807
|
-
!Array.isArray(aggregated) &&
|
|
808
|
-
Object.values(aggregated).every((v) => typeof v !== "object" || v === null)) {
|
|
809
|
-
// Flat { group -> value } shape (single operation, single group-by): render a table.
|
|
810
|
-
const rows = Object.entries(aggregated).map(([group, value]) => ({
|
|
811
|
-
group,
|
|
812
|
-
value: value === null || value === undefined ? "—" : String(value),
|
|
813
|
-
}));
|
|
814
|
-
console.log(formatTable(rows, [
|
|
815
|
-
{ header: "GROUP", key: "group" },
|
|
816
|
-
{ header: valueLabel, key: "value" },
|
|
817
|
-
]));
|
|
718
|
+
// The route answers rows (#4265): one key/value line when
|
|
719
|
+
// ungrouped, a table with one column per group key when grouped.
|
|
720
|
+
const rendered = renderAggregateResult(result?.result, {
|
|
721
|
+
groupBy,
|
|
722
|
+
op: options.op,
|
|
723
|
+
field: options.field,
|
|
724
|
+
});
|
|
725
|
+
if (rendered.kind === "scalar") {
|
|
726
|
+
keyValue(rendered.label, rendered.value);
|
|
818
727
|
}
|
|
819
728
|
else {
|
|
820
|
-
|
|
821
|
-
console.log(JSON.stringify(aggregated ?? {}, null, 2));
|
|
729
|
+
console.log(rendered.text);
|
|
822
730
|
}
|
|
823
731
|
}
|
|
824
732
|
catch (err) {
|
|
@@ -1204,227 +1112,6 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1204
1112
|
process.exit(1);
|
|
1205
1113
|
}
|
|
1206
1114
|
});
|
|
1207
|
-
// ---- CEL context subcommand group ----
|
|
1208
|
-
// `cel-context` is the user-facing name for the dict that feeds CEL
|
|
1209
|
-
// access rules and filter placeholders. `metadata` is kept as a hidden
|
|
1210
|
-
// alias for backwards compatibility with existing scripts.
|
|
1211
|
-
const registerCelContextGroup = (name, hidden) => {
|
|
1212
|
-
const group = databases
|
|
1213
|
-
.command(name, { hidden })
|
|
1214
|
-
.description(hidden
|
|
1215
|
-
? "(deprecated: use resource metadata categories, `primitive metadata`) Manage database CEL context (alias of 'cel-context')"
|
|
1216
|
-
: "(deprecated: use resource metadata categories, `primitive metadata`) Manage database CEL context (the dict referenced by access rules and filters)");
|
|
1217
|
-
// CEL context is deprecated in favour of resource metadata categories
|
|
1218
|
-
// (#1420, #1815), which carry separate read and write rules.
|
|
1219
|
-
deprecateCommandGroup(group, CEL_CONTEXT_SUCCESSOR, { issue: 1815 });
|
|
1220
|
-
group
|
|
1221
|
-
.command("get")
|
|
1222
|
-
.description("Read database CEL context")
|
|
1223
|
-
.argument("<database-id>", "Database ID")
|
|
1224
|
-
.option("--app <app-id>", "App ID")
|
|
1225
|
-
.option("--json", "Output as JSON")
|
|
1226
|
-
.action(async (databaseId, options) => {
|
|
1227
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
1228
|
-
const client = new ApiClient();
|
|
1229
|
-
try {
|
|
1230
|
-
const result = await client.getDatabaseCelContext(resolvedAppId, databaseId);
|
|
1231
|
-
if (options.json) {
|
|
1232
|
-
json(result);
|
|
1233
|
-
return;
|
|
1234
|
-
}
|
|
1235
|
-
printResult("Database ID", result.databaseId);
|
|
1236
|
-
const ctx = result.celContext ?? result.metadata;
|
|
1237
|
-
if (ctx && Object.keys(ctx).length > 0) {
|
|
1238
|
-
printResult("CEL Context", JSON.stringify(ctx, null, 2));
|
|
1239
|
-
}
|
|
1240
|
-
else {
|
|
1241
|
-
info("No CEL context set.");
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
catch (err) {
|
|
1245
|
-
error(err.message);
|
|
1246
|
-
process.exit(1);
|
|
1247
|
-
}
|
|
1248
|
-
});
|
|
1249
|
-
group
|
|
1250
|
-
.command("update")
|
|
1251
|
-
.description("Update database CEL context (merge with existing)")
|
|
1252
|
-
.argument("<database-id>", "Database ID")
|
|
1253
|
-
.requiredOption("--data <json>", "CEL context fields as JSON (merged with existing)")
|
|
1254
|
-
.option("--app <app-id>", "App ID")
|
|
1255
|
-
.option("--json", "Output as JSON")
|
|
1256
|
-
.action(async (databaseId, options) => {
|
|
1257
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
1258
|
-
const client = new ApiClient();
|
|
1259
|
-
let data;
|
|
1260
|
-
try {
|
|
1261
|
-
data = JSON.parse(options.data);
|
|
1262
|
-
}
|
|
1263
|
-
catch {
|
|
1264
|
-
error("Invalid --data JSON.");
|
|
1265
|
-
process.exit(1);
|
|
1266
|
-
return;
|
|
1267
|
-
}
|
|
1268
|
-
try {
|
|
1269
|
-
const result = await client.updateDatabaseCelContext(resolvedAppId, databaseId, data);
|
|
1270
|
-
if (options.json) {
|
|
1271
|
-
json(result);
|
|
1272
|
-
return;
|
|
1273
|
-
}
|
|
1274
|
-
success("CEL context updated.");
|
|
1275
|
-
const ctx = result.celContext ?? result.metadata;
|
|
1276
|
-
if (ctx) {
|
|
1277
|
-
keyValue("CEL Context", JSON.stringify(ctx));
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
catch (err) {
|
|
1281
|
-
error(err.message);
|
|
1282
|
-
process.exit(1);
|
|
1283
|
-
}
|
|
1284
|
-
});
|
|
1285
|
-
};
|
|
1286
|
-
registerCelContextGroup("cel-context", false);
|
|
1287
|
-
// Hidden legacy alias — `primitive databases metadata get/update` still works.
|
|
1288
|
-
registerCelContextGroup("metadata", true);
|
|
1289
|
-
// ---- Operations subcommand group ----
|
|
1290
|
-
const operations = databases
|
|
1291
|
-
.command("operations")
|
|
1292
|
-
.description("List and execute registered operations on a database");
|
|
1293
|
-
// List operations
|
|
1294
|
-
operations
|
|
1295
|
-
.command("list")
|
|
1296
|
-
.description("List registered operations available on a database")
|
|
1297
|
-
.argument("<database-id>", "Database ID")
|
|
1298
|
-
.option("--app <app-id>", "App ID")
|
|
1299
|
-
.option("--json", "Output as JSON")
|
|
1300
|
-
.action(async (databaseId, options) => {
|
|
1301
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
1302
|
-
const client = new ApiClient();
|
|
1303
|
-
try {
|
|
1304
|
-
const result = await client.listDatabaseOperations(resolvedAppId, databaseId);
|
|
1305
|
-
const list = Array.isArray(result) ? result : [];
|
|
1306
|
-
if (options.json) {
|
|
1307
|
-
json(wholeListEnvelope(list));
|
|
1308
|
-
return;
|
|
1309
|
-
}
|
|
1310
|
-
if (list.length === 0) {
|
|
1311
|
-
info("No operations found.");
|
|
1312
|
-
return;
|
|
1313
|
-
}
|
|
1314
|
-
console.log(formatTable(list, [
|
|
1315
|
-
{ header: "NAME", key: "name" },
|
|
1316
|
-
{ header: "TYPE", key: "type" },
|
|
1317
|
-
{ header: "MODEL", key: "modelName" },
|
|
1318
|
-
{ header: "ACCESS", key: "access" },
|
|
1319
|
-
]));
|
|
1320
|
-
}
|
|
1321
|
-
catch (err) {
|
|
1322
|
-
error(err.message);
|
|
1323
|
-
process.exit(1);
|
|
1324
|
-
}
|
|
1325
|
-
});
|
|
1326
|
-
// Execute operation
|
|
1327
|
-
operations
|
|
1328
|
-
.command("execute")
|
|
1329
|
-
.description("Execute a registered operation on a database")
|
|
1330
|
-
.argument("<database-id>", "Database ID")
|
|
1331
|
-
.argument("<operation-name>", "Operation name")
|
|
1332
|
-
.option("--params <json>", "Operation parameters as JSON")
|
|
1333
|
-
.option("--limit <n>", "Max records to return (for queries)")
|
|
1334
|
-
.option("--cursor <cursor>", "Pagination cursor")
|
|
1335
|
-
.option("--token <jwt>", "Execute as a specific user (dev/test — JWT visible in process args)")
|
|
1336
|
-
.option("--app <app-id>", "App ID")
|
|
1337
|
-
.option("--json", "Output as JSON")
|
|
1338
|
-
.option("--timing", "Show server-side timing breakdown")
|
|
1339
|
-
.action(async (databaseId, operationName, options) => {
|
|
1340
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
1341
|
-
const client = new ApiClient();
|
|
1342
|
-
let params;
|
|
1343
|
-
if (options.params) {
|
|
1344
|
-
try {
|
|
1345
|
-
params = JSON.parse(options.params);
|
|
1346
|
-
}
|
|
1347
|
-
catch {
|
|
1348
|
-
error("Invalid --params JSON.");
|
|
1349
|
-
process.exit(1);
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
const body = {};
|
|
1354
|
-
if (params)
|
|
1355
|
-
body.params = params;
|
|
1356
|
-
if (options.limit)
|
|
1357
|
-
body.limit = parseInt(options.limit, 10);
|
|
1358
|
-
if (options.cursor)
|
|
1359
|
-
body.cursor = options.cursor;
|
|
1360
|
-
try {
|
|
1361
|
-
const result = await client.executeDatabaseOperation(resolvedAppId, databaseId, operationName, body, options.token, { timing: !!options.timing });
|
|
1362
|
-
if (options.json) {
|
|
1363
|
-
json(result);
|
|
1364
|
-
return;
|
|
1365
|
-
}
|
|
1366
|
-
// Smart display based on result shape. `executeDatabaseOperation()`
|
|
1367
|
-
// has already normalized a query page to `{ items, hasMore,
|
|
1368
|
-
// nextCursor? }` (#2440), so `items` is exactly "this result was a
|
|
1369
|
-
// list" — the two branches cannot disagree about it.
|
|
1370
|
-
if (Array.isArray(result.items)) {
|
|
1371
|
-
if (result.items.length === 0) {
|
|
1372
|
-
info("No records found.");
|
|
1373
|
-
}
|
|
1374
|
-
else {
|
|
1375
|
-
const cols = Object.keys(result.items[0]).filter((k) => k !== "type");
|
|
1376
|
-
console.log(formatTable(result.items, cols.map((c) => ({
|
|
1377
|
-
header: c.toUpperCase(),
|
|
1378
|
-
key: c,
|
|
1379
|
-
format: (v) => {
|
|
1380
|
-
if (v === null || v === undefined)
|
|
1381
|
-
return "—";
|
|
1382
|
-
if (typeof v === "object") {
|
|
1383
|
-
const s = JSON.stringify(v);
|
|
1384
|
-
return s.length > 50 ? s.slice(0, 47) + "..." : s;
|
|
1385
|
-
}
|
|
1386
|
-
const s = String(v);
|
|
1387
|
-
return s.length > 50 ? s.slice(0, 47) + "..." : s;
|
|
1388
|
-
},
|
|
1389
|
-
}))));
|
|
1390
|
-
}
|
|
1391
|
-
// Gate on the cursor itself, the way `records query` does: `hasMore`
|
|
1392
|
-
// without a cursor used to print the literal `undefined` (#2440).
|
|
1393
|
-
if (result.nextCursor) {
|
|
1394
|
-
info(`More records available. Use --cursor ${result.nextCursor} to continue.`);
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
else if (result.count !== undefined) {
|
|
1398
|
-
console.log(`Count: ${result.count}`);
|
|
1399
|
-
}
|
|
1400
|
-
else if (result.result !== undefined) {
|
|
1401
|
-
// Aggregate result
|
|
1402
|
-
json(result);
|
|
1403
|
-
}
|
|
1404
|
-
else if (result.results && Array.isArray(result.results)) {
|
|
1405
|
-
// Batch/mutation results
|
|
1406
|
-
success(`Operation executed (${result.results.length} result${result.results.length === 1 ? "" : "s"}).`);
|
|
1407
|
-
}
|
|
1408
|
-
else if (result.success !== undefined) {
|
|
1409
|
-
success("Operation executed.");
|
|
1410
|
-
}
|
|
1411
|
-
else {
|
|
1412
|
-
json(result);
|
|
1413
|
-
}
|
|
1414
|
-
// Display timing if present
|
|
1415
|
-
if (result._timing) {
|
|
1416
|
-
console.log("");
|
|
1417
|
-
console.log("Timing:");
|
|
1418
|
-
for (const [key, value] of Object.entries(result._timing)) {
|
|
1419
|
-
console.log(` ${key}: ${value}ms`);
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
catch (err) {
|
|
1424
|
-
error(err.message);
|
|
1425
|
-
process.exit(1);
|
|
1426
|
-
}
|
|
1427
|
-
});
|
|
1428
1115
|
// ---- Indexes subcommand group ----
|
|
1429
1116
|
const indexes = databases
|
|
1430
1117
|
.command("indexes")
|
|
@@ -1680,6 +1367,11 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1680
1367
|
info("No databases to import.");
|
|
1681
1368
|
return;
|
|
1682
1369
|
}
|
|
1370
|
+
// Check every database's files before writing anything, so a malformed
|
|
1371
|
+
// export fails without leaving a database behind. Each one is read
|
|
1372
|
+
// again as it is imported rather than holding every export's records.
|
|
1373
|
+
for (const dbDir of dbDirs)
|
|
1374
|
+
readDatabaseExport(dbDir);
|
|
1683
1375
|
const summary = {
|
|
1684
1376
|
created: 0,
|
|
1685
1377
|
updated: 0,
|
|
@@ -1690,6 +1382,7 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1690
1382
|
aborted: false,
|
|
1691
1383
|
indexesRegistered: 0,
|
|
1692
1384
|
batchErrors: [],
|
|
1385
|
+
databases: [],
|
|
1693
1386
|
};
|
|
1694
1387
|
for (const dbDir of dbDirs) {
|
|
1695
1388
|
await importSingleDatabase(client, resolvedAppId, dbDir, {
|
|
@@ -1745,11 +1438,10 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1745
1438
|
// ============================================
|
|
1746
1439
|
databases
|
|
1747
1440
|
.command("import-csv")
|
|
1748
|
-
.description("Import rows from a CSV file into a database
|
|
1441
|
+
.description("Import rows from a CSV file into a database: one save per row through records/batch, best-effort (a row the database refuses is counted as failed and the rest are written)")
|
|
1749
1442
|
.argument("<database-id>", "Database ID")
|
|
1750
1443
|
.argument("<file>", "Path to the CSV file")
|
|
1751
|
-
.requiredOption("--model <name>", "Model
|
|
1752
|
-
.option("--operation <op>", "Registered mutation operation to use (must be a {modelName,id,data} save-like op)", "seed_save")
|
|
1444
|
+
.requiredOption("--model <name>", "Model every row is saved into")
|
|
1753
1445
|
.option("--column-map <json>", 'Map CSV headers to field names, e.g. \'{"CSV Header":"fieldName"}\'')
|
|
1754
1446
|
.option("--types <json>", 'Coerce fields by type, e.g. \'{"price":"number","active":"boolean"}\'')
|
|
1755
1447
|
.option("--id-column <col>", "Use this CSV column for record ids (else a ULID is generated)")
|
|
@@ -1785,11 +1477,11 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1785
1477
|
return;
|
|
1786
1478
|
}
|
|
1787
1479
|
}
|
|
1788
|
-
//
|
|
1789
|
-
//
|
|
1480
|
+
// `records/batch` has no fixed server item cap — bound it with the same
|
|
1481
|
+
// conservative client-side ceiling `databases import` uses.
|
|
1790
1482
|
const parsedBatchSize = parseBatchSize(options.batchSize, {
|
|
1791
|
-
max:
|
|
1792
|
-
overMaxMessage:
|
|
1483
|
+
max: IMPORT_BATCH_SIZE_CEILING,
|
|
1484
|
+
overMaxMessage: `--batch-size cannot exceed ${IMPORT_BATCH_SIZE_CEILING} (a conservative client-side limit for batch write requests).`,
|
|
1793
1485
|
});
|
|
1794
1486
|
if ("error" in parsedBatchSize) {
|
|
1795
1487
|
error(parsedBatchSize.error);
|
|
@@ -1808,21 +1500,14 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1808
1500
|
let rows = parseCsv(raw, options.delimiter);
|
|
1809
1501
|
// 2. Apply column map.
|
|
1810
1502
|
rows = applyColumnMap(rows, columnMap);
|
|
1811
|
-
// 3. Coerce types, assign ids, shape into batch
|
|
1503
|
+
// 3. Coerce types, assign ids, shape into records/batch saves.
|
|
1812
1504
|
const { batch, rowCount } = buildRows(rows, {
|
|
1813
1505
|
modelName: options.model,
|
|
1814
1506
|
types,
|
|
1815
1507
|
idColumn: options.idColumn,
|
|
1816
1508
|
});
|
|
1817
1509
|
const chunks = chunk(batch, batchSize);
|
|
1818
|
-
// 4.
|
|
1819
|
-
// BEFORE writing (op-not-found / incompatible → clear up-front error
|
|
1820
|
-
// rather than per-row server-side failure). Skip the round-trip only
|
|
1821
|
-
// when there is genuinely nothing to import in a dry run.
|
|
1822
|
-
if (!options.dryRun || rowCount > 0) {
|
|
1823
|
-
await validateImportOperation(client, resolvedAppId, databaseId, options.operation);
|
|
1824
|
-
}
|
|
1825
|
-
// 5. Empty / headers-only CSV: no request, report 0/0.
|
|
1510
|
+
// 4. Empty / headers-only CSV: no request, report 0/0.
|
|
1826
1511
|
if (rowCount === 0) {
|
|
1827
1512
|
if (options.json) {
|
|
1828
1513
|
json({ imported: 0, failed: 0, rows: 0, batches: 0, errors: [], dryRun: !!options.dryRun });
|
|
@@ -1833,7 +1518,7 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1833
1518
|
}
|
|
1834
1519
|
return;
|
|
1835
1520
|
}
|
|
1836
|
-
//
|
|
1521
|
+
// 5. Dry run: report counts, no writes.
|
|
1837
1522
|
if (options.dryRun) {
|
|
1838
1523
|
if (options.json) {
|
|
1839
1524
|
json({ dryRun: true, rows: rowCount, batches: chunks.length, batchSize });
|
|
@@ -1846,32 +1531,60 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1846
1531
|
}
|
|
1847
1532
|
return;
|
|
1848
1533
|
}
|
|
1849
|
-
//
|
|
1534
|
+
// 6. Import chunk by chunk through records/batch, non-atomic (the
|
|
1535
|
+
// route's default): each row's outcome is its own, so a refused row
|
|
1536
|
+
// is counted failed and its siblings commit.
|
|
1850
1537
|
let imported = 0;
|
|
1851
1538
|
let failed = 0;
|
|
1852
1539
|
const errors = [];
|
|
1853
1540
|
let aborted = false;
|
|
1854
1541
|
for (let i = 0; i < chunks.length; i++) {
|
|
1855
1542
|
const piece = chunks[i];
|
|
1543
|
+
let chunkFailed = false;
|
|
1856
1544
|
try {
|
|
1857
|
-
const res = await client.
|
|
1858
|
-
|
|
1859
|
-
|
|
1545
|
+
const res = await client.batchDatabaseRecords(resolvedAppId, databaseId, piece);
|
|
1546
|
+
const results = res?.results ?? [];
|
|
1547
|
+
let chunkImported = 0;
|
|
1548
|
+
let chunkRefused = 0;
|
|
1549
|
+
const firstErrors = [];
|
|
1550
|
+
for (let j = 0; j < piece.length; j++) {
|
|
1551
|
+
const r = results[j];
|
|
1552
|
+
if (r && r.success) {
|
|
1553
|
+
chunkImported++;
|
|
1554
|
+
}
|
|
1555
|
+
else {
|
|
1556
|
+
chunkRefused++;
|
|
1557
|
+
if (r?.error && firstErrors.length < 3)
|
|
1558
|
+
firstErrors.push(r.error);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
imported += chunkImported;
|
|
1562
|
+
failed += chunkRefused;
|
|
1563
|
+
if (chunkRefused > 0) {
|
|
1564
|
+
chunkFailed = true;
|
|
1565
|
+
errors.push({
|
|
1566
|
+
batch: i,
|
|
1567
|
+
error: `${chunkRefused} row(s) failed${firstErrors.length ? `: ${firstErrors.join("; ")}` : ""}`,
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1860
1570
|
if (!options.json) {
|
|
1861
|
-
info(`Batch ${i + 1}/${chunks.length}: imported ${
|
|
1571
|
+
info(`Batch ${i + 1}/${chunks.length}: imported ${chunkImported}, failed ${chunkRefused}`);
|
|
1862
1572
|
}
|
|
1863
1573
|
}
|
|
1864
1574
|
catch (err) {
|
|
1575
|
+
// A chunk-level failure (a 400 for a malformed op): the whole
|
|
1576
|
+
// chunk was refused, so every row in it counts as failed.
|
|
1865
1577
|
const msg = err?.message || String(err);
|
|
1578
|
+
chunkFailed = true;
|
|
1866
1579
|
errors.push({ batch: i, error: msg });
|
|
1867
1580
|
failed += piece.length;
|
|
1868
1581
|
if (!options.json) {
|
|
1869
1582
|
warn(`Batch ${i + 1}/${chunks.length} failed: ${msg}`);
|
|
1870
1583
|
}
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1584
|
+
}
|
|
1585
|
+
if (chunkFailed && options.stopOnError) {
|
|
1586
|
+
aborted = true;
|
|
1587
|
+
break;
|
|
1875
1588
|
}
|
|
1876
1589
|
}
|
|
1877
1590
|
if (options.json) {
|
|
@@ -1889,7 +1602,7 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1889
1602
|
}
|
|
1890
1603
|
}
|
|
1891
1604
|
}
|
|
1892
|
-
// Non-zero exit if any chunk
|
|
1605
|
+
// Non-zero exit if any row or chunk failed.
|
|
1893
1606
|
if (errors.length > 0) {
|
|
1894
1607
|
process.exit(1);
|
|
1895
1608
|
}
|
|
@@ -1907,7 +1620,7 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1907
1620
|
.description("Manage the TOML schema attached to a database type");
|
|
1908
1621
|
schema
|
|
1909
1622
|
.command("generate")
|
|
1910
|
-
.description("Scaffold a starting TOML schema for <database-type> from
|
|
1623
|
+
.description("Scaffold a starting TOML schema for <database-type> from the models and fields its databases hold, and append it to the local database-type-configs/<type>.toml file.")
|
|
1911
1624
|
.argument("<database-type>", "Database type name")
|
|
1912
1625
|
.option("-f, --force", "Overwrite an existing [models.*] block in the local file")
|
|
1913
1626
|
.option("--json", "Output the generated TOML as JSON")
|
|
@@ -1946,10 +1659,10 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1946
1659
|
console.log(scaffoldedToml);
|
|
1947
1660
|
return;
|
|
1948
1661
|
}
|
|
1949
|
-
// 3. For each candidate,
|
|
1950
|
-
//
|
|
1951
|
-
//
|
|
1952
|
-
//
|
|
1662
|
+
// 3. For each candidate, append the scaffolded TOML at the end of the
|
|
1663
|
+
// file. Per the plan-of-record, textual insertion is deliberate —
|
|
1664
|
+
// smol-toml parse/stringify drops comments, so we splice as a raw
|
|
1665
|
+
// string.
|
|
1953
1666
|
for (const targetPath of candidateFiles) {
|
|
1954
1667
|
const original = readFileSync(targetPath, "utf-8");
|
|
1955
1668
|
// Detect existing [models.*] block. If present, refuse unless --force.
|
|
@@ -1958,26 +1671,9 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1958
1671
|
error(`${targetPath} already has [models.*] blocks. Re-run with --force to overwrite.`);
|
|
1959
1672
|
process.exit(1);
|
|
1960
1673
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
cleaned = stripModelsBlocks(original);
|
|
1965
|
-
}
|
|
1966
|
-
// Splice: insert before the first [[operations]] block, otherwise
|
|
1967
|
-
// at the end of file.
|
|
1968
|
-
let merged;
|
|
1969
|
-
const opsAnchor = cleaned.indexOf("\n[[operations]]");
|
|
1970
|
-
if (opsAnchor >= 0) {
|
|
1971
|
-
merged =
|
|
1972
|
-
cleaned.slice(0, opsAnchor + 1) +
|
|
1973
|
-
scaffoldedToml +
|
|
1974
|
-
"\n" +
|
|
1975
|
-
cleaned.slice(opsAnchor + 1);
|
|
1976
|
-
}
|
|
1977
|
-
else {
|
|
1978
|
-
const sep = cleaned.endsWith("\n") ? "" : "\n";
|
|
1979
|
-
merged = cleaned + sep + scaffoldedToml + "\n";
|
|
1980
|
-
}
|
|
1674
|
+
const merged = spliceScaffoldedSchema(original, scaffoldedToml, {
|
|
1675
|
+
force: !!options.force,
|
|
1676
|
+
});
|
|
1981
1677
|
writeFileSync(targetPath, merged);
|
|
1982
1678
|
success(`Inserted schema into ${targetPath}`);
|
|
1983
1679
|
}
|
|
@@ -1988,137 +1684,18 @@ database-type-config' and the 'database-type-configs' group for inspection:
|
|
|
1988
1684
|
process.exit(1);
|
|
1989
1685
|
}
|
|
1990
1686
|
});
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
try {
|
|
2004
|
-
// 0. Validate --lang up front so an unknown value fails clearly rather
|
|
2005
|
-
// than silently defaulting to the TS emitter.
|
|
2006
|
-
const lang = String(options.lang ?? "ts").toLowerCase();
|
|
2007
|
-
if (lang !== "ts" && lang !== "swift") {
|
|
2008
|
-
error(`Unknown --lang "${options.lang}". Use "ts" or "swift".`);
|
|
2009
|
-
process.exit(1);
|
|
2010
|
-
}
|
|
2011
|
-
// 1. The single source database-type-configs/ directory: the
|
|
2012
|
-
// environment's, and only ever the environment's (#1510, #3154).
|
|
2013
|
-
const dbTypesSourceDir = resolveCodegenSourceDir({
|
|
2014
|
-
subdir: "database-type-configs",
|
|
2015
|
-
});
|
|
2016
|
-
// 2. Collect the source .toml files (one per database type), filtered
|
|
2017
|
-
// to a single type when an argument is given.
|
|
2018
|
-
const inputs = [];
|
|
2019
|
-
for (const fileName of readdirSync(dbTypesSourceDir)) {
|
|
2020
|
-
if (!fileName.endsWith(".toml"))
|
|
2021
|
-
continue;
|
|
2022
|
-
const typeName = fileName.slice(0, -".toml".length);
|
|
2023
|
-
if (databaseType && typeName !== databaseType)
|
|
2024
|
-
continue;
|
|
2025
|
-
const tomlPath = path.join(dbTypesSourceDir, fileName);
|
|
2026
|
-
inputs.push({
|
|
2027
|
-
databaseType: typeName,
|
|
2028
|
-
tomlPath,
|
|
2029
|
-
tomlContent: readFileSync(tomlPath, "utf-8"),
|
|
2030
|
-
});
|
|
2031
|
-
}
|
|
2032
|
-
if (inputs.length === 0) {
|
|
2033
|
-
error(databaseType
|
|
2034
|
-
? `No database-type-configs/${databaseType}.toml found under primitive/<env>/.`
|
|
2035
|
-
: "No database-type-configs/*.toml files found to generate from.");
|
|
2036
|
-
process.exit(1);
|
|
2037
|
-
}
|
|
2038
|
-
// 3. Resolve the output directory. Default: a `generated/` subdir of
|
|
2039
|
-
// the first source dir (keeps generated output beside the schema).
|
|
2040
|
-
const outputDir = options.output
|
|
2041
|
-
? path.resolve(options.output)
|
|
2042
|
-
: path.join(path.dirname(inputs[0].tomlPath), "generated");
|
|
2043
|
-
// 4. Run codegen (or --check) with the selected emitter. Both the TS
|
|
2044
|
-
// and Swift generators return the same WriteOrCheckResult shape, so
|
|
2045
|
-
// the reporting below is emitter-agnostic; only the emitter (and the
|
|
2046
|
-
// regenerate hint) differs. When a single database-type filter
|
|
2047
|
-
// argument is given, the input set is partial — flag it so stale
|
|
2048
|
-
// cleanup is scoped to that type and sibling types' generated files
|
|
2049
|
-
// are left intact.
|
|
2050
|
-
const codegenResult = lang === "swift"
|
|
2051
|
-
? await generateDbSwiftTypes({
|
|
2052
|
-
inputs,
|
|
2053
|
-
outputDir,
|
|
2054
|
-
check: !!options.check,
|
|
2055
|
-
singleType: !!databaseType,
|
|
2056
|
-
})
|
|
2057
|
-
: await generateDbTypes({
|
|
2058
|
-
inputs,
|
|
2059
|
-
outputDir,
|
|
2060
|
-
check: !!options.check,
|
|
2061
|
-
singleType: !!databaseType,
|
|
2062
|
-
});
|
|
2063
|
-
if (options.check) {
|
|
2064
|
-
if (codegenResult.mismatches.length > 0) {
|
|
2065
|
-
if (options.json) {
|
|
2066
|
-
json({ ok: false, mismatches: codegenResult.mismatches });
|
|
2067
|
-
}
|
|
2068
|
-
else {
|
|
2069
|
-
error(`Check failed: ${codegenResult.mismatches.length} file(s) out of date.`);
|
|
2070
|
-
for (const m of codegenResult.mismatches) {
|
|
2071
|
-
error(` ${m.reason}: ${path.relative(process.cwd(), m.filePath)}`);
|
|
2072
|
-
}
|
|
2073
|
-
// Echo back the options that decide WHERE and WHAT was
|
|
2074
|
-
// checked, the way `workflows codegen` does (#2895). Suggesting
|
|
2075
|
-
// a bare `databases codegen` after a `--check -o <dir>` sent
|
|
2076
|
-
// people to regenerate into the default
|
|
2077
|
-
// <config-dir>/database-type-configs/generated/ instead of the
|
|
2078
|
-
// directory that is actually out of date, so the files stayed
|
|
2079
|
-
// stale and the next check failed the same way (#2911).
|
|
2080
|
-
const regenerate = ["primitive databases codegen"];
|
|
2081
|
-
if (databaseType)
|
|
2082
|
-
regenerate.push(databaseType);
|
|
2083
|
-
if (lang === "swift")
|
|
2084
|
-
regenerate.push("--lang swift");
|
|
2085
|
-
if (options.output)
|
|
2086
|
-
regenerate.push(`-o ${options.output}`);
|
|
2087
|
-
info(`Run \`${regenerate.join(" ")}\` to regenerate.`);
|
|
2088
|
-
}
|
|
2089
|
-
process.exit(1);
|
|
2090
|
-
}
|
|
2091
|
-
if (options.json) {
|
|
2092
|
-
json({ ok: true, checked: codegenResult.writtenFiles.length });
|
|
2093
|
-
}
|
|
2094
|
-
else {
|
|
2095
|
-
success(`Check passed: ${codegenResult.writtenFiles.length} file(s) up to date.`);
|
|
2096
|
-
}
|
|
2097
|
-
return;
|
|
2098
|
-
}
|
|
2099
|
-
if (options.json) {
|
|
2100
|
-
json({
|
|
2101
|
-
written: codegenResult.writtenFiles,
|
|
2102
|
-
deleted: codegenResult.deletedFiles,
|
|
2103
|
-
});
|
|
2104
|
-
return;
|
|
2105
|
-
}
|
|
2106
|
-
success(`Generated ${codegenResult.writtenFiles.length} file(s)` +
|
|
2107
|
-
(codegenResult.deletedFiles.length > 0
|
|
2108
|
-
? `, deleted ${codegenResult.deletedFiles.length} stale file(s).`
|
|
2109
|
-
: "."));
|
|
2110
|
-
for (const f of codegenResult.writtenFiles) {
|
|
2111
|
-
keyValue(" wrote", path.relative(process.cwd(), f));
|
|
2112
|
-
}
|
|
2113
|
-
for (const f of codegenResult.deletedFiles) {
|
|
2114
|
-
keyValue(" deleted", path.relative(process.cwd(), f));
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
catch (err) {
|
|
2118
|
-
error(err.message);
|
|
2119
|
-
process.exit(1);
|
|
2120
|
-
}
|
|
2121
|
-
});
|
|
1687
|
+
}
|
|
1688
|
+
/**
|
|
1689
|
+
* Append a scaffolded `[models.*]` schema to a database type file's text,
|
|
1690
|
+
* first stripping the file's existing `[models.*]` blocks when `force` is set.
|
|
1691
|
+
* Appended at the end of the file: #3189 retired the `[[operations]]` blocks
|
|
1692
|
+
* the scaffold used to be inserted ahead of.
|
|
1693
|
+
*/
|
|
1694
|
+
export function spliceScaffoldedSchema(original, scaffoldedToml, options = {}) {
|
|
1695
|
+
const hasModelsBlock = /\n\[models\./.test("\n" + original);
|
|
1696
|
+
const cleaned = hasModelsBlock && options.force ? stripModelsBlocks(original) : original;
|
|
1697
|
+
const sep = cleaned.endsWith("\n") ? "" : "\n";
|
|
1698
|
+
return cleaned + sep + scaffoldedToml + "\n";
|
|
2122
1699
|
}
|
|
2123
1700
|
/** Remove every existing `[models.*]` block from a TOML file body. */
|
|
2124
1701
|
function stripModelsBlocks(text) {
|
|
@@ -2157,7 +1734,6 @@ async function exportSingleDatabase(client, appId, databaseId, outputDir, jsonOu
|
|
|
2157
1734
|
databaseId: dbMeta.databaseId || databaseId,
|
|
2158
1735
|
title: dbMeta.title,
|
|
2159
1736
|
databaseType: dbMeta.databaseType || null,
|
|
2160
|
-
metadata: dbMeta.metadata || null,
|
|
2161
1737
|
};
|
|
2162
1738
|
writeFileSync(path.join(dbDir, "metadata.json"), JSON.stringify(metadata, null, 2));
|
|
2163
1739
|
// 3. Export records: list models, then query each.
|
|
@@ -2188,8 +1764,9 @@ async function exportSingleDatabase(client, appId, databaseId, outputDir, jsonOu
|
|
|
2188
1764
|
hasMore = !!cursor && records.length > 0;
|
|
2189
1765
|
}
|
|
2190
1766
|
}
|
|
2191
|
-
// 4. Export indexes
|
|
2192
|
-
|
|
1767
|
+
// 4. Export indexes. The route answers `{ indexes: [...] }`; the file holds
|
|
1768
|
+
// the bare list, which every version of `databases import` reads.
|
|
1769
|
+
const indexes = indexList(await client.listDatabaseIndexes(appId, databaseId), "the index listing");
|
|
2193
1770
|
writeFileSync(path.join(dbDir, "indexes.json"), JSON.stringify(indexes, null, 2));
|
|
2194
1771
|
// 5. Export unique constraints
|
|
2195
1772
|
const constraints = await client.listDatabaseUniqueConstraints(appId, databaseId);
|
|
@@ -2222,20 +1799,27 @@ async function exportSingleDatabase(client, appId, databaseId, outputDir, jsonOu
|
|
|
2222
1799
|
const IMPORT_BATCH_SIZE_CEILING = 25000;
|
|
2223
1800
|
async function importSingleDatabase(client, appId, dbDir, options) {
|
|
2224
1801
|
const { overwrite, dryRun, batchSize, stopOnError, json: jsonOutput, summary } = options;
|
|
2225
|
-
const
|
|
2226
|
-
if (!
|
|
1802
|
+
const exported = readDatabaseExport(dbDir);
|
|
1803
|
+
if (!exported) {
|
|
2227
1804
|
warn(`Skipping ${dbDir}: no metadata.json found`);
|
|
2228
1805
|
summary.skipped++;
|
|
1806
|
+
summary.databases.push({ sourceDatabaseId: path.basename(dbDir), action: "skipped", reason: "no metadata.json" });
|
|
2229
1807
|
return;
|
|
2230
1808
|
}
|
|
2231
|
-
const metadata
|
|
1809
|
+
const { metadata, indexes, constraints, ops } = exported;
|
|
2232
1810
|
const databaseId = metadata.databaseId;
|
|
1811
|
+
const entry = (fields) => ({
|
|
1812
|
+
sourceDatabaseId: databaseId,
|
|
1813
|
+
...(isNonEmptyString(metadata.title) ? { title: metadata.title } : {}),
|
|
1814
|
+
...fields,
|
|
1815
|
+
});
|
|
2233
1816
|
if (dryRun) {
|
|
2234
1817
|
// Report record/batch counts without writing anything (no DB creation,
|
|
2235
1818
|
// no index registration, no batch write).
|
|
2236
|
-
const recordCount =
|
|
1819
|
+
const recordCount = ops.length;
|
|
2237
1820
|
const batchCount = chunk(new Array(recordCount), batchSize).length;
|
|
2238
1821
|
summary.created++;
|
|
1822
|
+
summary.databases.push(entry({ action: "created" }));
|
|
2239
1823
|
summary.recordsImported += recordCount;
|
|
2240
1824
|
summary.batches += batchCount;
|
|
2241
1825
|
info(`[dry-run] Would import database ${databaseId} (${metadata.title || "untitled"}) — ${recordCount} record(s), ${batchCount} batch(es)`);
|
|
@@ -2258,6 +1842,7 @@ async function importSingleDatabase(client, appId, dbDir, options) {
|
|
|
2258
1842
|
if (exists && !overwrite) {
|
|
2259
1843
|
warn(`Skipping ${databaseId}: already exists (use --overwrite to replace)`);
|
|
2260
1844
|
summary.skipped++;
|
|
1845
|
+
summary.databases.push(entry({ action: "skipped", reason: "exists" }));
|
|
2261
1846
|
return;
|
|
2262
1847
|
}
|
|
2263
1848
|
// Create database if it doesn't exist
|
|
@@ -2269,65 +1854,47 @@ async function importSingleDatabase(client, appId, dbDir, options) {
|
|
|
2269
1854
|
if (metadata.databaseType) {
|
|
2270
1855
|
createData.databaseType = metadata.databaseType;
|
|
2271
1856
|
}
|
|
2272
|
-
|
|
2273
|
-
|
|
1857
|
+
// Exports written before #3991 carry the retired database CEL context
|
|
1858
|
+
// dict; the server refuses it on create, so it is not imported.
|
|
1859
|
+
if (metadata.metadata && Object.keys(metadata.metadata).length > 0) {
|
|
1860
|
+
warn(`${databaseId}: not importing its database CEL context (retired in #3991); ` +
|
|
1861
|
+
"move those values into a resource metadata category.");
|
|
2274
1862
|
}
|
|
2275
1863
|
const created = await client.createDatabase(appId, createData);
|
|
2276
1864
|
targetDatabaseId = created.databaseId || created.id || databaseId;
|
|
2277
1865
|
summary.created++;
|
|
1866
|
+
summary.databases.push(entry({ action: "created", targetDatabaseId }));
|
|
2278
1867
|
}
|
|
2279
1868
|
else {
|
|
2280
1869
|
summary.updated++;
|
|
1870
|
+
summary.databases.push(entry({ action: "updated", targetDatabaseId }));
|
|
2281
1871
|
}
|
|
2282
1872
|
// Register indexes
|
|
2283
|
-
const
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
}
|
|
2296
|
-
catch {
|
|
2297
|
-
// Index may already exist
|
|
2298
|
-
}
|
|
1873
|
+
for (const idx of indexes) {
|
|
1874
|
+
try {
|
|
1875
|
+
await client.registerDatabaseIndex(appId, targetDatabaseId, {
|
|
1876
|
+
modelName: idx.modelName || idx.model_name,
|
|
1877
|
+
fieldName: idx.fieldName || idx.field_name,
|
|
1878
|
+
fieldType: idx.fieldType || idx.field_type || "string",
|
|
1879
|
+
unique: Boolean(idx.isUnique || idx.is_unique),
|
|
1880
|
+
});
|
|
1881
|
+
summary.indexesRegistered++;
|
|
1882
|
+
}
|
|
1883
|
+
catch {
|
|
1884
|
+
// Index may already exist
|
|
2299
1885
|
}
|
|
2300
1886
|
}
|
|
2301
1887
|
// Register unique constraints
|
|
2302
|
-
const
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
modelName: c.modelName || c.model_name,
|
|
2309
|
-
constraintName: c.constraintName || c.constraint_name,
|
|
2310
|
-
fields: c.fields ? (typeof c.fields === "string" ? JSON.parse(c.fields) : c.fields) : [],
|
|
2311
|
-
});
|
|
2312
|
-
}
|
|
2313
|
-
catch {
|
|
2314
|
-
// Constraint may already exist
|
|
2315
|
-
}
|
|
1888
|
+
for (const c of constraints) {
|
|
1889
|
+
try {
|
|
1890
|
+
await client.registerDatabaseUniqueConstraint(appId, targetDatabaseId, c);
|
|
1891
|
+
}
|
|
1892
|
+
catch {
|
|
1893
|
+
// Constraint may already exist
|
|
2316
1894
|
}
|
|
2317
1895
|
}
|
|
2318
1896
|
// Import records via chunked batch writes (was one save per record).
|
|
2319
|
-
|
|
2320
|
-
if (existsSync(recordsPath)) {
|
|
2321
|
-
const lines = readFileSync(recordsPath, "utf-8").split("\n").filter(Boolean);
|
|
2322
|
-
// Map each records.jsonl line to a batch save op. Preserve the explicit
|
|
2323
|
-
// `_type`/`_id`; strip the redundant id/type keys out of `_data` (the
|
|
2324
|
-
// per-record loop did the same) so they don't duplicate into stored data.
|
|
2325
|
-
const ops = lines.map((line) => {
|
|
2326
|
-
const record = JSON.parse(line);
|
|
2327
|
-
const { _type, _id, _data } = record;
|
|
2328
|
-
const { id: _ignoredId, type: _ignoredType, ...data } = _data;
|
|
2329
|
-
return { op: "save", modelName: _type, id: _id, data };
|
|
2330
|
-
});
|
|
1897
|
+
if (ops.length > 0) {
|
|
2331
1898
|
const chunks = chunk(ops, batchSize);
|
|
2332
1899
|
summary.batches += chunks.length;
|
|
2333
1900
|
for (let i = 0; i < chunks.length; i++) {
|
|
@@ -2381,13 +1948,108 @@ async function importSingleDatabase(client, appId, dbDir, options) {
|
|
|
2381
1948
|
}
|
|
2382
1949
|
}
|
|
2383
1950
|
}
|
|
2384
|
-
|
|
1951
|
+
const as = targetDatabaseId !== databaseId ? ` as ${targetDatabaseId}` : "";
|
|
1952
|
+
info(`Imported database ${databaseId} (${metadata.title || "untitled"})${as}`);
|
|
1953
|
+
}
|
|
1954
|
+
/**
|
|
1955
|
+
* The index list in the server's `{ indexes: [...] }` answer, or in an
|
|
1956
|
+
* export's indexes.json: exports written before #4490 hold that envelope,
|
|
1957
|
+
* later ones the bare list. `source` names where it came from in the error.
|
|
1958
|
+
*/
|
|
1959
|
+
function indexList(parsed, source) {
|
|
1960
|
+
const list = Array.isArray(parsed) ? parsed : parsed?.indexes;
|
|
1961
|
+
if (!Array.isArray(list)) {
|
|
1962
|
+
throw new Error(`${source} is not an index list (expected an array, or an object with an "indexes" array).`);
|
|
1963
|
+
}
|
|
1964
|
+
return list;
|
|
2385
1965
|
}
|
|
2386
|
-
/**
|
|
2387
|
-
|
|
1966
|
+
/**
|
|
1967
|
+
* Read and check every file in one database's export directory, so an export
|
|
1968
|
+
* that cannot be imported fails before anything is written. Null when the
|
|
1969
|
+
* directory has no metadata.json.
|
|
1970
|
+
*/
|
|
1971
|
+
function readDatabaseExport(dbDir) {
|
|
1972
|
+
const metadataPath = path.join(dbDir, "metadata.json");
|
|
1973
|
+
if (!existsSync(metadataPath))
|
|
1974
|
+
return null;
|
|
1975
|
+
const metadata = readExportJson(metadataPath);
|
|
1976
|
+
const indexesPath = path.join(dbDir, "indexes.json");
|
|
1977
|
+
const indexes = existsSync(indexesPath) ? indexList(readExportJson(indexesPath), indexesPath) : [];
|
|
1978
|
+
const constraintsPath = path.join(dbDir, "constraints.json");
|
|
1979
|
+
const constraints = [];
|
|
1980
|
+
if (existsSync(constraintsPath)) {
|
|
1981
|
+
const parsed = readExportJson(constraintsPath);
|
|
1982
|
+
const list = Array.isArray(parsed) ? parsed : parsed?.constraints;
|
|
1983
|
+
if (!Array.isArray(list)) {
|
|
1984
|
+
throw new Error(`${constraintsPath} is not a constraint list (expected an array).`);
|
|
1985
|
+
}
|
|
1986
|
+
list.forEach((c, i) => constraints.push(exportedConstraint(c, `${constraintsPath} entry ${i + 1}`)));
|
|
1987
|
+
}
|
|
1988
|
+
// Map each records.jsonl line to a batch save op. Preserve the explicit
|
|
1989
|
+
// `_type`/`_id`; strip the redundant id/type keys out of `_data` (the
|
|
1990
|
+
// per-record loop did the same) so they don't duplicate into stored data.
|
|
2388
1991
|
const recordsPath = path.join(dbDir, "records.jsonl");
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
1992
|
+
const ops = [];
|
|
1993
|
+
if (existsSync(recordsPath)) {
|
|
1994
|
+
readFileSync(recordsPath, "utf-8")
|
|
1995
|
+
.split("\n")
|
|
1996
|
+
.forEach((line, i) => {
|
|
1997
|
+
if (!line)
|
|
1998
|
+
return;
|
|
1999
|
+
const where = `${recordsPath} line ${i + 1}`;
|
|
2000
|
+
let record;
|
|
2001
|
+
try {
|
|
2002
|
+
record = JSON.parse(line);
|
|
2003
|
+
}
|
|
2004
|
+
catch (err) {
|
|
2005
|
+
throw new Error(`${where} is not valid JSON: ${err.message}`);
|
|
2006
|
+
}
|
|
2007
|
+
const { _type, _id, _data } = record ?? {};
|
|
2008
|
+
if (!isNonEmptyString(_type) || !isNonEmptyString(_id) || !_data || typeof _data !== "object") {
|
|
2009
|
+
throw new Error(`${where} is not a record (expected "_type", "_id" and "_data").`);
|
|
2010
|
+
}
|
|
2011
|
+
const { id: _ignoredId, type: _ignoredType, ...data } = _data;
|
|
2012
|
+
ops.push({ op: "save", modelName: _type, id: _id, data });
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
return { metadata, indexes, constraints, ops };
|
|
2016
|
+
}
|
|
2017
|
+
function isNonEmptyString(value) {
|
|
2018
|
+
return typeof value === "string" && value.length > 0;
|
|
2019
|
+
}
|
|
2020
|
+
/**
|
|
2021
|
+
* One constraints.json entry as the register route takes it. `fields` is the
|
|
2022
|
+
* list the server answers, or the JSON string older exports carry; the route
|
|
2023
|
+
* refuses fewer than two, so such an entry fails here rather than after the
|
|
2024
|
+
* database is created.
|
|
2025
|
+
*/
|
|
2026
|
+
function exportedConstraint(c, where) {
|
|
2027
|
+
const modelName = c?.modelName || c?.model_name;
|
|
2028
|
+
const constraintName = c?.constraintName || c?.constraint_name;
|
|
2029
|
+
let fields = c?.fields;
|
|
2030
|
+
if (typeof fields === "string") {
|
|
2031
|
+
try {
|
|
2032
|
+
fields = JSON.parse(fields);
|
|
2033
|
+
}
|
|
2034
|
+
catch (err) {
|
|
2035
|
+
throw new Error(`${where} has fields that are not valid JSON: ${err.message}`);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
if (!isNonEmptyString(modelName) ||
|
|
2039
|
+
!isNonEmptyString(constraintName) ||
|
|
2040
|
+
!Array.isArray(fields) ||
|
|
2041
|
+
fields.length < 2 ||
|
|
2042
|
+
!fields.every(isNonEmptyString)) {
|
|
2043
|
+
throw new Error(`${where} is not a unique constraint (expected "model_name", "constraint_name" and two or more "fields").`);
|
|
2044
|
+
}
|
|
2045
|
+
return { modelName, constraintName, fields };
|
|
2046
|
+
}
|
|
2047
|
+
function readExportJson(file) {
|
|
2048
|
+
try {
|
|
2049
|
+
return JSON.parse(readFileSync(file, "utf-8"));
|
|
2050
|
+
}
|
|
2051
|
+
catch (err) {
|
|
2052
|
+
throw new Error(`${file} is not valid JSON: ${err.message}`);
|
|
2053
|
+
}
|
|
2392
2054
|
}
|
|
2393
2055
|
//# sourceMappingURL=databases.js.map
|