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
|
@@ -7,7 +7,9 @@ import { confirmPrompt } from "../lib/confirm-prompt.js";
|
|
|
7
7
|
import { pageCursorOption, pageLimitOption, parsePageLimit, printEmptyPage, printPageHint, } from "../lib/list-options.js";
|
|
8
8
|
import { normalizeCliListEnvelope, wholeListEnvelope, } from "../lib/paginate.js";
|
|
9
9
|
import { resolveOwnerUserId } from "../lib/resolve-owner.js";
|
|
10
|
-
import {
|
|
10
|
+
import { parseCustodianOrExit } from "../lib/custodian.js";
|
|
11
|
+
import { parseCollectionPrincipal, parseCollectionRole } from "../lib/collection-principal.js";
|
|
12
|
+
import { COLLECTION_EXPORT_FILENAME, COLLECTION_EXPORT_VERSION, duplicateNameSources, emptyImportSummary, grantDisposition, grantUpdateLine, sameGrantGroup, groupTargetsByName, importExitCode, planCollection, readCollectionExportFile, rowCustodian, custodianUserId, permissionForRole, serverReason, } from "../lib/collection-export.js";
|
|
11
13
|
export function registerCollectionsCommands(program) {
|
|
12
14
|
const collections = program
|
|
13
15
|
.command("collections")
|
|
@@ -18,12 +20,14 @@ Examples:
|
|
|
18
20
|
$ primitive collections create "Sprint 12"
|
|
19
21
|
$ primitive collections create "Sprint 12" --owner user@example.com
|
|
20
22
|
$ primitive collections get <collection-id>
|
|
23
|
+
$ primitive collections list --custodian user:<user-id>
|
|
24
|
+
$ primitive collections transfer <collection-id> user:<user-id>
|
|
21
25
|
$ primitive collections documents list <collection-id>
|
|
22
26
|
$ primitive collections documents add <collection-id> <document-id>
|
|
23
|
-
$ primitive collections access <collection-id>
|
|
24
|
-
$ primitive collections share <collection-id> --group team/engineering --permission read-write
|
|
25
27
|
$ primitive collections members list <collection-id>
|
|
26
|
-
$ primitive collections members add <collection-id> <user-id> --
|
|
28
|
+
$ primitive collections members add <collection-id> <user-id> --role contributor
|
|
29
|
+
$ primitive collections members add <collection-id> group:<group-id> --role viewer
|
|
30
|
+
$ primitive collections members set-role <collection-id> <user-id> editor
|
|
27
31
|
$ primitive collections for-document <document-id>
|
|
28
32
|
`);
|
|
29
33
|
// List collections
|
|
@@ -34,18 +38,26 @@ Examples:
|
|
|
34
38
|
.argument("[app-id]", "App ID (uses current app if not specified)")
|
|
35
39
|
.option("--app <app-id>", "App ID")
|
|
36
40
|
.option("--all", "List every collection in the app (admin-only). Default is collections you're a direct member of.")
|
|
41
|
+
.option("--custodian <custodian>", 'Only this custodian\'s collections: "app" or "user:<user-id>" (needs an admin token)')
|
|
37
42
|
.addOption(pageLimitOption())
|
|
38
43
|
.addOption(pageCursorOption())
|
|
39
44
|
.option("--json", "Output as JSON")
|
|
40
45
|
.action(async (appId, options) => {
|
|
41
46
|
const limit = parsePageLimit(options.limit);
|
|
47
|
+
const custodian = options.custodian === undefined ? undefined : parseCustodianOrExit(options.custodian, "--custodian");
|
|
48
|
+
if (custodian && options.all) {
|
|
49
|
+
error("--custodian and --all are alternatives; pass one.");
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
42
52
|
const resolvedAppId = resolveAppId(appId, options);
|
|
43
53
|
const client = new ApiClient();
|
|
44
54
|
try {
|
|
45
55
|
const params = { limit, cursor: options.cursor };
|
|
46
|
-
const page = normalizeCliListEnvelope(
|
|
47
|
-
? await client.
|
|
48
|
-
:
|
|
56
|
+
const page = normalizeCliListEnvelope(custodian
|
|
57
|
+
? await client.listCollectionsByCustodian(resolvedAppId, custodian, params)
|
|
58
|
+
: options.all
|
|
59
|
+
? await client.listAllCollections(resolvedAppId, params)
|
|
60
|
+
: await client.listCollections(resolvedAppId, params));
|
|
49
61
|
const list = page.items;
|
|
50
62
|
if (options.json) {
|
|
51
63
|
json(page);
|
|
@@ -63,7 +75,10 @@ Examples:
|
|
|
63
75
|
{ header: "CREATED", key: "createdAt", format: formatDate },
|
|
64
76
|
];
|
|
65
77
|
// `permission` is only returned by the default list() endpoint
|
|
66
|
-
if (
|
|
78
|
+
if (custodian) {
|
|
79
|
+
columns.splice(2, 0, { header: "CUSTODIAN", key: "custodian" });
|
|
80
|
+
}
|
|
81
|
+
else if (!options.all) {
|
|
67
82
|
columns.splice(4, 0, { header: "PERMISSION", key: "permission", format: (v) => v || "—" });
|
|
68
83
|
}
|
|
69
84
|
console.log(formatTable(list, columns));
|
|
@@ -82,25 +97,21 @@ Examples:
|
|
|
82
97
|
// Create collection
|
|
83
98
|
collections
|
|
84
99
|
.command("create")
|
|
85
|
-
.description("Create a new collection")
|
|
100
|
+
.description("Create a new collection, held by the caller or by --owner")
|
|
86
101
|
.argument("<name>", "Collection name")
|
|
87
102
|
.option("--description <description>", "Collection description")
|
|
88
|
-
.option("--initial-metadata <json>", "Create-time resource metadata as JSON, keyed by category name
|
|
103
|
+
.option("--initial-metadata <json>", "Create-time resource metadata as JSON, keyed by category name")
|
|
89
104
|
.option("--app <app-id>", "App ID")
|
|
90
|
-
.option("--owner <userId-or-email>", "
|
|
105
|
+
.option("--owner <userId-or-email>", "The user who holds the collection (its custodian and owner); needs an admin token")
|
|
91
106
|
.option("--json", "Output as JSON")
|
|
92
107
|
.addHelpText("after", `
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
The owner is the collection's creator, so under the default rules they — and
|
|
102
|
-
no other non-admin — can edit it, delete it, and manage its documents and
|
|
103
|
-
members.
|
|
108
|
+
Custody:
|
|
109
|
+
Without --owner the caller holds the new collection: an app user's token
|
|
110
|
+
makes them its custodian and owner; a console admin's token gives the app
|
|
111
|
+
custody.
|
|
112
|
+
With --owner the collection is created through the admin API for that user,
|
|
113
|
+
who becomes its custodian and owner; this needs an admin token assigned to
|
|
114
|
+
the app.
|
|
104
115
|
|
|
105
116
|
An email given to --owner is resolved to a user id before anything is
|
|
106
117
|
created; a user who is not in the app fails the command without creating a
|
|
@@ -111,7 +122,7 @@ Ownership:
|
|
|
111
122
|
const client = new ApiClient();
|
|
112
123
|
// Resolve --owner to a userId through the one shared lookup (#2763),
|
|
113
124
|
// before the create: an email nobody answers to must fail with nothing
|
|
114
|
-
// created
|
|
125
|
+
// created.
|
|
115
126
|
let ownerUserId;
|
|
116
127
|
if (options.owner) {
|
|
117
128
|
try {
|
|
@@ -124,8 +135,6 @@ Ownership:
|
|
|
124
135
|
}
|
|
125
136
|
try {
|
|
126
137
|
const data = { name };
|
|
127
|
-
if (ownerUserId)
|
|
128
|
-
data.createdBy = ownerUserId;
|
|
129
138
|
if (options.description)
|
|
130
139
|
data.description = options.description;
|
|
131
140
|
// Create-time per-category ResourceMetadata rows (issue #1420).
|
|
@@ -138,7 +147,9 @@ Ownership:
|
|
|
138
147
|
process.exit(1);
|
|
139
148
|
}
|
|
140
149
|
}
|
|
141
|
-
const result =
|
|
150
|
+
const result = ownerUserId
|
|
151
|
+
? await client.createCollectionAsAdmin(resolvedAppId, { ...data, custodian: `user:${ownerUserId}` })
|
|
152
|
+
: await client.createCollection(resolvedAppId, data);
|
|
142
153
|
if (options.json) {
|
|
143
154
|
json(result);
|
|
144
155
|
return;
|
|
@@ -146,9 +157,8 @@ Ownership:
|
|
|
146
157
|
success("Collection created.");
|
|
147
158
|
keyValue("Collection ID", result.collectionId);
|
|
148
159
|
keyValue("Name", result.name);
|
|
149
|
-
// Read back from the RESPONSE
|
|
150
|
-
|
|
151
|
-
keyValue("Owner", result.createdBy);
|
|
160
|
+
// Read back from the RESPONSE: who holds it is the server's answer.
|
|
161
|
+
keyValue("Custodian", result.custodian ?? "(none)");
|
|
152
162
|
if (result.description)
|
|
153
163
|
keyValue("Description", result.description);
|
|
154
164
|
}
|
|
@@ -184,6 +194,38 @@ Ownership:
|
|
|
184
194
|
process.exit(1);
|
|
185
195
|
}
|
|
186
196
|
});
|
|
197
|
+
// Transfer collection
|
|
198
|
+
collections
|
|
199
|
+
.command("transfer")
|
|
200
|
+
.description("Transfer a collection to another user or to the app")
|
|
201
|
+
.argument("<collection-id>", "Collection ID")
|
|
202
|
+
.argument("<custodian>", '"user:<user-id>" (a member of the app) or "app"')
|
|
203
|
+
.option("--app <app-id>", "App ID")
|
|
204
|
+
.option("--json", "Output as JSON")
|
|
205
|
+
.allowExcessArguments(false)
|
|
206
|
+
.addHelpText("after", `
|
|
207
|
+
The custodian and the owner row move together: the new custodian holds the
|
|
208
|
+
owner role, the previous owner stays as a manager (or, moving to the app,
|
|
209
|
+
loses the owner row), and no other role changes.
|
|
210
|
+
`)
|
|
211
|
+
.action(async (collectionId, custodianArg, options) => {
|
|
212
|
+
const to = parseCustodianOrExit(custodianArg, "custodian");
|
|
213
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
214
|
+
const client = new ApiClient();
|
|
215
|
+
try {
|
|
216
|
+
const result = await client.transferCollection(resolvedAppId, collectionId, to);
|
|
217
|
+
if (options.json) {
|
|
218
|
+
json(result);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
success(`Collection ${collectionId} transferred.`);
|
|
222
|
+
printResult("Custodian", result.custodian);
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
error(err.message);
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
187
229
|
// Update collection
|
|
188
230
|
collections
|
|
189
231
|
.command("update")
|
|
@@ -259,130 +301,6 @@ Ownership:
|
|
|
259
301
|
process.exit(1);
|
|
260
302
|
}
|
|
261
303
|
});
|
|
262
|
-
// ---- Access command ----
|
|
263
|
-
collections
|
|
264
|
-
.command("access")
|
|
265
|
-
.description("Combined view of the groups and members with access to a collection " +
|
|
266
|
-
"(members only: 'collections members list'; group grants: 'collections share'/'unshare')")
|
|
267
|
-
.argument("<collection-id>", "Collection ID")
|
|
268
|
-
.option("--app <app-id>", "App ID")
|
|
269
|
-
.option("--json", "Output as JSON")
|
|
270
|
-
.action(async (collectionId, options) => {
|
|
271
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
272
|
-
const client = new ApiClient();
|
|
273
|
-
try {
|
|
274
|
-
const result = await client.getCollectionAccess(resolvedAppId, collectionId);
|
|
275
|
-
if (options.json) {
|
|
276
|
-
json(result);
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
const groups = result.groups || [];
|
|
280
|
-
const members = result.members || [];
|
|
281
|
-
if (groups.length === 0 && members.length === 0) {
|
|
282
|
-
info("No groups or members have access to this collection.");
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
if (groups.length > 0) {
|
|
286
|
-
console.log("\nGroups:");
|
|
287
|
-
console.log(formatTable(groups, [
|
|
288
|
-
{ header: "TYPE", key: "groupType" },
|
|
289
|
-
{ header: "ID", key: "groupId" },
|
|
290
|
-
{ header: "PERMISSION", key: "permission" },
|
|
291
|
-
{ header: "GRANTED", key: "grantedAt", format: formatDate },
|
|
292
|
-
]));
|
|
293
|
-
}
|
|
294
|
-
if (members.length > 0) {
|
|
295
|
-
console.log("\nMembers:");
|
|
296
|
-
console.log(formatTable(members, [
|
|
297
|
-
{ header: "USER_ID", key: "userId", format: formatId },
|
|
298
|
-
{ header: "PERMISSION", key: "permission" },
|
|
299
|
-
]));
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
catch (err) {
|
|
303
|
-
error(err.message);
|
|
304
|
-
process.exit(1);
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
// ---- Share / unshare (group permissions) ----
|
|
308
|
-
collections
|
|
309
|
-
.command("share")
|
|
310
|
-
.description("Grant a group access to a collection")
|
|
311
|
-
.argument("<collection-id>", "Collection ID")
|
|
312
|
-
.requiredOption("--group <group>", "Group in format type/id (e.g. team/engineering)")
|
|
313
|
-
.requiredOption("--permission <permission>", "Permission level (reader or read-write)")
|
|
314
|
-
.option("--app <app-id>", "App ID")
|
|
315
|
-
.option("--json", "Output as JSON")
|
|
316
|
-
.action(async (collectionId, options) => {
|
|
317
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
318
|
-
const client = new ApiClient();
|
|
319
|
-
const parts = options.group.split("/");
|
|
320
|
-
if (parts.length !== 2) {
|
|
321
|
-
error("Group must be in format type/id (e.g. team/engineering).");
|
|
322
|
-
process.exit(1);
|
|
323
|
-
}
|
|
324
|
-
const [groupType, groupId] = parts;
|
|
325
|
-
try {
|
|
326
|
-
const result = await client.grantCollectionGroupPermission(resolvedAppId, collectionId, {
|
|
327
|
-
groupType,
|
|
328
|
-
groupId,
|
|
329
|
-
permission: options.permission,
|
|
330
|
-
});
|
|
331
|
-
if (options.json) {
|
|
332
|
-
json(result);
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
success(`Granted ${options.permission} access to ${groupType}/${groupId} on collection.`);
|
|
336
|
-
}
|
|
337
|
-
catch (err) {
|
|
338
|
-
error(err.message);
|
|
339
|
-
process.exit(1);
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
collections
|
|
343
|
-
.command("unshare")
|
|
344
|
-
.description("Revoke a group's access to a collection")
|
|
345
|
-
.argument("<collection-id>", "Collection ID")
|
|
346
|
-
.requiredOption("--group <group>", "Group in format type/id (e.g. team/engineering)")
|
|
347
|
-
.option("--app <app-id>", "App ID")
|
|
348
|
-
.option("-y, --yes", "Skip confirmation prompt")
|
|
349
|
-
.option("--json", "Output as JSON")
|
|
350
|
-
.action(async (collectionId, options) => {
|
|
351
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
352
|
-
const parts = options.group.split("/");
|
|
353
|
-
if (parts.length !== 2) {
|
|
354
|
-
error("Group must be in format type/id (e.g. team/engineering).");
|
|
355
|
-
process.exit(1);
|
|
356
|
-
}
|
|
357
|
-
const [groupType, groupId] = parts;
|
|
358
|
-
if (!options.yes) {
|
|
359
|
-
let confirm;
|
|
360
|
-
try {
|
|
361
|
-
confirm = await confirmPrompt(`Revoke ${groupType}/${groupId} access to collection ${collectionId}?`);
|
|
362
|
-
}
|
|
363
|
-
catch (err) {
|
|
364
|
-
error(err.message);
|
|
365
|
-
process.exit(1);
|
|
366
|
-
}
|
|
367
|
-
if (!confirm) {
|
|
368
|
-
info("Cancelled.");
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
const client = new ApiClient();
|
|
373
|
-
try {
|
|
374
|
-
await client.revokeCollectionGroupPermission(resolvedAppId, collectionId, groupType, groupId);
|
|
375
|
-
if (options.json) {
|
|
376
|
-
json({ success: true, collectionId, groupType, groupId });
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
success(`Revoked ${groupType}/${groupId} access from collection.`);
|
|
380
|
-
}
|
|
381
|
-
catch (err) {
|
|
382
|
-
error(err.message);
|
|
383
|
-
process.exit(1);
|
|
384
|
-
}
|
|
385
|
-
});
|
|
386
304
|
// ---- Documents subcommand group ----
|
|
387
305
|
const documents = collections
|
|
388
306
|
.command("documents")
|
|
@@ -526,18 +444,17 @@ Ownership:
|
|
|
526
444
|
}
|
|
527
445
|
});
|
|
528
446
|
// ---- Members subcommand group ----
|
|
447
|
+
//
|
|
448
|
+
// The one home for who holds a role on a collection: users and groups,
|
|
449
|
+
// addressed as principals (`<user-id>`, `group:<group-id>`). Writes go
|
|
450
|
+
// through the admin API, as the console's do, so the owner row can be
|
|
451
|
+
// restored and demoted from here.
|
|
529
452
|
const members = collections
|
|
530
453
|
.command("members")
|
|
531
|
-
.description("Manage
|
|
532
|
-
// List members
|
|
533
|
-
//
|
|
534
|
-
// The server has no GET /collections/:id/members route — membership is read
|
|
535
|
-
// from the access endpoint, which returns groups and members together. This
|
|
536
|
-
// command is the members half of it, so reading "who is on this collection"
|
|
537
|
-
// doesn't require knowing about a command named for neither (#2764).
|
|
454
|
+
.description("Manage a collection's members — users and groups — and their roles");
|
|
538
455
|
members
|
|
539
456
|
.command("list")
|
|
540
|
-
.description("List users
|
|
457
|
+
.description("List a collection's members, users and groups, with their roles")
|
|
541
458
|
.argument("<collection-id>", "Collection ID")
|
|
542
459
|
.option("--app <app-id>", "App ID")
|
|
543
460
|
.option("--json", "Output as JSON")
|
|
@@ -545,21 +462,20 @@ Ownership:
|
|
|
545
462
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
546
463
|
const client = new ApiClient();
|
|
547
464
|
try {
|
|
548
|
-
const
|
|
549
|
-
const list = result.members ?? [];
|
|
465
|
+
const { items } = await client.listCollectionMembers(resolvedAppId, collectionId);
|
|
550
466
|
if (options.json) {
|
|
551
|
-
// The
|
|
552
|
-
|
|
553
|
-
json(wholeListEnvelope(list));
|
|
467
|
+
// The whole membership, in the one list envelope (#3646).
|
|
468
|
+
json(wholeListEnvelope(items));
|
|
554
469
|
return;
|
|
555
470
|
}
|
|
556
|
-
if (
|
|
557
|
-
info("No
|
|
471
|
+
if (items.length === 0) {
|
|
472
|
+
info("No user or group holds a role on this collection.");
|
|
558
473
|
return;
|
|
559
474
|
}
|
|
560
|
-
console.log(formatTable(
|
|
561
|
-
{ header: "
|
|
562
|
-
{ header: "
|
|
475
|
+
console.log(formatTable(items, [
|
|
476
|
+
{ header: "PRINCIPAL", key: "principal" },
|
|
477
|
+
{ header: "ROLE", key: "role" },
|
|
478
|
+
{ header: "ADDED", key: "addedAt", format: formatDate },
|
|
563
479
|
]));
|
|
564
480
|
}
|
|
565
481
|
catch (err) {
|
|
@@ -567,49 +483,97 @@ Ownership:
|
|
|
567
483
|
process.exit(1);
|
|
568
484
|
}
|
|
569
485
|
});
|
|
570
|
-
// Add member
|
|
571
486
|
members
|
|
572
487
|
.command("add")
|
|
573
|
-
.description("
|
|
488
|
+
.description("Give a user or a group a role on a collection")
|
|
574
489
|
.argument("<collection-id>", "Collection ID")
|
|
575
|
-
.argument("<
|
|
576
|
-
.requiredOption("--
|
|
490
|
+
.argument("<principal>", "A user id, or group:<group-id>")
|
|
491
|
+
.requiredOption("--role <role>", "viewer, contributor, editor or manager (a group holds up to manager); owner restores the custodian's owner row")
|
|
577
492
|
.option("--app <app-id>", "App ID")
|
|
578
493
|
.option("--json", "Output as JSON")
|
|
579
|
-
.
|
|
494
|
+
.allowExcessArguments(false)
|
|
495
|
+
.action(async (collectionId, principalArg, options) => {
|
|
496
|
+
let principal;
|
|
497
|
+
let role;
|
|
498
|
+
try {
|
|
499
|
+
principal = parseCollectionPrincipal(principalArg);
|
|
500
|
+
role = parseCollectionRole(options.role);
|
|
501
|
+
}
|
|
502
|
+
catch (err) {
|
|
503
|
+
error(err.message);
|
|
504
|
+
process.exit(1);
|
|
505
|
+
}
|
|
580
506
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
581
507
|
const client = new ApiClient();
|
|
582
508
|
try {
|
|
583
|
-
const result = await client.addCollectionMember(resolvedAppId, collectionId, {
|
|
584
|
-
userId,
|
|
585
|
-
permission: options.permission,
|
|
586
|
-
});
|
|
509
|
+
const result = await client.addCollectionMember(resolvedAppId, collectionId, { ...principal, role: role });
|
|
587
510
|
if (options.json) {
|
|
588
511
|
json(result);
|
|
589
512
|
return;
|
|
590
513
|
}
|
|
591
|
-
success(
|
|
514
|
+
success(`${principalArg} holds ${result.role ?? role} on collection ${collectionId}.`);
|
|
515
|
+
}
|
|
516
|
+
catch (err) {
|
|
517
|
+
error(err.message);
|
|
518
|
+
process.exit(1);
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
members
|
|
522
|
+
.command("set-role")
|
|
523
|
+
.description("Change a collection member's role")
|
|
524
|
+
.argument("<collection-id>", "Collection ID")
|
|
525
|
+
.argument("<principal>", "A user id, or group:<group-id>")
|
|
526
|
+
.argument("<role>", "owner, manager, editor, contributor or viewer")
|
|
527
|
+
.option("--app <app-id>", "App ID")
|
|
528
|
+
.option("--json", "Output as JSON")
|
|
529
|
+
.allowExcessArguments(false)
|
|
530
|
+
.action(async (collectionId, principalArg, roleArg, options) => {
|
|
531
|
+
let role;
|
|
532
|
+
try {
|
|
533
|
+
parseCollectionPrincipal(principalArg);
|
|
534
|
+
role = parseCollectionRole(roleArg);
|
|
535
|
+
}
|
|
536
|
+
catch (err) {
|
|
537
|
+
error(err.message);
|
|
538
|
+
process.exit(1);
|
|
539
|
+
}
|
|
540
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
541
|
+
const client = new ApiClient();
|
|
542
|
+
try {
|
|
543
|
+
const result = await client.setCollectionMemberRole(resolvedAppId, collectionId, principalArg, role);
|
|
544
|
+
if (options.json) {
|
|
545
|
+
json(result);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
success(`${principalArg} is now ${result.role ?? role} on collection ${collectionId}.`);
|
|
592
549
|
}
|
|
593
550
|
catch (err) {
|
|
594
551
|
error(err.message);
|
|
595
552
|
process.exit(1);
|
|
596
553
|
}
|
|
597
554
|
});
|
|
598
|
-
// Remove member
|
|
599
555
|
members
|
|
600
556
|
.command("remove")
|
|
601
|
-
.description("Remove a user from a collection")
|
|
557
|
+
.description("Remove a user or a group from a collection")
|
|
602
558
|
.argument("<collection-id>", "Collection ID")
|
|
603
|
-
.argument("<
|
|
559
|
+
.argument("<principal>", "A user id, or group:<group-id>")
|
|
604
560
|
.option("--app <app-id>", "App ID")
|
|
605
561
|
.option("-y, --yes", "Skip confirmation prompt")
|
|
606
562
|
.option("--json", "Output as JSON")
|
|
607
|
-
.
|
|
563
|
+
.allowExcessArguments(false)
|
|
564
|
+
.action(async (collectionId, principalArg, options) => {
|
|
565
|
+
try {
|
|
566
|
+
parseCollectionPrincipal(principalArg);
|
|
567
|
+
}
|
|
568
|
+
catch (err) {
|
|
569
|
+
error(err.message);
|
|
570
|
+
process.exit(1);
|
|
571
|
+
}
|
|
608
572
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
609
573
|
if (!options.yes) {
|
|
610
574
|
let confirm;
|
|
611
575
|
try {
|
|
612
|
-
confirm = await confirmPrompt(`Remove
|
|
576
|
+
confirm = await confirmPrompt(`Remove ${principalArg} from collection ${collectionId}?`);
|
|
613
577
|
}
|
|
614
578
|
catch (err) {
|
|
615
579
|
error(err.message);
|
|
@@ -622,12 +586,12 @@ Ownership:
|
|
|
622
586
|
}
|
|
623
587
|
const client = new ApiClient();
|
|
624
588
|
try {
|
|
625
|
-
await client.removeCollectionMember(resolvedAppId, collectionId,
|
|
589
|
+
await client.removeCollectionMember(resolvedAppId, collectionId, principalArg);
|
|
626
590
|
if (options.json) {
|
|
627
|
-
json({ success: true, collectionId,
|
|
591
|
+
json({ success: true, collectionId, principal: principalArg });
|
|
628
592
|
return;
|
|
629
593
|
}
|
|
630
|
-
success(
|
|
594
|
+
success(`${principalArg} removed from collection.`);
|
|
631
595
|
}
|
|
632
596
|
catch (err) {
|
|
633
597
|
error(err.message);
|
|
@@ -648,12 +612,13 @@ Ownership:
|
|
|
648
612
|
* whole app. Owner and member identities travel as EMAILS wherever the server
|
|
649
613
|
* can resolve one: users are app-scoped (`models.yaml`), so the same person is
|
|
650
614
|
* a different userId in every app and a recorded source userId means nothing
|
|
651
|
-
* in the target.
|
|
615
|
+
* in the target. Custody and roles travel as they are: `app` or the owner,
|
|
616
|
+
* and each member's and group's role.
|
|
652
617
|
*/
|
|
653
618
|
function registerCollectionsExport(collections) {
|
|
654
619
|
collections
|
|
655
620
|
.command("export")
|
|
656
|
-
.description("Export every collection in an app (name,
|
|
621
|
+
.description("Export every collection in an app (name, custodian, members and groups with roles, documents)")
|
|
657
622
|
.option("--app <app-id>", "App ID")
|
|
658
623
|
.option("--output <dir>", "Output directory", "./primitive-export")
|
|
659
624
|
.option("--json", "Output as JSON")
|
|
@@ -665,6 +630,9 @@ that 'documents export-all' writes, so one directory is one app's migration.
|
|
|
665
630
|
Import it with 'primitive collections import' AFTER 'documents import', which
|
|
666
631
|
preserves document ids.
|
|
667
632
|
|
|
633
|
+
The file records each collection's custodian (the app, or the user holding
|
|
634
|
+
the owner row), its settings, and every member's and group's role.
|
|
635
|
+
|
|
668
636
|
Owner and member identities are recorded as emails wherever the server can
|
|
669
637
|
resolve one — users are app-scoped, so a source app's userId means nothing in
|
|
670
638
|
the target. A userId with no resolvable email is recorded as-is and will only
|
|
@@ -750,30 +718,34 @@ async function exportCollections(client, appId, options) {
|
|
|
750
718
|
const collectionId = row.collectionId;
|
|
751
719
|
try {
|
|
752
720
|
const documents = await readAllCollectionDocuments(client, appId, collectionId);
|
|
753
|
-
const
|
|
721
|
+
const roster = await client.listCollectionMembers(appId, collectionId);
|
|
754
722
|
const members = [];
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
723
|
+
const groups = [];
|
|
724
|
+
for (const m of roster.items) {
|
|
725
|
+
// `permission` and `groupType` stay beside `role`: a CLI written
|
|
726
|
+
// before roles reads them, and a group written before the role model
|
|
727
|
+
// resolves only with its type.
|
|
728
|
+
const permission = m.permission ?? permissionForRole(m.role);
|
|
729
|
+
if (m.groupId) {
|
|
730
|
+
// The server already leaves system groups out of the member list.
|
|
731
|
+
groups.push({ groupType: m.groupType, groupId: m.groupId, permission, role: m.role });
|
|
732
|
+
}
|
|
733
|
+
else if (m.userId) {
|
|
734
|
+
members.push({ ...(await identity(m.userId)), permission, role: m.role });
|
|
735
|
+
}
|
|
760
736
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
.filter((g) => !String(g.groupType).startsWith("_"))
|
|
765
|
-
.map((g) => ({
|
|
766
|
-
groupType: g.groupType,
|
|
767
|
-
groupId: g.groupId,
|
|
768
|
-
permission: g.permission,
|
|
769
|
-
}));
|
|
737
|
+
// A row created before custody has none (null); its creator holds the
|
|
738
|
+
// owner row and is recorded as the owner.
|
|
739
|
+
const ownerUserId = custodianUserId(rowCustodian(row));
|
|
770
740
|
exported.push({
|
|
771
741
|
collectionId,
|
|
772
742
|
name: row.name,
|
|
773
743
|
description: row.description ?? null,
|
|
774
744
|
collectionType: row.collectionType ?? null,
|
|
775
745
|
createdAt: row.createdAt ?? null,
|
|
776
|
-
|
|
746
|
+
custodian: typeof row.custodian === "string" && row.custodian ? row.custodian : null,
|
|
747
|
+
owner: ownerUserId ? await identity(ownerUserId) : null,
|
|
748
|
+
settings: row.settings ?? null,
|
|
777
749
|
documents,
|
|
778
750
|
members,
|
|
779
751
|
groups,
|
|
@@ -845,21 +817,34 @@ async function exportCollections(client, appId, options) {
|
|
|
845
817
|
* go first ON PURPOSE: `addDocument` materializes every existing grant
|
|
846
818
|
* onto a newly added document, so a fresh collection's access never
|
|
847
819
|
* depends on the grant's own fan-out over the document list (F4).
|
|
820
|
+
*
|
|
821
|
+
* A collection is created through the admin route with its custodian — the
|
|
822
|
+
* owner resolved by email, or the app — so the server writes the owner row;
|
|
823
|
+
* members and groups are then replayed by role through the admin member
|
|
824
|
+
* route (#4731).
|
|
848
825
|
*/
|
|
849
826
|
function registerCollectionsImport(collections) {
|
|
850
827
|
collections
|
|
851
828
|
.command("import")
|
|
852
|
-
.description("Recreate collections from a collections.json export (
|
|
829
|
+
.description("Recreate collections from a collections.json export (custodian, members and groups with roles, documents)")
|
|
853
830
|
.argument("<path>", "Export directory holding collections.json, or the file itself")
|
|
854
831
|
.option("--app <app-id>", "App ID")
|
|
855
|
-
.option("--owner <userId-or-email>", "
|
|
832
|
+
.option("--owner <userId-or-email>", "Give every imported collection this user as custodian and owner, instead of the custodian each one records")
|
|
856
833
|
.option("--overwrite", "Merge into a collection that already carries the same name")
|
|
857
834
|
.option("--dry-run", "Show the plan and the problems without writing anything")
|
|
858
835
|
.option("--json", "Output as JSON")
|
|
859
836
|
.addHelpText("after", `
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
837
|
+
Needs an admin token assigned to the app, checked before anything is read or
|
|
838
|
+
written: each collection is created through the admin API with the custodian
|
|
839
|
+
the file records — the app, or the owner, who holds the owner row — and
|
|
840
|
+
members and groups are added at the role the file records, through the admin
|
|
841
|
+
member routes. The custodian's own row follows the file too: a role below
|
|
842
|
+
owner is replayed, and a newly created collection whose file records no row
|
|
843
|
+
for its custodian has the owner row removed again. A member recorded as owner
|
|
844
|
+
who is not the custodian (with --owner, the recorded owner) is reported as an
|
|
845
|
+
'owner' problem and not imported. A file written before roles is read with
|
|
846
|
+
reader as viewer and read-write as editor, and its owner's own read-write row
|
|
847
|
+
as the owner row.
|
|
863
848
|
|
|
864
849
|
Run 'primitive documents import' FIRST: it preserves document ids, which is
|
|
865
850
|
what the collections file refers to.
|
|
@@ -869,16 +854,16 @@ app's userId means nothing here. A recorded userId is used only when the file
|
|
|
869
854
|
came from this same app.
|
|
870
855
|
|
|
871
856
|
Matching is by name. Without --overwrite a name already in the target is
|
|
872
|
-
skipped; with it, the collection is merged — but only when its
|
|
873
|
-
|
|
857
|
+
skipped; with it, the collection is merged — but only when its custodian and
|
|
858
|
+
type match, since neither can be changed by a merge.
|
|
874
859
|
A name more than one collection in the target carries is ambiguous: that
|
|
875
860
|
collection is refused as a 'collection' problem naming every matching id, with
|
|
876
861
|
or without --overwrite, and nothing is written for it. A name repeated inside
|
|
877
862
|
the file is refused for the second and later occurrences, since matching by
|
|
878
863
|
name could not tell them apart.
|
|
879
864
|
|
|
880
|
-
A group
|
|
881
|
-
the file records, and what the group can reach follows the change. On a
|
|
865
|
+
A group or member the target already holds at another role is UPDATED to the
|
|
866
|
+
role the file records, and what the group can reach follows the change. On a
|
|
882
867
|
collection holding more than 200 documents the server refuses a collection-wide
|
|
883
868
|
group change whole (COLLECTION_FANOUT_LIMIT): that is reported as a 'group'
|
|
884
869
|
problem, and the rest of the collection is still merged.
|
|
@@ -905,8 +890,39 @@ Exit code 1 when any per-item problem was reported. A skip is not a problem.
|
|
|
905
890
|
function isNotFound(err) {
|
|
906
891
|
return err?.statusCode === 404;
|
|
907
892
|
}
|
|
893
|
+
/**
|
|
894
|
+
* The recorded type of a grant's group, sent beside its id: a group written
|
|
895
|
+
* before the role model carries no public id, and resolves only by its type.
|
|
896
|
+
*/
|
|
897
|
+
function groupTypeOf(grant) {
|
|
898
|
+
return grant.groupType ? { groupType: grant.groupType } : {};
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* The import's writes all go through the admin API, so an app user's token —
|
|
902
|
+
* the app owner's included — could apply only part of a file (a merge's
|
|
903
|
+
* documents through the app route, and nothing else). Refused before any read
|
|
904
|
+
* of the target app (E14). The probe is one row of an admin-API read on the
|
|
905
|
+
* app, behind the same app-assignment gate as the admin create.
|
|
906
|
+
*/
|
|
907
|
+
async function requireAdminAppAccess(client, appId) {
|
|
908
|
+
try {
|
|
909
|
+
await client.listGroupsByCustodian(appId, "app", { limit: 1 });
|
|
910
|
+
}
|
|
911
|
+
catch (err) {
|
|
912
|
+
const status = err?.statusCode;
|
|
913
|
+
if (status === 401 || status === 403 || status === 404) {
|
|
914
|
+
throw new Error(`collections import needs an admin token assigned to app ${appId}: it ` +
|
|
915
|
+
`creates collections and replays members through the admin API ` +
|
|
916
|
+
`(${serverReason(err)}). Log in as an admin of the app and re-run. ` +
|
|
917
|
+
`Nothing has been written.`);
|
|
918
|
+
}
|
|
919
|
+
throw new Error(`Checking admin access to app ${appId} failed: ${err?.message ?? err}. ` +
|
|
920
|
+
`Nothing has been written.`);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
908
923
|
async function importCollections(client, appId, importPath, options) {
|
|
909
924
|
const file = readCollectionExportFile(importPath);
|
|
925
|
+
await requireAdminAppAccess(client, appId);
|
|
910
926
|
// A file exported before the platform removed collection `contextId` (#3926)
|
|
911
927
|
// still carries it. Said once for the run, on stderr, and never sent: the
|
|
912
928
|
// server refuses the key, and the binding belongs in a metadata category.
|
|
@@ -1033,7 +1049,9 @@ async function importCollections(client, appId, importPath, options) {
|
|
|
1033
1049
|
};
|
|
1034
1050
|
let ownerUserId = explicitOwnerUserId ?? null;
|
|
1035
1051
|
let ownerReason;
|
|
1036
|
-
|
|
1052
|
+
// App custody needs no owner; `--owner` gives every collection one.
|
|
1053
|
+
const appCustody = !explicitOwnerUserId && source.custodian === "app";
|
|
1054
|
+
if (!ownerUserId && !appCustody) {
|
|
1037
1055
|
try {
|
|
1038
1056
|
const resolved = await resolveIdentity(source.owner);
|
|
1039
1057
|
ownerUserId = resolved.userId;
|
|
@@ -1047,6 +1065,7 @@ async function importCollections(client, appId, importPath, options) {
|
|
|
1047
1065
|
const row = planCollection(source, {
|
|
1048
1066
|
ownerUserId,
|
|
1049
1067
|
ownerReason,
|
|
1068
|
+
appCustody,
|
|
1050
1069
|
existing: matches.length === 1 ? matches[0] : null,
|
|
1051
1070
|
ambiguousTargetIds: matches.length > 1 ? matches.map((m) => m.collectionId) : null,
|
|
1052
1071
|
duplicateOfSourceId: duplicates.get(source.collectionId) ?? null,
|
|
@@ -1058,33 +1077,36 @@ async function importCollections(client, appId, importPath, options) {
|
|
|
1058
1077
|
planned.push({ row, source, grants: [], members: [], documents: [] });
|
|
1059
1078
|
continue;
|
|
1060
1079
|
}
|
|
1061
|
-
// What the target already grants, so a merge can compare
|
|
1080
|
+
// What the target already grants, so a merge can compare roles.
|
|
1062
1081
|
let targetGrants = [];
|
|
1063
1082
|
if (row.action === "merge" && row.targetCollectionId) {
|
|
1064
1083
|
try {
|
|
1065
|
-
const
|
|
1066
|
-
targetGrants =
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1084
|
+
const roster = await client.listCollectionMembers(appId, row.targetCollectionId);
|
|
1085
|
+
targetGrants = roster.items
|
|
1086
|
+
.filter((m) => m.groupId)
|
|
1087
|
+
.map((m) => ({
|
|
1088
|
+
groupId: m.groupId,
|
|
1089
|
+
...(m.groupType ? { groupType: m.groupType } : {}),
|
|
1090
|
+
role: m.role,
|
|
1070
1091
|
}));
|
|
1071
1092
|
}
|
|
1072
1093
|
catch (err) {
|
|
1073
|
-
fatal(`the
|
|
1094
|
+
fatal(`the members of ${row.targetCollectionId}`, err);
|
|
1074
1095
|
}
|
|
1075
1096
|
}
|
|
1076
1097
|
const grants = [];
|
|
1077
1098
|
for (const grant of source.groups ?? []) {
|
|
1078
|
-
const label =
|
|
1079
|
-
// The server reserves the `_` prefix for its own system
|
|
1080
|
-
// export never writes one, and re-granting one is a 400.
|
|
1081
|
-
|
|
1082
|
-
|
|
1099
|
+
const label = `group:${grant.groupId}`;
|
|
1100
|
+
// The server reserves the `_` group-type prefix for its own system
|
|
1101
|
+
// groups; an export never writes one, and re-granting one is a 400. A
|
|
1102
|
+
// group id may start with `_` (only the type is reserved).
|
|
1103
|
+
if (String(grant.groupType ?? "").startsWith("_")) {
|
|
1104
|
+
warn(`${source.name}: skipping the reserved group ${grant.groupType ? `type ${grant.groupType} ` : ""}` +
|
|
1083
1105
|
`(${label}); the server manages those itself.`);
|
|
1084
1106
|
continue;
|
|
1085
1107
|
}
|
|
1086
1108
|
try {
|
|
1087
|
-
await client.getGroup(appId, grant.
|
|
1109
|
+
await client.getGroup(appId, grant.groupId, groupTypeOf(grant));
|
|
1088
1110
|
}
|
|
1089
1111
|
catch (err) {
|
|
1090
1112
|
if (isNotFound(err)) {
|
|
@@ -1094,14 +1116,16 @@ async function importCollections(client, appId, importPath, options) {
|
|
|
1094
1116
|
fatal(`group ${label}`, err);
|
|
1095
1117
|
}
|
|
1096
1118
|
const disposition = grantDisposition(grant, targetGrants);
|
|
1097
|
-
const existing = targetGrants.find((g) => g
|
|
1119
|
+
const existing = targetGrants.find((g) => sameGrantGroup(g, grant));
|
|
1098
1120
|
grants.push({
|
|
1099
1121
|
grant,
|
|
1100
1122
|
disposition,
|
|
1101
|
-
...(existing ? {
|
|
1123
|
+
...(existing ? { existingRole: existing.role } : {}),
|
|
1102
1124
|
});
|
|
1103
1125
|
}
|
|
1126
|
+
const custodianUser = row.custodian ? custodianUserId(row.custodian) : null;
|
|
1104
1127
|
const members = [];
|
|
1128
|
+
let custodianRecorded = false;
|
|
1105
1129
|
for (const member of source.members ?? []) {
|
|
1106
1130
|
let resolved;
|
|
1107
1131
|
try {
|
|
@@ -1115,8 +1139,32 @@ async function importCollections(client, appId, importPath, options) {
|
|
|
1115
1139
|
note("member", resolved.id, resolved.reason);
|
|
1116
1140
|
continue;
|
|
1117
1141
|
}
|
|
1118
|
-
|
|
1142
|
+
if (resolved.userId === custodianUser) {
|
|
1143
|
+
// The custodian's own row is replayed as the file records it: a role
|
|
1144
|
+
// below owner demotes the owner row custody wrote, and on a merge an
|
|
1145
|
+
// owner entry restores it. A create's owner entry is already written.
|
|
1146
|
+
custodianRecorded = true;
|
|
1147
|
+
if (row.action === "create" && member.role === "owner")
|
|
1148
|
+
continue;
|
|
1149
|
+
members.push({ userId: resolved.userId, role: member.role });
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
if (member.role === "owner") {
|
|
1153
|
+
// The owner row is the custodian's alone; anyone else recorded as
|
|
1154
|
+
// owner is invalid data, reported rather than granted another role.
|
|
1155
|
+
note("owner", resolved.id, `recorded as owner, but the owner row belongs to the custodian ` +
|
|
1156
|
+
`(${row.custodian}); not imported`);
|
|
1157
|
+
continue;
|
|
1158
|
+
}
|
|
1159
|
+
members.push({ userId: resolved.userId, role: member.role });
|
|
1119
1160
|
}
|
|
1161
|
+
// A user custodian the file records no row for had it removed in the
|
|
1162
|
+
// source; a create writes one, so it goes again. `--owner` names a new
|
|
1163
|
+
// custodian the file cannot speak for, and a merge leaves rows it does not
|
|
1164
|
+
// record alone.
|
|
1165
|
+
const removeOwnerRowOf = row.action === "create" && custodianUser && !custodianRecorded && !explicitOwnerUserId
|
|
1166
|
+
? custodianUser
|
|
1167
|
+
: undefined;
|
|
1120
1168
|
const documents = [];
|
|
1121
1169
|
for (const document of source.documents ?? []) {
|
|
1122
1170
|
try {
|
|
@@ -1132,7 +1180,7 @@ async function importCollections(client, appId, importPath, options) {
|
|
|
1132
1180
|
}
|
|
1133
1181
|
documents.push(document.documentId);
|
|
1134
1182
|
}
|
|
1135
|
-
planned.push({ row, source, grants, members, documents });
|
|
1183
|
+
planned.push({ row, source, grants, members, ...(removeOwnerRowOf ? { removeOwnerRowOf } : {}), documents });
|
|
1136
1184
|
}
|
|
1137
1185
|
// ── Apply phase ────────────────────────────────────────────────────────
|
|
1138
1186
|
for (const entry of planned) {
|
|
@@ -1167,38 +1215,41 @@ async function importCollections(client, appId, importPath, options) {
|
|
|
1167
1215
|
summary.merged += 1;
|
|
1168
1216
|
info(`[dry-run] Would merge ${source.name} into ${row.targetCollectionId}`);
|
|
1169
1217
|
}
|
|
1170
|
-
for (const { grant, disposition,
|
|
1218
|
+
for (const { grant, disposition, existingRole } of entry.grants) {
|
|
1171
1219
|
if (disposition === "already-applied")
|
|
1172
1220
|
continue;
|
|
1173
|
-
const label =
|
|
1221
|
+
const label = `group:${grant.groupId}`;
|
|
1174
1222
|
info(disposition === "update"
|
|
1175
|
-
? `[dry-run] Would update ${grantUpdateLine(label,
|
|
1176
|
-
: `[dry-run] Would grant ${label} ${grant.
|
|
1223
|
+
? `[dry-run] Would update ${grantUpdateLine(label, existingRole, grant.role)}`
|
|
1224
|
+
: `[dry-run] Would grant ${label} ${grant.role}`);
|
|
1177
1225
|
summary.groupsGranted += 1;
|
|
1178
1226
|
}
|
|
1179
1227
|
summary.membersAdded += entry.members.length;
|
|
1228
|
+
if (entry.removeOwnerRowOf) {
|
|
1229
|
+
info(`[dry-run] Would remove the owner row of ${entry.removeOwnerRowOf} ` +
|
|
1230
|
+
`(the export records no membership for the custodian)`);
|
|
1231
|
+
}
|
|
1180
1232
|
summary.documentsAdded += entry.documents.length;
|
|
1181
1233
|
continue;
|
|
1182
1234
|
}
|
|
1183
1235
|
let targetCollectionId = row.targetCollectionId;
|
|
1184
1236
|
if (row.action === "create") {
|
|
1185
1237
|
try {
|
|
1186
|
-
const created = await client.
|
|
1238
|
+
const created = await client.createCollectionAsAdmin(appId, {
|
|
1187
1239
|
name: source.name,
|
|
1188
1240
|
...(source.description ? { description: source.description } : {}),
|
|
1189
1241
|
...(source.collectionType ? { collectionType: source.collectionType } : {}),
|
|
1190
|
-
|
|
1242
|
+
custodian: row.custodian,
|
|
1243
|
+
...(source.settings ? { settings: source.settings } : {}),
|
|
1191
1244
|
});
|
|
1192
1245
|
targetCollectionId = created.collectionId;
|
|
1193
1246
|
row.targetCollectionId = targetCollectionId;
|
|
1194
1247
|
summary.created += 1;
|
|
1195
1248
|
info(`Created ${source.name} as ${targetCollectionId}`);
|
|
1196
|
-
//
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
`${created.createdBy}); the token is not an admin one, or the ` +
|
|
1201
|
-
`server predates #3641`);
|
|
1249
|
+
// The custodian the server RECORDED is the answer.
|
|
1250
|
+
if (created.custodian && created.custodian !== row.custodian) {
|
|
1251
|
+
note("owner", row.custodian, `the server recorded ${created.custodian} as custodian, not ` +
|
|
1252
|
+
`${row.custodian}`);
|
|
1202
1253
|
}
|
|
1203
1254
|
}
|
|
1204
1255
|
catch (err) {
|
|
@@ -1210,21 +1261,21 @@ async function importCollections(client, appId, importPath, options) {
|
|
|
1210
1261
|
summary.merged += 1;
|
|
1211
1262
|
info(`Merged ${source.name} into ${targetCollectionId}`);
|
|
1212
1263
|
}
|
|
1213
|
-
for (const { grant, disposition,
|
|
1264
|
+
for (const { grant, disposition, existingRole } of entry.grants) {
|
|
1214
1265
|
if (disposition === "already-applied")
|
|
1215
1266
|
continue;
|
|
1216
|
-
const label =
|
|
1267
|
+
const label = `group:${grant.groupId}`;
|
|
1217
1268
|
try {
|
|
1218
|
-
await client.
|
|
1219
|
-
groupType: grant.groupType,
|
|
1269
|
+
await client.addCollectionMember(appId, targetCollectionId, {
|
|
1220
1270
|
groupId: grant.groupId,
|
|
1221
|
-
|
|
1271
|
+
...groupTypeOf(grant),
|
|
1272
|
+
role: grant.role,
|
|
1222
1273
|
});
|
|
1223
1274
|
summary.groupsGranted += 1;
|
|
1224
|
-
// A
|
|
1275
|
+
// A role change is worth saying out loud: it is the one grant write
|
|
1225
1276
|
// that can take access away from people who already had it.
|
|
1226
1277
|
if (disposition === "update") {
|
|
1227
|
-
info(`Updated ${grantUpdateLine(label,
|
|
1278
|
+
info(`Updated ${grantUpdateLine(label, existingRole, grant.role)}`);
|
|
1228
1279
|
}
|
|
1229
1280
|
}
|
|
1230
1281
|
catch (err) {
|
|
@@ -1235,10 +1286,9 @@ async function importCollections(client, appId, importPath, options) {
|
|
|
1235
1286
|
try {
|
|
1236
1287
|
const result = await client.addCollectionMember(appId, targetCollectionId, {
|
|
1237
1288
|
userId: member.userId,
|
|
1238
|
-
|
|
1289
|
+
role: member.role,
|
|
1239
1290
|
});
|
|
1240
|
-
// `already_member` is the server saying it
|
|
1241
|
-
// the owner's own `_col-writer` enrolment, which every export records.
|
|
1291
|
+
// `already_member` is the server saying it already held that role.
|
|
1242
1292
|
if (result?.status !== "already_member")
|
|
1243
1293
|
summary.membersAdded += 1;
|
|
1244
1294
|
}
|
|
@@ -1246,6 +1296,16 @@ async function importCollections(client, appId, importPath, options) {
|
|
|
1246
1296
|
note("member", member.userId, serverReason(err));
|
|
1247
1297
|
}
|
|
1248
1298
|
}
|
|
1299
|
+
if (entry.removeOwnerRowOf) {
|
|
1300
|
+
try {
|
|
1301
|
+
await client.removeCollectionMember(appId, targetCollectionId, entry.removeOwnerRowOf);
|
|
1302
|
+
info(`Removed the owner row of ${entry.removeOwnerRowOf} from ${source.name}: ` +
|
|
1303
|
+
`the export records no membership for the custodian`);
|
|
1304
|
+
}
|
|
1305
|
+
catch (err) {
|
|
1306
|
+
note("owner", entry.removeOwnerRowOf, serverReason(err));
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1249
1309
|
for (const documentId of entry.documents) {
|
|
1250
1310
|
try {
|
|
1251
1311
|
await client.addCollectionDocument(appId, targetCollectionId, { documentId });
|