@tailor-platform/sdk 2.0.0-next.4 → 2.0.0-next.6
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/CHANGELOG.md +135 -0
- package/dist/{application-BX13RArL.mjs → application-BJXRpQj5.mjs} +602 -1842
- package/dist/application-BJXRpQj5.mjs.map +1 -0
- package/dist/application-BV-AXawv.mjs +3 -0
- package/dist/cli/cache/bundle-cache.d.mts +2 -1
- package/dist/cli/cache/store.d.mts +1 -1
- package/dist/cli/commands/api/index.d.mts +1 -0
- package/dist/cli/commands/deploy/aigateway.d.mts +5 -1
- package/dist/cli/commands/deploy/application.d.mts +7 -1
- package/dist/cli/commands/deploy/apply-phases.d.mts +14 -1
- package/dist/cli/commands/deploy/auth.d.mts +5 -1
- package/dist/cli/commands/deploy/deploy.d.mts +13 -3
- package/dist/cli/commands/deploy/executor.d.mts +7 -1
- package/dist/cli/commands/deploy/function-registry.d.mts +5 -0
- package/dist/cli/commands/deploy/idp.d.mts +5 -1
- package/dist/cli/commands/deploy/resolver.d.mts +5 -1
- package/dist/cli/commands/deploy/secret-manager.d.mts +5 -1
- package/dist/cli/commands/deploy/staticwebsite.d.mts +5 -1
- package/dist/cli/commands/deploy/tailordb/index.d.mts +8 -1
- package/dist/cli/commands/deploy/types.d.mts +3 -1
- package/dist/cli/commands/deploy/workflow-execution-policy.d.mts +3 -1
- package/dist/cli/commands/deploy/workflow.d.mts +4 -1
- package/dist/cli/commands/executor/get.d.mts +0 -1
- package/dist/cli/commands/executor/jobs.d.mts +0 -1
- package/dist/cli/commands/executor/list.d.mts +0 -1
- package/dist/cli/commands/executor/transform.d.mts +1 -0
- package/dist/cli/commands/executor/trigger.d.mts +0 -1
- package/dist/cli/commands/executor/webhook.d.mts +0 -1
- package/dist/cli/commands/function/get.d.mts +0 -1
- package/dist/cli/commands/function/list.d.mts +0 -1
- package/dist/cli/commands/function/transform.d.mts +1 -0
- package/dist/cli/commands/generate/options.d.mts +0 -1
- package/dist/cli/commands/generate/service.d.mts +6 -2
- package/dist/cli/commands/machineuser/list.d.mts +0 -1
- package/dist/cli/commands/machineuser/token.d.mts +0 -1
- package/dist/cli/commands/oauth2client/get.d.mts +0 -1
- package/dist/cli/commands/oauth2client/list.d.mts +0 -1
- package/dist/cli/commands/oauth2client/transform.d.mts +1 -0
- package/dist/cli/commands/organization/folder/create.d.mts +0 -1
- package/dist/cli/commands/organization/folder/delete.d.mts +0 -1
- package/dist/cli/commands/organization/folder/get.d.mts +0 -1
- package/dist/cli/commands/organization/folder/list.d.mts +0 -1
- package/dist/cli/commands/organization/folder/update.d.mts +0 -1
- package/dist/cli/commands/organization/get.d.mts +0 -1
- package/dist/cli/commands/organization/list.d.mts +0 -1
- package/dist/cli/commands/organization/transform.d.mts +2 -0
- package/dist/cli/commands/organization/tree.d.mts +0 -1
- package/dist/cli/commands/organization/update.d.mts +0 -1
- package/dist/cli/commands/remove.d.mts +0 -1
- package/dist/cli/commands/show.d.mts +9 -4
- package/dist/cli/commands/staticwebsite/deploy.d.mts +19 -0
- package/dist/cli/commands/tailordb/migrate/config.d.mts +0 -1
- package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +16 -3
- package/dist/cli/commands/tailordb/migrate/generate.d.mts +0 -1
- package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +6 -0
- package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +1 -1
- package/dist/cli/commands/tailordb/truncate.d.mts +0 -1
- package/dist/cli/commands/workflow/executions.d.mts +0 -1
- package/dist/cli/commands/workflow/get.d.mts +2 -1
- package/dist/cli/commands/workflow/list.d.mts +0 -1
- package/dist/cli/commands/workflow/resume.d.mts +0 -1
- package/dist/cli/commands/workflow/start.d.mts +0 -1
- package/dist/cli/commands/workflow/status.d.mts +1 -0
- package/dist/cli/commands/workflow/transform.d.mts +1 -0
- package/dist/cli/commands/workflow/wait.d.mts +0 -1
- package/dist/cli/commands/workflow/waiter.d.mts +1 -1
- package/dist/cli/commands/workspace/app/health.d.mts +0 -1
- package/dist/cli/commands/workspace/app/list.d.mts +0 -1
- package/dist/cli/commands/workspace/app/transform.d.mts +2 -0
- package/dist/cli/commands/workspace/create.d.mts +3 -1
- package/dist/cli/commands/workspace/delete.d.mts +0 -1
- package/dist/cli/commands/workspace/get.d.mts +0 -1
- package/dist/cli/commands/workspace/list.d.mts +1 -1
- package/dist/cli/commands/workspace/restore.d.mts +0 -1
- package/dist/cli/commands/workspace/transform.d.mts +4 -0
- package/dist/cli/commands/workspace/user/invite.d.mts +0 -1
- package/dist/cli/commands/workspace/user/list.d.mts +0 -1
- package/dist/cli/commands/workspace/user/remove.d.mts +0 -1
- package/dist/cli/commands/workspace/user/transform.d.mts +1 -0
- package/dist/cli/commands/workspace/user/update.d.mts +0 -1
- package/dist/cli/index.d.mts +6 -3
- package/dist/cli/index.mjs +871 -1577
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +10 -4
- package/dist/cli/lib.mjs +50 -4
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/query/index.d.mts +0 -1
- package/dist/cli/services/application.d.mts +12 -1
- package/dist/cli/services/auth/service.d.mts +2 -0
- package/dist/cli/services/executor/service.d.mts +1 -1
- package/dist/cli/services/http-adapter/bundler.d.mts +3 -1
- package/dist/cli/services/http-adapter/service.d.mts +2 -1
- package/dist/cli/services/resolver/service.d.mts +1 -1
- package/dist/cli/services/tailordb/service.d.mts +3 -1
- package/dist/cli/services/workflow/bundler.d.mts +2 -1
- package/dist/cli/services/workflow/service.d.mts +1 -1
- package/dist/cli/shared/args.d.mts +1 -1
- package/dist/cli/shared/client.d.mts +1 -1
- package/dist/cli/shared/config-loader.d.mts +1 -2
- package/dist/cli/shared/context.d.mts +2 -1
- package/dist/cli/shared/readonly-guard.d.mts +25 -0
- package/dist/cli/shared/script-executor.d.mts +0 -1
- package/dist/cli/shared/seed-chunker.d.mts +7 -3
- package/dist/cli/shared/tailordb-namespaces.d.mts +37 -0
- package/dist/cli/shared/type-generator.d.mts +0 -1
- package/dist/cli/shared/workspace-name.d.mts +1 -0
- package/dist/completion/zsh-worker.zsh +15 -303
- package/dist/configure/config/index.d.mts +0 -1
- package/dist/configure/config/types.d.mts +2 -2
- package/dist/configure/index.d.mts +14 -4
- package/dist/configure/index.mjs +201 -6
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/aigateway/index.d.mts +0 -1
- package/dist/configure/services/aigateway/types.d.mts +0 -1
- package/dist/configure/services/auth/index.d.mts +0 -1
- package/dist/configure/services/auth/types.d.mts +15 -14
- package/dist/configure/services/executor/executor.d.mts +2 -13
- package/dist/configure/services/executor/index.d.mts +1 -0
- package/dist/configure/services/executor/operation.d.mts +9 -4
- package/dist/configure/services/executor/trigger/event.d.mts +0 -1
- package/dist/configure/services/executor/trigger/index.d.mts +0 -1
- package/dist/configure/services/executor/trigger/schedule.d.mts +0 -1
- package/dist/configure/services/executor/trigger/webhook.d.mts +0 -1
- package/dist/configure/services/http-adapter/http-adapter.d.mts +1 -2
- package/dist/configure/services/idp/index.d.mts +1 -5
- package/dist/configure/services/idp/permission.d.mts +4 -5
- package/dist/configure/services/idp/types.d.mts +0 -1
- package/dist/configure/services/index.d.mts +5 -0
- package/dist/configure/services/resolver/resolver.d.mts +1 -0
- package/dist/configure/services/resolver/types.d.mts +0 -1
- package/dist/configure/services/secrets/index.d.mts +0 -1
- package/dist/configure/services/secrets/types.d.mts +0 -1
- package/dist/configure/services/staticwebsite/index.d.mts +0 -1
- package/dist/configure/services/staticwebsite/types.d.mts +0 -1
- package/dist/configure/services/tailordb/permission.d.mts +4 -5
- package/dist/configure/services/tailordb/schema.d.mts +86 -56
- package/dist/configure/services/tailordb/types.d.mts +66 -17
- package/dist/configure/services/workflow/execution-policy.d.mts +2 -3
- package/dist/configure/services/workflow/execution-policy.types.d.mts +0 -1
- package/dist/configure/services/workflow/index.d.mts +1 -0
- package/dist/configure/services/workflow/job.d.mts +0 -1
- package/dist/configure/services/workflow/wait-point.d.mts +0 -1
- package/dist/configure/services/workflow/workflow.d.mts +1 -1
- package/dist/configure/types/field.d.mts +1 -2
- package/dist/configure/types/field.types.d.mts +6 -45
- package/dist/configure/types/index.d.mts +1 -0
- package/dist/configure/types/type.d.mts +2 -11
- package/dist/{crashreport-D3RjuSYb.mjs → crashreport-0ioNRZ4K.mjs} +1 -1
- package/dist/{crashreport-Cg-pqRJV.mjs → crashreport-CxHne11E.mjs} +4 -3
- package/dist/{crashreport-Cg-pqRJV.mjs.map → crashreport-CxHne11E.mjs.map} +1 -1
- package/dist/errors-DfnF4ibq.mjs +100 -0
- package/dist/errors-DfnF4ibq.mjs.map +1 -0
- package/dist/{globals-BjhJJ2dN.mjs → globals-D-YbJKW-.mjs} +16 -10
- package/dist/globals-D-YbJKW-.mjs.map +1 -0
- package/dist/kysely/index.d.mts +3 -4
- package/dist/{kysely-type-D-zflMGf.mjs → kysely-type-DqIRgDYR.mjs} +4 -3
- package/dist/kysely-type-DqIRgDYR.mjs.map +1 -0
- package/dist/{logger-BEiZZ3qT.mjs → logger-BwS4ppwO.mjs} +3 -3
- package/dist/logger-BwS4ppwO.mjs.map +1 -0
- package/dist/parser/service/auth/index.d.mts +1 -1
- package/dist/parser/service/auth/schema.d.mts +2 -0
- package/dist/parser/service/http-adapter/index.d.mts +1 -1
- package/dist/parser/service/idp/types.d.mts +1 -0
- package/dist/parser/service/tailordb/type-source.d.mts +10 -0
- package/dist/parser/service/tailordb/types.d.mts +6 -1
- package/dist/plugin/builtin/enum-constants/index.d.mts +0 -1
- package/dist/plugin/builtin/file-utils/index.d.mts +0 -1
- package/dist/plugin/builtin/kysely-type/index.d.mts +0 -1
- package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
- package/dist/plugin/builtin/seed/index.d.mts +0 -1
- package/dist/plugin/get-generated-type.d.mts +0 -1
- package/dist/plugin/manager.d.mts +2 -1
- package/dist/plugin/types.d.mts +0 -1
- package/dist/plugin/with-context.d.mts +0 -1
- package/dist/{register-ts-hook-BM1caLT0.mjs → register-ts-hook-DL31O2W9.mjs} +3076 -1744
- package/dist/register-ts-hook-DL31O2W9.mjs.map +1 -0
- package/dist/{registry-BSbq6SPo.mjs → registry-CC3CbQiF.mjs} +3 -3
- package/dist/registry-CC3CbQiF.mjs.map +1 -0
- package/dist/runtime/aigateway.d.mts +0 -1
- package/dist/runtime/authconnection.d.mts +0 -1
- package/dist/runtime/context.d.mts +0 -1
- package/dist/runtime/field-parse.d.mts +10 -0
- package/dist/runtime/globals.d.mts +5 -3
- package/dist/runtime/index.d.mts +0 -1
- package/dist/runtime/index.mjs +1 -1
- package/dist/runtime/types.d.mts +9 -4
- package/dist/runtime/workflow.d.mts +70 -10
- package/dist/runtime/workflow.mjs +1 -1
- package/dist/{schema-C4LYbXVG.mjs → schema--xYWRGfe.mjs} +272 -51
- package/dist/schema--xYWRGfe.mjs.map +1 -0
- package/dist/{secret-file-aoNXJmKa.mjs → secret-file-C3J4Gy8r.mjs} +2 -27
- package/dist/secret-file-C3J4Gy8r.mjs.map +1 -0
- package/dist/{secretmanager-Bd45j7an.mjs → secretmanager-IY4UvinW.mjs} +42 -9
- package/dist/secretmanager-IY4UvinW.mjs.map +1 -0
- package/dist/seed/index.d.mts +3 -2
- package/dist/{service-ufH7sS8o.mjs → service-CnHz9rwz.mjs} +47 -20
- package/dist/service-CnHz9rwz.mjs.map +1 -0
- package/dist/{service-DeZeYa06.mjs → service-DaeJlBoH.mjs} +3 -3
- package/dist/{service-DeZeYa06.mjs.map → service-DaeJlBoH.mjs.map} +1 -1
- package/dist/service-nU6ITOHL.mjs +3 -0
- package/dist/service_pb-BoLM0X4K.mjs +167 -0
- package/dist/service_pb-BoLM0X4K.mjs.map +1 -0
- package/dist/{service_pb-B2FF59sP.mjs → service_pb-r1T7kpJc.mjs} +1 -1
- package/dist/tailor-proto/src/tailor/v1/aigateway_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/aigateway_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/application_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/application_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/auth_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/auth_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/events_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/events_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/executor_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/executor_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/function_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/function_registry_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/function_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/http_adapter_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/idp_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/idp_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/metadata_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/metadata_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/meter_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/pipeline_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/pipeline_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/secret_manager_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/secret_manager_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/service_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/stateflow_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/stateflow_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/staticwebsite_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/staticwebsite_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/tailordb_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/tailordb_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/telemetryrouter_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/telemetryrouter_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/workflow_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/workflow_resource_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/workspace_pb.d.mts +0 -1
- package/dist/tailor-proto/src/tailor/v1/workspace_resource_pb.d.mts +0 -1
- package/dist/types/aigateway.generated.d.mts +5 -2
- package/dist/types/auth-connection.generated.d.mts +24 -11
- package/dist/types/auth.generated.d.mts +115 -84
- package/dist/types/executor.generated.d.mts +133 -123
- package/dist/types/helpers.d.mts +7 -6
- package/dist/types/http-adapter.generated.d.mts +11 -5
- package/dist/types/idp.generated.d.mts +536 -524
- package/dist/types/resolver.generated.d.mts +26 -11
- package/dist/types/staticwebsite.generated.d.mts +7 -3
- package/dist/types/tailordb.generated.d.mts +57 -29
- package/dist/types/workflow.generated.d.mts +9 -4
- package/dist/user-agent-Bgsszb5I.mjs +28 -0
- package/dist/user-agent-Bgsszb5I.mjs.map +1 -0
- package/dist/utils/test/index.d.mts +1 -2
- package/dist/utils/test/index.mjs +36 -10
- package/dist/utils/test/index.mjs.map +1 -1
- package/dist/utils/test/mock.d.mts +0 -1
- package/dist/vitest/environment.mjs +1 -1
- package/dist/vitest/index.d.mts +9 -9
- package/dist/vitest/index.mjs +591 -173
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mock-kysely.d.mts +0 -1
- package/dist/vitest/mock.d.mts +8 -8
- package/dist/vitest/mocks/aigateway.d.mts +16 -6
- package/dist/vitest/mocks/authconnection.d.mts +16 -6
- package/dist/vitest/mocks/file.d.mts +23 -6
- package/dist/vitest/mocks/iconv.d.mts +28 -4
- package/dist/vitest/mocks/idp.d.mts +26 -10
- package/dist/vitest/mocks/secretmanager.d.mts +16 -5
- package/dist/vitest/mocks/tailordb.d.mts +54 -10
- package/dist/vitest/mocks/workflow.d.mts +72 -21
- package/dist/vitest/setup.mjs +2 -2
- package/dist/vitest/workflow-local.d.mts +1 -0
- package/dist/workflow-9kHGKxF2.mjs +64 -0
- package/dist/workflow-9kHGKxF2.mjs.map +1 -0
- package/dist/{service_pb-B5w9rjPY.mjs → workspace_resource_pb-Db3fv68L.mjs} +57 -165
- package/dist/workspace_resource_pb-Db3fv68L.mjs.map +1 -0
- package/docs/cli/application.md +61 -15
- package/docs/cli/setup.md +1 -1
- package/docs/cli/tailordb.md +8 -140
- package/docs/cli/workspace.md +6 -4
- package/docs/cli-reference.md +18 -14
- package/docs/github-actions.md +8 -0
- package/docs/migration/v2.md +247 -0
- package/docs/runtime.md +5 -6
- package/docs/services/aigateway.md +2 -0
- package/docs/services/executor.md +5 -0
- package/docs/services/tailordb.md +144 -68
- package/docs/services/workflow.md +3 -3
- package/docs/testing.md +105 -89
- package/package.json +22 -25
- package/dist/application-BO4ZF8wP.mjs +0 -3
- package/dist/application-BX13RArL.mjs.map +0 -1
- package/dist/cli/erd-viewer-assets/app.js +0 -1424
- package/dist/cli/erd-viewer-assets/index.html +0 -77
- package/dist/cli/erd-viewer-assets/serve.json +0 -13
- package/dist/cli/erd-viewer-assets/styles.css +0 -1036
- package/dist/cli/shared/trigger-context.d.mts +0 -1
- package/dist/configure/types/field-runtime.d.mts +0 -17
- package/dist/errors-118wUDRr.mjs +0 -42
- package/dist/errors-118wUDRr.mjs.map +0 -1
- package/dist/field-runtime-CAnh_oow.mjs +0 -183
- package/dist/field-runtime-CAnh_oow.mjs.map +0 -1
- package/dist/globals-BjhJJ2dN.mjs.map +0 -1
- package/dist/kysely-type-D-zflMGf.mjs.map +0 -1
- package/dist/logger-BEiZZ3qT.mjs.map +0 -1
- package/dist/register-ts-hook-BM1caLT0.mjs.map +0 -1
- package/dist/registry-BSbq6SPo.mjs.map +0 -1
- package/dist/resource_pb-BRv7AMXK.mjs +0 -49
- package/dist/resource_pb-BRv7AMXK.mjs.map +0 -1
- package/dist/schema-C4LYbXVG.mjs.map +0 -1
- package/dist/secret-file-aoNXJmKa.mjs.map +0 -1
- package/dist/secretmanager-Bd45j7an.mjs.map +0 -1
- package/dist/service-CkIwEctX.mjs +0 -3
- package/dist/service-ufH7sS8o.mjs.map +0 -1
- package/dist/service_pb-B5w9rjPY.mjs.map +0 -1
- package/dist/types-C-lQ4se3.mjs +0 -4
- package/dist/types-CXF2OYdR.mjs +0 -199
- package/dist/types-CXF2OYdR.mjs.map +0 -1
- package/dist/workflow-GNuSnsTx.mjs +0 -22
- package/dist/workflow-GNuSnsTx.mjs.map +0 -1
|
@@ -1,291 +1,31 @@
|
|
|
1
1
|
import { n as isSdkBranded } from "./brand-Eo4pLXPJ.mjs";
|
|
2
|
-
import { i as PageDirection } from "./resource_pb-BRv7AMXK.mjs";
|
|
3
2
|
import { t as assertDefined } from "./assert-DBxo8jPo.mjs";
|
|
4
|
-
import { a as parseBoolean, n as logger, r as styles } from "./logger-
|
|
5
|
-
import { a as AuthConfigSchema, c as stripTailorDBTypeBuilderHelpers, d as applyRelationMetadataToFieldConfig, f as buildRelationInfo, g as functionSchema, h as loadFilesWithIgnores, l as TailorDBServiceConfigSchema, m as validateRelationConfig, n as loadExecutor, o as AuthInvokerSchema, p as processRelationMetadata, s as TailorFieldSchema, t as createExecutorService, u as TailorDBTypeSchema } from "./service-
|
|
3
|
+
import { a as parseBoolean, n as logger, r as styles } from "./logger-BwS4ppwO.mjs";
|
|
4
|
+
import { a as AuthConfigSchema, c as stripTailorDBTypeBuilderHelpers, d as applyRelationMetadataToFieldConfig, f as buildRelationInfo, g as functionSchema, h as loadFilesWithIgnores, l as TailorDBServiceConfigSchema, m as validateRelationConfig, n as loadExecutor, o as AuthInvokerSchema, p as processRelationMetadata, s as TailorFieldSchema, t as createExecutorService, u as TailorDBTypeSchema } from "./service-CnHz9rwz.mjs";
|
|
6
5
|
import { t as multiline } from "./multiline-sfHpTZZK.mjs";
|
|
7
|
-
import { t as
|
|
8
|
-
import { i as userAgent, n as tightenSecretFilePermissions, r as writeSecretFile } from "./secret-file-aoNXJmKa.mjs";
|
|
9
|
-
import { t as createCLIError } from "./errors-118wUDRr.mjs";
|
|
6
|
+
import { t as userAgent } from "./user-agent-Bgsszb5I.mjs";
|
|
10
7
|
import { t as isPluginGeneratedType } from "./type-source-DH_LH20p.mjs";
|
|
11
8
|
import { builtinModules, createRequire } from "node:module";
|
|
12
9
|
import * as path from "pathe";
|
|
13
|
-
import {
|
|
10
|
+
import { resolve } from "pathe";
|
|
14
11
|
import { resolveTSConfig } from "pkg-types";
|
|
15
|
-
import { arg } from "politty";
|
|
16
12
|
import { z } from "zod";
|
|
17
13
|
import { create } from "@bufbuild/protobuf";
|
|
18
|
-
import * as fs
|
|
19
|
-
import {
|
|
20
|
-
import { parseEnv } from "node:util";
|
|
14
|
+
import * as fs from "node:fs";
|
|
15
|
+
import { readFileSync } from "node:fs";
|
|
21
16
|
import { MethodOptions_IdempotencyLevel } from "@bufbuild/protobuf/wkt";
|
|
22
17
|
import { pathToFileURL } from "node:url";
|
|
23
|
-
import { parseYAML, stringifyYAML } from "confbox";
|
|
24
|
-
import { findUpSync } from "find-up-simple";
|
|
25
|
-
import { lt } from "semver";
|
|
26
|
-
import { xdgConfig } from "xdg-basedir";
|
|
27
18
|
import { OAuth2Client } from "@badgateway/oauth2-client";
|
|
28
19
|
import { Code, ConnectError, createClient } from "@connectrpc/connect";
|
|
29
20
|
import { getGlobalDispatcher } from "undici";
|
|
30
21
|
import pLimit from "p-limit";
|
|
31
22
|
import * as crypto from "node:crypto";
|
|
32
23
|
import * as rolldown from "rolldown";
|
|
33
|
-
import * as fs from "node:fs/promises";
|
|
34
24
|
import { parseSync } from "oxc-parser";
|
|
35
25
|
import * as inflection from "inflection";
|
|
36
26
|
import * as globals from "globals";
|
|
37
27
|
import * as os from "node:os";
|
|
38
28
|
|
|
39
|
-
//#region src/cli/shared/args.ts
|
|
40
|
-
const unitToMs = {
|
|
41
|
-
ms: 1,
|
|
42
|
-
s: 1e3,
|
|
43
|
-
m: 60 * 1e3
|
|
44
|
-
};
|
|
45
|
-
const durationPattern = /^(\d+)(ms|s|m)$/;
|
|
46
|
-
/**
|
|
47
|
-
* Schema for duration string validation (e.g., "3s", "500ms", "1m")
|
|
48
|
-
* Only validates format; use parseDuration() to convert to milliseconds
|
|
49
|
-
*/
|
|
50
|
-
const durationArg = z.string().refine((val) => durationPattern.test(val), { message: "Invalid duration format. Expected format: '3s', '500ms', '1m'" }).refine((val) => {
|
|
51
|
-
const match = val.match(durationPattern);
|
|
52
|
-
if (!match) return false;
|
|
53
|
-
const digits = match[1];
|
|
54
|
-
return digits !== void 0 && parseInt(digits, 10) > 0;
|
|
55
|
-
}, { message: "Duration must be greater than 0" });
|
|
56
|
-
/**
|
|
57
|
-
* Parse a validated duration string into milliseconds
|
|
58
|
-
* @param duration - Duration string (e.g., "3s", "500ms", "1m")
|
|
59
|
-
* @returns Duration in milliseconds
|
|
60
|
-
*/
|
|
61
|
-
function parseDuration(duration) {
|
|
62
|
-
const match = assertDefined(duration.match(durationPattern), `invalid duration format: ${duration}`);
|
|
63
|
-
const value = parseInt(assertDefined(match[1], "duration digits group missing"), 10);
|
|
64
|
-
const unit = assertDefined(match[2], "duration unit group missing");
|
|
65
|
-
return value * unitToMs[unit];
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Schema for positive integer validation (from string input)
|
|
69
|
-
* Transforms the string to a number
|
|
70
|
-
*/
|
|
71
|
-
const positiveIntArg = z.coerce.number().int().positive();
|
|
72
|
-
/**
|
|
73
|
-
* Schema for non-negative integer validation (from string input).
|
|
74
|
-
* Accepts 0 (used for `--limit 0` to disable the limit).
|
|
75
|
-
*/
|
|
76
|
-
const nonNegativeIntArg = z.coerce.number().int().nonnegative();
|
|
77
|
-
/**
|
|
78
|
-
* Schema for sort order (`asc` or `desc`).
|
|
79
|
-
*/
|
|
80
|
-
const orderArg = z.enum(["asc", "desc"]);
|
|
81
|
-
/**
|
|
82
|
-
* Translate a CLI `--order` value into the proto `PageDirection` enum.
|
|
83
|
-
* Returns `undefined` when the user did not specify an order so that
|
|
84
|
-
* callers can omit the field and fall back to the server default.
|
|
85
|
-
* @param order - Order string from CLI args (`"asc"` | `"desc"` | undefined)
|
|
86
|
-
* @returns PageDirection, or undefined when `order` is undefined
|
|
87
|
-
*/
|
|
88
|
-
function toPageDirection(order) {
|
|
89
|
-
if (order === void 0) return void 0;
|
|
90
|
-
return order === "asc" ? PageDirection.ASC : PageDirection.DESC;
|
|
91
|
-
}
|
|
92
|
-
function hasMachineUserFlag(argv) {
|
|
93
|
-
return argv.slice(0, argv.indexOf("--") === -1 ? argv.length : argv.indexOf("--")).some((token) => token === "-m" || token.startsWith("-m=") || token === "--machine-user" || token.startsWith("--machine-user=") || token === "--machineUser" || token.startsWith("--machineUser=") || token === "--machineuser" || token.startsWith("--machineuser="));
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Resolve whether a parsed machine user value came from an explicit CLI option or env fallback.
|
|
97
|
-
* @param machineUser - Parsed machine user value
|
|
98
|
-
* @param argv - Raw CLI argv, excluding the executable and script path
|
|
99
|
-
* @param options - Source resolution options
|
|
100
|
-
* @returns Machine user input source, or undefined when no value was parsed
|
|
101
|
-
*/
|
|
102
|
-
function resolveMachineUserInputSource(machineUser, argv = process.argv.slice(2), options = {}) {
|
|
103
|
-
if (machineUser === void 0) return void 0;
|
|
104
|
-
if (options.valueIsExplicit) return "option";
|
|
105
|
-
if (hasMachineUserFlag(argv)) return "option";
|
|
106
|
-
return process.env.TAILOR_PLATFORM_MACHINE_USER_NAME === machineUser ? "env" : "option";
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Load env files from parsed arguments.
|
|
110
|
-
* Processes --env-file first, then --env-file-if-exists.
|
|
111
|
-
*
|
|
112
|
-
* Follows Node.js --env-file behavior:
|
|
113
|
-
* - Variables already set in the environment are NOT overwritten
|
|
114
|
-
* - Variables from later files override those from earlier files
|
|
115
|
-
* @param envFiles - Required env file path(s) that must exist
|
|
116
|
-
* @param envFilesIfExists - Optional env file path(s) that are loaded if they exist
|
|
117
|
-
*/
|
|
118
|
-
function loadEnvFiles(envFiles, envFilesIfExists) {
|
|
119
|
-
const originalEnvKeys = new Set(Object.keys(process.env));
|
|
120
|
-
const load = (files, required) => {
|
|
121
|
-
for (const file of [files ?? []].flat()) {
|
|
122
|
-
const envPath = path.resolve(process.cwd(), file);
|
|
123
|
-
if (!fs$1.existsSync(envPath)) {
|
|
124
|
-
if (required) throw new Error(`Environment file not found: ${envPath}`);
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
const parsed = parseEnv(fs$1.readFileSync(envPath, "utf-8"));
|
|
128
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
129
|
-
if (originalEnvKeys.has(key)) continue;
|
|
130
|
-
process.env[key] = value;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
load(envFiles, true);
|
|
135
|
-
load(envFilesIfExists, false);
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Common arguments for all CLI commands
|
|
139
|
-
*
|
|
140
|
-
* NOTE: --env-file and --env-file-if-exists collide with Node.js flags due to a bug
|
|
141
|
-
* (https://github.com/nodejs/node/issues/54232). Node.js parses these even after the
|
|
142
|
-
* script path, causing warnings (twice due to tsx loader).
|
|
143
|
-
*/
|
|
144
|
-
const commonArgs = {
|
|
145
|
-
"env-file": arg(z.string().optional(), {
|
|
146
|
-
alias: "e",
|
|
147
|
-
description: "Path to the environment file (error if not found)",
|
|
148
|
-
completion: {
|
|
149
|
-
type: "file",
|
|
150
|
-
matcher: [".env.*", ".env"]
|
|
151
|
-
}
|
|
152
|
-
}),
|
|
153
|
-
"env-file-if-exists": arg(z.string().optional(), {
|
|
154
|
-
description: "Path to the environment file (ignored if not found)",
|
|
155
|
-
completion: {
|
|
156
|
-
type: "file",
|
|
157
|
-
matcher: [".env.*", ".env"]
|
|
158
|
-
},
|
|
159
|
-
effect: (_value, { args }) => {
|
|
160
|
-
loadEnvFiles(args["env-file"], args["env-file-if-exists"]);
|
|
161
|
-
}
|
|
162
|
-
}),
|
|
163
|
-
verbose: arg(z.boolean().default(false), {
|
|
164
|
-
description: "Enable verbose logging",
|
|
165
|
-
effect: (value) => {
|
|
166
|
-
verboseMode = value;
|
|
167
|
-
}
|
|
168
|
-
}),
|
|
169
|
-
json: arg(z.boolean().default(false), {
|
|
170
|
-
alias: "j",
|
|
171
|
-
description: "Output as JSON",
|
|
172
|
-
effect: (value) => {
|
|
173
|
-
logger.jsonMode = value;
|
|
174
|
-
}
|
|
175
|
-
})
|
|
176
|
-
};
|
|
177
|
-
/**
|
|
178
|
-
* Arguments for commands that require workspace context
|
|
179
|
-
*/
|
|
180
|
-
const workspaceArgs = {
|
|
181
|
-
"workspace-id": arg(z.string().optional(), {
|
|
182
|
-
alias: "w",
|
|
183
|
-
description: "Workspace ID",
|
|
184
|
-
env: "TAILOR_PLATFORM_WORKSPACE_ID",
|
|
185
|
-
completion: { type: "none" }
|
|
186
|
-
}),
|
|
187
|
-
profile: arg(z.string().optional(), {
|
|
188
|
-
alias: "p",
|
|
189
|
-
description: "Workspace profile",
|
|
190
|
-
env: "TAILOR_PLATFORM_PROFILE",
|
|
191
|
-
completion: { type: "none" }
|
|
192
|
-
})
|
|
193
|
-
};
|
|
194
|
-
/**
|
|
195
|
-
* Shared config arg for commands that accept a config file path
|
|
196
|
-
*/
|
|
197
|
-
const configArg = { config: arg(z.string().default("tailor.config.ts"), {
|
|
198
|
-
alias: "c",
|
|
199
|
-
description: "Path to Tailor config file",
|
|
200
|
-
env: "TAILOR_CONFIG_PATH",
|
|
201
|
-
completion: {
|
|
202
|
-
type: "file",
|
|
203
|
-
extensions: ["ts"]
|
|
204
|
-
}
|
|
205
|
-
}) };
|
|
206
|
-
/**
|
|
207
|
-
* Shared config arg for commands that accept one or more comma-separated config file paths
|
|
208
|
-
*/
|
|
209
|
-
const multiConfigArg = { config: arg(z.string().default("tailor.config.ts"), {
|
|
210
|
-
alias: "c",
|
|
211
|
-
description: "Path to SDK config file. Use comma-separated paths to deploy multiple apps together.",
|
|
212
|
-
env: "TAILOR_PLATFORM_SDK_CONFIG_PATH",
|
|
213
|
-
completion: {
|
|
214
|
-
type: "file",
|
|
215
|
-
extensions: ["ts"]
|
|
216
|
-
}
|
|
217
|
-
}) };
|
|
218
|
-
/**
|
|
219
|
-
* Arguments for commands that interact with deployed resources (includes config)
|
|
220
|
-
*/
|
|
221
|
-
const deploymentArgs = {
|
|
222
|
-
...workspaceArgs,
|
|
223
|
-
...configArg
|
|
224
|
-
};
|
|
225
|
-
/**
|
|
226
|
-
* Arguments for commands that require confirmation
|
|
227
|
-
*/
|
|
228
|
-
const confirmationArgs = { yes: arg(z.boolean().default(false), {
|
|
229
|
-
alias: "y",
|
|
230
|
-
description: "Skip confirmation prompts"
|
|
231
|
-
}) };
|
|
232
|
-
/**
|
|
233
|
-
* Arguments for commands that require organization context
|
|
234
|
-
*/
|
|
235
|
-
const organizationArgs = { "organization-id": arg(z.string(), {
|
|
236
|
-
alias: "o",
|
|
237
|
-
description: "Organization ID",
|
|
238
|
-
env: "TAILOR_PLATFORM_ORGANIZATION_ID",
|
|
239
|
-
completion: { type: "none" }
|
|
240
|
-
}) };
|
|
241
|
-
/**
|
|
242
|
-
* Arguments for list commands that accept `--order` / `--limit`. Sort
|
|
243
|
-
* order defaults to `desc` (newest first) because most callers want the
|
|
244
|
-
* latest items; pass `--order asc` to opt in to ascending order. The
|
|
245
|
-
* limit is unbounded by default so existing invocations keep returning
|
|
246
|
-
* every item; pass `--limit N` to cap the result size.
|
|
247
|
-
* @param defaultOrder - Default value for `--order` (defaults to `"desc"`)
|
|
248
|
-
* @returns Argument shape suitable for spreading into a command schema
|
|
249
|
-
*/
|
|
250
|
-
const paginationArgs = (defaultOrder = "desc") => ({
|
|
251
|
-
order: arg(orderArg.default(defaultOrder), { description: "Sort order (asc or desc)" }),
|
|
252
|
-
limit: arg(nonNegativeIntArg.optional(), {
|
|
253
|
-
alias: "l",
|
|
254
|
-
description: "Maximum number of items to return (0 or omit: unlimited)"
|
|
255
|
-
})
|
|
256
|
-
});
|
|
257
|
-
/**
|
|
258
|
-
* Arguments for time-series log list commands. Defaults to newest-first
|
|
259
|
-
* (`desc`) and a 50-item cap so that listing stays responsive on busy
|
|
260
|
-
* workspaces. Pass `--limit 0` to disable the cap and fetch all entries.
|
|
261
|
-
*/
|
|
262
|
-
const pagedLogArgs = {
|
|
263
|
-
order: arg(orderArg.default("desc"), { description: "Sort order (asc or desc)" }),
|
|
264
|
-
limit: arg(nonNegativeIntArg.default(50), {
|
|
265
|
-
alias: "l",
|
|
266
|
-
description: "Maximum number of items to return (0: unlimited)"
|
|
267
|
-
})
|
|
268
|
-
};
|
|
269
|
-
/**
|
|
270
|
-
* Arguments for commands that require folder context
|
|
271
|
-
*/
|
|
272
|
-
const folderArgs = { "folder-id": arg(z.string(), {
|
|
273
|
-
alias: "f",
|
|
274
|
-
description: "Folder ID",
|
|
275
|
-
env: "TAILOR_PLATFORM_FOLDER_ID",
|
|
276
|
-
completion: { type: "none" }
|
|
277
|
-
}) };
|
|
278
|
-
let verboseMode = false;
|
|
279
|
-
/**
|
|
280
|
-
* Returns whether verbose mode is enabled.
|
|
281
|
-
* Used by the global cleanup handler which doesn't have access to parsed args.
|
|
282
|
-
* @returns Whether verbose mode is enabled
|
|
283
|
-
*/
|
|
284
|
-
function isVerbose() {
|
|
285
|
-
return verboseMode;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
//#endregion
|
|
289
29
|
//#region src/parser/app-config/log-level.ts
|
|
290
30
|
const LOG_LEVELS = [
|
|
291
31
|
"DEBUG",
|
|
@@ -298,64 +38,6 @@ function isLogLevel(value) {
|
|
|
298
38
|
return LOG_LEVELS.includes(value);
|
|
299
39
|
}
|
|
300
40
|
|
|
301
|
-
//#endregion
|
|
302
|
-
//#region src/parser/app-config/schema.ts
|
|
303
|
-
const envValueSchema = z.union([
|
|
304
|
-
z.string(),
|
|
305
|
-
z.number(),
|
|
306
|
-
z.boolean()
|
|
307
|
-
]);
|
|
308
|
-
const LogLevelSchema = z.enum(LOG_LEVELS);
|
|
309
|
-
const logLevelSchema = z.string().refine((value) => LogLevelSchema.safeParse(value.trim().toUpperCase()).success, { message: `'logLevel' must be one of: ${LOG_LEVELS.join(", ")}.` });
|
|
310
|
-
/**
|
|
311
|
-
* Structural validation schema for `defineConfig({...})`. Validates only
|
|
312
|
-
* top-level fields with platform-side constraints (notably `id`); fields
|
|
313
|
-
* that carry SDK builder objects (`auth`, `idp`, `db`, ...) are accepted
|
|
314
|
-
* as opaque values, since their internal shapes are validated by their
|
|
315
|
-
* own factory functions and parser-level schemas.
|
|
316
|
-
*
|
|
317
|
-
* The `id` is auto-managed by `deploy` and stored as a plain UUID. A
|
|
318
|
-
* label-compatible prefix is added at the metadata boundary, so user-facing
|
|
319
|
-
* configs only need to carry a UUID.
|
|
320
|
-
*/
|
|
321
|
-
const AppConfigSchema = z.strictObject({
|
|
322
|
-
id: z.uuid({ message: "'id' must be a UUID." }).optional(),
|
|
323
|
-
name: z.string().min(1, { message: "'name' must be a non-empty string." }),
|
|
324
|
-
env: z.record(z.string(), envValueSchema).optional(),
|
|
325
|
-
cors: z.array(z.string()).optional(),
|
|
326
|
-
allowedIpAddresses: z.array(z.string()).optional(),
|
|
327
|
-
disableIntrospection: z.boolean().optional(),
|
|
328
|
-
inlineSourcemap: z.boolean().optional(),
|
|
329
|
-
logLevel: logLevelSchema.optional(),
|
|
330
|
-
db: z.unknown().optional(),
|
|
331
|
-
resolver: z.unknown().optional(),
|
|
332
|
-
idp: z.unknown().optional(),
|
|
333
|
-
auth: z.unknown().optional(),
|
|
334
|
-
executor: z.unknown().optional(),
|
|
335
|
-
workflow: z.unknown().optional(),
|
|
336
|
-
httpAdapter: z.unknown().optional(),
|
|
337
|
-
staticWebsites: z.unknown().optional(),
|
|
338
|
-
aiGateways: z.unknown().optional(),
|
|
339
|
-
secrets: z.unknown().optional()
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
//#endregion
|
|
343
|
-
//#region src/parser/plugin-config/schema.ts
|
|
344
|
-
const PluginConfigSchema = z.looseObject({
|
|
345
|
-
id: z.string(),
|
|
346
|
-
description: z.string(),
|
|
347
|
-
importPath: z.string().optional(),
|
|
348
|
-
pluginConfig: z.unknown().optional(),
|
|
349
|
-
typeConfigRequired: z.union([z.boolean(), functionSchema]).optional(),
|
|
350
|
-
onTypeLoaded: functionSchema.optional(),
|
|
351
|
-
onNamespaceLoaded: functionSchema.optional(),
|
|
352
|
-
onTailorDBReady: functionSchema.optional(),
|
|
353
|
-
onResolverReady: functionSchema.optional(),
|
|
354
|
-
onExecutorReady: functionSchema.optional()
|
|
355
|
-
}).refine((p) => {
|
|
356
|
-
return !(p.onTypeLoaded || p.onNamespaceLoaded) || !!p.importPath;
|
|
357
|
-
}, { message: "importPath is required when plugin has definition-time hooks (onTypeLoaded/onNamespaceLoaded)" }).transform((plugin) => plugin);
|
|
358
|
-
|
|
359
41
|
//#endregion
|
|
360
42
|
//#region src/cli/shared/apply-concurrency.ts
|
|
361
43
|
/**
|
|
@@ -498,7 +180,7 @@ function initOAuth2Client(config) {
|
|
|
498
180
|
*/
|
|
499
181
|
async function initOperatorClient(accessToken, config) {
|
|
500
182
|
const platformConfig = config ?? getPlatformConfigForToken(accessToken);
|
|
501
|
-
const [{ createTracingInterceptor }, { OperatorService }] = await Promise.all([import("./interceptor-e33JtsC-.mjs"), import("./service_pb-
|
|
183
|
+
const [{ createTracingInterceptor }, { OperatorService }] = await Promise.all([import("./interceptor-e33JtsC-.mjs"), import("./service_pb-r1T7kpJc.mjs")]);
|
|
502
184
|
const interceptors = [
|
|
503
185
|
await userAgentInterceptor(),
|
|
504
186
|
await bearerTokenInterceptor(accessToken),
|
|
@@ -551,9 +233,9 @@ async function bearerTokenInterceptor(accessToken) {
|
|
|
551
233
|
/**
|
|
552
234
|
* Create an interceptor that retries failed unary requests with backoff.
|
|
553
235
|
*
|
|
554
|
-
* Retries
|
|
555
|
-
* only for methods declared idempotent, up to 3 attempts
|
|
556
|
-
*
|
|
236
|
+
* Retries unary methods on `Unavailable`/`ResourceExhausted`, and `Internal`
|
|
237
|
+
* only for methods declared idempotent, up to 3 attempts. Workspace creation is
|
|
238
|
+
* excluded because it has no idempotency key and a lost response is ambiguous.
|
|
557
239
|
* As a targeted exception for the deploy/apply flow, a post-retry `AlreadyExists`
|
|
558
240
|
* from an allowlisted Create (see `RETRY_SAFE_CREATE_METHODS`) is treated as
|
|
559
241
|
* success, since it means a prior attempt already committed the resource
|
|
@@ -578,10 +260,10 @@ function retryInterceptor() {
|
|
|
578
260
|
logger.debug(`retry: ${req.method.name} returned AlreadyExists on attempt ${i + 1}; treating as success (prior attempt likely committed)`);
|
|
579
261
|
return synthesizeEmptyUnaryResponse(req);
|
|
580
262
|
}
|
|
581
|
-
const { reportCrash } = await import("./crashreport-
|
|
263
|
+
const { reportCrash } = await import("./crashreport-0ioNRZ4K.mjs");
|
|
582
264
|
await reportCrash(error, "handledError");
|
|
583
265
|
}
|
|
584
|
-
if (isRetirable(error, req.method.idempotency)) {
|
|
266
|
+
if (req.method.name !== "CreateWorkspace" && isRetirable(error, req.method.idempotency)) {
|
|
585
267
|
lastError = error;
|
|
586
268
|
logger.debug(`retry: ${req.method.name} attempt ${i + 1} failed with ${connectCodeName(error)}; retrying`);
|
|
587
269
|
continue;
|
|
@@ -986,806 +668,6 @@ async function closeConnectionPool() {
|
|
|
986
668
|
await getGlobalDispatcher().close();
|
|
987
669
|
}
|
|
988
670
|
|
|
989
|
-
//#endregion
|
|
990
|
-
//#region src/cli/shared/token-store.ts
|
|
991
|
-
const SERVICE_NAME = "tailor-platform-cli";
|
|
992
|
-
let entryClass;
|
|
993
|
-
async function getEntryClass() {
|
|
994
|
-
if (entryClass !== void 0) return entryClass;
|
|
995
|
-
try {
|
|
996
|
-
const mod = await import("@napi-rs/keyring");
|
|
997
|
-
const probe = new mod.Entry(SERVICE_NAME, "__probe__");
|
|
998
|
-
probe.setPassword("probe");
|
|
999
|
-
probe.deletePassword();
|
|
1000
|
-
entryClass = mod.Entry;
|
|
1001
|
-
} catch {
|
|
1002
|
-
logger.warn("System keyring is not available. Tokens will be stored in the config file. Set TAILOR_PLATFORM_TOKEN environment variable for CI environments.");
|
|
1003
|
-
entryClass = false;
|
|
1004
|
-
}
|
|
1005
|
-
return entryClass;
|
|
1006
|
-
}
|
|
1007
|
-
/**
|
|
1008
|
-
* Check whether the OS keyring is available and functional.
|
|
1009
|
-
* @returns true if keyring is available
|
|
1010
|
-
*/
|
|
1011
|
-
async function isKeyringAvailable() {
|
|
1012
|
-
return await getEntryClass() !== false;
|
|
1013
|
-
}
|
|
1014
|
-
/**
|
|
1015
|
-
* Load tokens from the OS keyring for a given account.
|
|
1016
|
-
* @param account - User identifier (e.g. email or client ID)
|
|
1017
|
-
* @returns Token data or undefined if not found or keyring unavailable
|
|
1018
|
-
*/
|
|
1019
|
-
async function loadKeyringTokens(account) {
|
|
1020
|
-
const Entry = await getEntryClass();
|
|
1021
|
-
if (!Entry) return void 0;
|
|
1022
|
-
try {
|
|
1023
|
-
const raw = new Entry(SERVICE_NAME, account).getPassword();
|
|
1024
|
-
if (raw === null) return void 0;
|
|
1025
|
-
return JSON.parse(raw);
|
|
1026
|
-
} catch {
|
|
1027
|
-
return;
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Save tokens to the OS keyring for a given account.
|
|
1032
|
-
* @param account - User identifier (e.g. email or client ID)
|
|
1033
|
-
* @param tokens - Token data to store
|
|
1034
|
-
*/
|
|
1035
|
-
async function saveKeyringTokens(account, tokens) {
|
|
1036
|
-
const Entry = await getEntryClass();
|
|
1037
|
-
if (!Entry) return;
|
|
1038
|
-
new Entry(SERVICE_NAME, account).setPassword(JSON.stringify(tokens));
|
|
1039
|
-
}
|
|
1040
|
-
/**
|
|
1041
|
-
* Delete tokens from the OS keyring for a given account.
|
|
1042
|
-
* @param account - User identifier (e.g. email or client ID)
|
|
1043
|
-
*/
|
|
1044
|
-
async function deleteKeyringTokens(account) {
|
|
1045
|
-
const Entry = await getEntryClass();
|
|
1046
|
-
if (!Entry) return;
|
|
1047
|
-
try {
|
|
1048
|
-
new Entry(SERVICE_NAME, account).deletePassword();
|
|
1049
|
-
} catch {}
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
//#endregion
|
|
1053
|
-
//#region src/cli/shared/context.ts
|
|
1054
|
-
const pfProfileSchema = z.object({
|
|
1055
|
-
user: z.string(),
|
|
1056
|
-
workspace_id: z.string(),
|
|
1057
|
-
readonly: z.boolean().optional(),
|
|
1058
|
-
machine_user: z.string().optional(),
|
|
1059
|
-
machine_user_override: z.enum(["allow", "deny"]).optional(),
|
|
1060
|
-
platform_url: z.url().optional(),
|
|
1061
|
-
oauth2_client_id: z.string().optional(),
|
|
1062
|
-
console_url: z.url().optional()
|
|
1063
|
-
});
|
|
1064
|
-
const pfUserSchemaV1 = z.object({
|
|
1065
|
-
access_token: z.string(),
|
|
1066
|
-
refresh_token: z.string().optional(),
|
|
1067
|
-
token_expires_at: z.string()
|
|
1068
|
-
});
|
|
1069
|
-
const pfUserKeyringSchema = z.object({
|
|
1070
|
-
storage: z.literal("keyring"),
|
|
1071
|
-
token_expires_at: z.string()
|
|
1072
|
-
});
|
|
1073
|
-
const pfUserFileSchema = z.object({
|
|
1074
|
-
storage: z.literal("file"),
|
|
1075
|
-
token_expires_at: z.string(),
|
|
1076
|
-
access_token: z.string(),
|
|
1077
|
-
refresh_token: z.string().optional()
|
|
1078
|
-
});
|
|
1079
|
-
const pfUserSchemaV2 = z.discriminatedUnion("storage", [pfUserKeyringSchema, pfUserFileSchema]);
|
|
1080
|
-
const pfUserKeyringSchemaV3 = pfUserKeyringSchema.extend({ email: z.string().optional() });
|
|
1081
|
-
const pfUserFileSchemaV3 = pfUserFileSchema.extend({ email: z.string().optional() });
|
|
1082
|
-
const pfUserSchemaV3 = z.discriminatedUnion("storage", [pfUserKeyringSchemaV3, pfUserFileSchemaV3]);
|
|
1083
|
-
const pfConfigSchemaV1 = z.object({
|
|
1084
|
-
version: z.literal(1),
|
|
1085
|
-
users: z.partialRecord(z.string(), pfUserSchemaV1),
|
|
1086
|
-
profiles: z.partialRecord(z.string(), pfProfileSchema),
|
|
1087
|
-
current_user: z.string().nullable()
|
|
1088
|
-
});
|
|
1089
|
-
const V2_CONFIG_VERSION = 2;
|
|
1090
|
-
const LATEST_CONFIG_VERSION = 3;
|
|
1091
|
-
const V2_MIN_SDK_VERSION = "1.29.0";
|
|
1092
|
-
const V3_MIN_SDK_VERSION = "2.0.0";
|
|
1093
|
-
const semverSchema = z.templateLiteral([
|
|
1094
|
-
z.number().int(),
|
|
1095
|
-
".",
|
|
1096
|
-
z.number().int(),
|
|
1097
|
-
".",
|
|
1098
|
-
z.number().int()
|
|
1099
|
-
]);
|
|
1100
|
-
const pfConfigSchemaV2 = z.object({
|
|
1101
|
-
version: z.literal(V2_CONFIG_VERSION),
|
|
1102
|
-
min_sdk_version: semverSchema,
|
|
1103
|
-
latest_version: z.number().int().optional(),
|
|
1104
|
-
latest_min_sdk_version: semverSchema.optional(),
|
|
1105
|
-
users: z.partialRecord(z.string(), pfUserSchemaV2),
|
|
1106
|
-
profiles: z.partialRecord(z.string(), pfProfileSchema),
|
|
1107
|
-
current_user: z.string().nullable()
|
|
1108
|
-
});
|
|
1109
|
-
const pfConfigSchemaV3 = z.object({
|
|
1110
|
-
version: z.literal(LATEST_CONFIG_VERSION),
|
|
1111
|
-
min_sdk_version: semverSchema,
|
|
1112
|
-
latest_version: z.number().int().optional(),
|
|
1113
|
-
latest_min_sdk_version: semverSchema.optional(),
|
|
1114
|
-
users: z.partialRecord(z.string(), pfUserSchemaV3),
|
|
1115
|
-
profiles: z.partialRecord(z.string(), pfProfileSchema),
|
|
1116
|
-
current_user: z.string().nullable()
|
|
1117
|
-
});
|
|
1118
|
-
function platformConfigPath() {
|
|
1119
|
-
if (!xdgConfig) throw new Error("User home directory not found");
|
|
1120
|
-
return path.join(xdgConfig, "tailor-platform", "config.yaml");
|
|
1121
|
-
}
|
|
1122
|
-
/**
|
|
1123
|
-
* Convert stored profile platform fields to platform client settings.
|
|
1124
|
-
* @param profile - Profile platform settings
|
|
1125
|
-
* @returns Platform client settings
|
|
1126
|
-
*/
|
|
1127
|
-
function platformConfigFromProfile(profile) {
|
|
1128
|
-
const config = {
|
|
1129
|
-
...profile?.platform_url ? { platformUrl: profile.platform_url } : {},
|
|
1130
|
-
...profile?.oauth2_client_id ? { oauth2ClientId: profile.oauth2_client_id } : {},
|
|
1131
|
-
...profile?.console_url ? { consoleUrl: profile.console_url } : {}
|
|
1132
|
-
};
|
|
1133
|
-
return Object.keys(config).length > 0 ? config : void 0;
|
|
1134
|
-
}
|
|
1135
|
-
function platformUserKey(user, config) {
|
|
1136
|
-
const platformUrl = getPlatformBaseUrl(config);
|
|
1137
|
-
if (platformUrl === normalizeBaseUrl("https://api.tailor.tech")) return user;
|
|
1138
|
-
return `${platformUrl}|${user}`;
|
|
1139
|
-
}
|
|
1140
|
-
function userFromPlatformUserKey(userKey, config) {
|
|
1141
|
-
const platformPrefix = `${getPlatformBaseUrl(config)}|`;
|
|
1142
|
-
return userKey.startsWith(platformPrefix) ? userKey.slice(platformPrefix.length) : userKey;
|
|
1143
|
-
}
|
|
1144
|
-
function canUseLegacyUserKey(platformUrl) {
|
|
1145
|
-
try {
|
|
1146
|
-
return getPlatformBaseUrl() === platformUrl;
|
|
1147
|
-
} catch {
|
|
1148
|
-
return false;
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
function findUserByEmail(users, email, platformConfig) {
|
|
1152
|
-
const platformUrl = getPlatformBaseUrl(platformConfig);
|
|
1153
|
-
const platformPrefix = `${platformUrl}|`;
|
|
1154
|
-
const defaultPlatform = platformUrl === normalizeBaseUrl(defaultPlatformBaseUrl);
|
|
1155
|
-
return Object.entries(users).find(([key, entry]) => {
|
|
1156
|
-
if (entry?.email !== email) return false;
|
|
1157
|
-
return defaultPlatform ? !key.includes("|") : key.startsWith(platformPrefix);
|
|
1158
|
-
});
|
|
1159
|
-
}
|
|
1160
|
-
function findUserEntry(config, user, platformConfig, opts = {}) {
|
|
1161
|
-
const userKey = platformUserKey(user, platformConfig);
|
|
1162
|
-
const userEntry = config.users[userKey];
|
|
1163
|
-
if (userEntry) return {
|
|
1164
|
-
userKey,
|
|
1165
|
-
userEntry
|
|
1166
|
-
};
|
|
1167
|
-
const emailMatch = findUserByEmail(config.users, user, platformConfig);
|
|
1168
|
-
if (emailMatch?.[1]) return {
|
|
1169
|
-
userKey: emailMatch[0],
|
|
1170
|
-
userEntry: emailMatch[1]
|
|
1171
|
-
};
|
|
1172
|
-
const platformUrl = getPlatformBaseUrl(platformConfig);
|
|
1173
|
-
if (userKey !== user && (opts.allowLegacyUserKey === false || !canUseLegacyUserKey(platformUrl))) return {
|
|
1174
|
-
userKey,
|
|
1175
|
-
userEntry
|
|
1176
|
-
};
|
|
1177
|
-
const legacyEntry = config.users[user];
|
|
1178
|
-
return legacyEntry ? {
|
|
1179
|
-
userKey: user,
|
|
1180
|
-
userEntry: legacyEntry
|
|
1181
|
-
} : {
|
|
1182
|
-
userKey,
|
|
1183
|
-
userEntry
|
|
1184
|
-
};
|
|
1185
|
-
}
|
|
1186
|
-
/**
|
|
1187
|
-
* Resolve the config user key that would be used for a user on the selected platform.
|
|
1188
|
-
* @param config - Platform config
|
|
1189
|
-
* @param user - User name
|
|
1190
|
-
* @param platformConfig - Optional platform connection settings
|
|
1191
|
-
* @param opts - Token lookup options
|
|
1192
|
-
* @returns Resolved config user key
|
|
1193
|
-
*/
|
|
1194
|
-
function resolveUserTokenKey(config, user, platformConfig, opts) {
|
|
1195
|
-
return findUserEntry(config, user, platformConfig, opts).userKey;
|
|
1196
|
-
}
|
|
1197
|
-
function resolveConfigUser(config, user, platformConfig, opts) {
|
|
1198
|
-
const { userKey, userEntry } = findUserEntry(config, user, platformConfig, opts);
|
|
1199
|
-
return userEntry ? userFromPlatformUserKey(userKey, platformConfig) : void 0;
|
|
1200
|
-
}
|
|
1201
|
-
/**
|
|
1202
|
-
* Check whether tokens are registered for a user on the selected platform.
|
|
1203
|
-
* @param config - Platform config
|
|
1204
|
-
* @param user - User name
|
|
1205
|
-
* @param platformConfig - Optional platform connection settings
|
|
1206
|
-
* @param opts - Token lookup options
|
|
1207
|
-
* @returns True when the user has a registered token entry
|
|
1208
|
-
*/
|
|
1209
|
-
function hasUserTokenEntry(config, user, platformConfig, opts) {
|
|
1210
|
-
return findUserEntry(config, user, platformConfig, opts).userEntry !== void 0;
|
|
1211
|
-
}
|
|
1212
|
-
function hasUserKeyForName(users, user) {
|
|
1213
|
-
return users[user] !== void 0 || Object.keys(users).some((key) => key.endsWith(`|${user}`));
|
|
1214
|
-
}
|
|
1215
|
-
function hasUserEmailEntry(users, user) {
|
|
1216
|
-
return Object.values(users).some((entry) => entry?.email === user);
|
|
1217
|
-
}
|
|
1218
|
-
/**
|
|
1219
|
-
* Check whether any platform has tokens registered for a user.
|
|
1220
|
-
* @param config - Platform config
|
|
1221
|
-
* @param user - User name
|
|
1222
|
-
* @returns True when the user has a token entry for any platform
|
|
1223
|
-
*/
|
|
1224
|
-
function hasAnyUserTokenEntry(config, user) {
|
|
1225
|
-
return hasUserKeyForName(config.users, user) || hasUserEmailEntry(config.users, user);
|
|
1226
|
-
}
|
|
1227
|
-
function hasCurrentUserEntry(users, currentUser) {
|
|
1228
|
-
return hasUserKeyForName(users, currentUser);
|
|
1229
|
-
}
|
|
1230
|
-
/**
|
|
1231
|
-
* Migrate a v1 config to v2.
|
|
1232
|
-
* Tokens are kept in the config file (storage: "file") during migration.
|
|
1233
|
-
* They will be moved to the OS keyring on next login or token refresh.
|
|
1234
|
-
* @param v1Config - v1 configuration to migrate
|
|
1235
|
-
* @returns Migrated v2 configuration
|
|
1236
|
-
*/
|
|
1237
|
-
function migrateV1ToV2(v1Config) {
|
|
1238
|
-
const users = {};
|
|
1239
|
-
for (const [name, v1User] of Object.entries(v1Config.users)) {
|
|
1240
|
-
if (!v1User) continue;
|
|
1241
|
-
users[name] = {
|
|
1242
|
-
access_token: v1User.access_token,
|
|
1243
|
-
refresh_token: v1User.refresh_token,
|
|
1244
|
-
token_expires_at: v1User.token_expires_at,
|
|
1245
|
-
storage: "file"
|
|
1246
|
-
};
|
|
1247
|
-
}
|
|
1248
|
-
return {
|
|
1249
|
-
version: V2_CONFIG_VERSION,
|
|
1250
|
-
min_sdk_version: V2_MIN_SDK_VERSION,
|
|
1251
|
-
users,
|
|
1252
|
-
profiles: v1Config.profiles,
|
|
1253
|
-
current_user: v1Config.current_user
|
|
1254
|
-
};
|
|
1255
|
-
}
|
|
1256
|
-
function inferEmailFromUserId(user) {
|
|
1257
|
-
return z.email().safeParse(user).success ? user : void 0;
|
|
1258
|
-
}
|
|
1259
|
-
function migrateV2ToV3(v2Config) {
|
|
1260
|
-
const users = {};
|
|
1261
|
-
for (const [user, entry] of Object.entries(v2Config.users)) {
|
|
1262
|
-
if (!entry) continue;
|
|
1263
|
-
const email = inferEmailFromUserId(user);
|
|
1264
|
-
users[user] = {
|
|
1265
|
-
...entry,
|
|
1266
|
-
...email ? { email } : {}
|
|
1267
|
-
};
|
|
1268
|
-
}
|
|
1269
|
-
return {
|
|
1270
|
-
version: LATEST_CONFIG_VERSION,
|
|
1271
|
-
min_sdk_version: V3_MIN_SDK_VERSION,
|
|
1272
|
-
users,
|
|
1273
|
-
profiles: v2Config.profiles,
|
|
1274
|
-
current_user: v2Config.current_user
|
|
1275
|
-
};
|
|
1276
|
-
}
|
|
1277
|
-
function migrateV1ToV3(v1Config) {
|
|
1278
|
-
return migrateV2ToV3(migrateV1ToV2(v1Config));
|
|
1279
|
-
}
|
|
1280
|
-
function formatUnknownError(error) {
|
|
1281
|
-
return error instanceof Error ? error.message : String(error);
|
|
1282
|
-
}
|
|
1283
|
-
async function trySaveTokensInKeyring(user, tokens) {
|
|
1284
|
-
if (!await isKeyringAvailable()) return false;
|
|
1285
|
-
try {
|
|
1286
|
-
await saveKeyringTokens(user, tokens);
|
|
1287
|
-
return true;
|
|
1288
|
-
} catch (error) {
|
|
1289
|
-
logger.warn(`System keyring failed to store credentials. Tokens will be stored in the config file. ${formatUnknownError(error)}`);
|
|
1290
|
-
return false;
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
async function warnIfNewerConfigAvailable(config) {
|
|
1294
|
-
if (!config.latest_min_sdk_version) return;
|
|
1295
|
-
if (lt((await readPackageJson()).version ?? "0.0.0", config.latest_min_sdk_version)) logger.warn(multiline`
|
|
1296
|
-
A newer config version (${String(config.latest_version)}) is available.
|
|
1297
|
-
Please update your SDK to >= ${config.latest_min_sdk_version}: pnpm update @tailor-platform/sdk
|
|
1298
|
-
`);
|
|
1299
|
-
}
|
|
1300
|
-
/**
|
|
1301
|
-
* Read Tailor Platform CLI configuration, migrating from v1 if necessary.
|
|
1302
|
-
* @returns Parsed platform configuration
|
|
1303
|
-
*/
|
|
1304
|
-
async function readPlatformConfig() {
|
|
1305
|
-
const configPath = platformConfigPath();
|
|
1306
|
-
if (!fs$1.existsSync(configPath)) {
|
|
1307
|
-
const config = {
|
|
1308
|
-
version: LATEST_CONFIG_VERSION,
|
|
1309
|
-
min_sdk_version: V3_MIN_SDK_VERSION,
|
|
1310
|
-
users: {},
|
|
1311
|
-
profiles: {},
|
|
1312
|
-
current_user: null
|
|
1313
|
-
};
|
|
1314
|
-
writePlatformConfig(config);
|
|
1315
|
-
return config;
|
|
1316
|
-
}
|
|
1317
|
-
const rawConfig = parseYAML(fs$1.readFileSync(configPath, "utf-8"));
|
|
1318
|
-
tightenSecretFilePermissions(configPath);
|
|
1319
|
-
const version = rawConfig != null && typeof rawConfig === "object" && "version" in rawConfig ? rawConfig.version : void 0;
|
|
1320
|
-
if (typeof version === "number" && version > LATEST_CONFIG_VERSION) {
|
|
1321
|
-
const minSdk = "min_sdk_version" in rawConfig ? String(rawConfig.min_sdk_version) : void 0;
|
|
1322
|
-
const updateHint = minSdk ? `Please update your SDK to >= ${minSdk}: pnpm update @tailor-platform/sdk` : "Please update your SDK: pnpm update @tailor-platform/sdk";
|
|
1323
|
-
throw new Error(multiline`
|
|
1324
|
-
Config file uses version ${String(version)}, but this SDK only supports up to version ${String(LATEST_CONFIG_VERSION)}.
|
|
1325
|
-
${updateHint}
|
|
1326
|
-
`);
|
|
1327
|
-
}
|
|
1328
|
-
const v3Result = pfConfigSchemaV3.safeParse(rawConfig);
|
|
1329
|
-
if (v3Result.success) {
|
|
1330
|
-
await warnIfNewerConfigAvailable(v3Result.data);
|
|
1331
|
-
return v3Result.data;
|
|
1332
|
-
}
|
|
1333
|
-
const v2Result = pfConfigSchemaV2.safeParse(rawConfig);
|
|
1334
|
-
if (v2Result.success) {
|
|
1335
|
-
await warnIfNewerConfigAvailable(v2Result.data);
|
|
1336
|
-
return migrateV2ToV3(v2Result.data);
|
|
1337
|
-
}
|
|
1338
|
-
const v1Result = pfConfigSchemaV1.safeParse(rawConfig);
|
|
1339
|
-
if (v1Result.success) return migrateV1ToV3(v1Result.data);
|
|
1340
|
-
throw new Error(multiline`
|
|
1341
|
-
Failed to parse config file at ${configPath}.
|
|
1342
|
-
The file may be corrupted or created by an incompatible SDK version.
|
|
1343
|
-
`);
|
|
1344
|
-
}
|
|
1345
|
-
function toV1ForDisk(config) {
|
|
1346
|
-
const users = {};
|
|
1347
|
-
for (const [name, entry] of Object.entries(config.users)) {
|
|
1348
|
-
if (!entry || entry.storage === "keyring") continue;
|
|
1349
|
-
users[name] = {
|
|
1350
|
-
access_token: entry.access_token,
|
|
1351
|
-
refresh_token: entry.refresh_token,
|
|
1352
|
-
token_expires_at: entry.token_expires_at
|
|
1353
|
-
};
|
|
1354
|
-
}
|
|
1355
|
-
const currentUser = config.current_user && hasCurrentUserEntry(users, config.current_user) ? config.current_user : null;
|
|
1356
|
-
return {
|
|
1357
|
-
version: 1,
|
|
1358
|
-
users,
|
|
1359
|
-
profiles: config.profiles,
|
|
1360
|
-
current_user: currentUser
|
|
1361
|
-
};
|
|
1362
|
-
}
|
|
1363
|
-
function hasProfilePlatformSettings(config) {
|
|
1364
|
-
return Object.values(config.profiles).some((profile) => profile?.platform_url !== void 0 || profile?.oauth2_client_id !== void 0 || profile?.console_url !== void 0);
|
|
1365
|
-
}
|
|
1366
|
-
function hasScopedUserKeys(config) {
|
|
1367
|
-
return Object.keys(config.users).some((userKey) => userKey.includes("|"));
|
|
1368
|
-
}
|
|
1369
|
-
function hasUserEmailMetadata(config) {
|
|
1370
|
-
return Object.values(config.users).some((user) => user != null && "email" in user && user.email !== void 0);
|
|
1371
|
-
}
|
|
1372
|
-
function toLatestForDisk(config) {
|
|
1373
|
-
return {
|
|
1374
|
-
...config.version === 1 ? migrateV1ToV2(config) : config,
|
|
1375
|
-
version: LATEST_CONFIG_VERSION,
|
|
1376
|
-
min_sdk_version: V3_MIN_SDK_VERSION
|
|
1377
|
-
};
|
|
1378
|
-
}
|
|
1379
|
-
/**
|
|
1380
|
-
* Write Tailor Platform CLI configuration to disk.
|
|
1381
|
-
* By default, file-backed configs without newer fields are converted to V1 for
|
|
1382
|
-
* backward compatibility, so an older SDK can still read the file. Configs
|
|
1383
|
-
* containing a keyring user are kept in V2 or later because the keyring storage
|
|
1384
|
-
* variant is not representable in V1. Configs containing profile-level Platform
|
|
1385
|
-
* settings, platform-scoped user tokens, canonical user IDs, or email metadata
|
|
1386
|
-
* are written in the latest min-SDK-gated format because older SDKs would
|
|
1387
|
-
* silently drop or misread those settings.
|
|
1388
|
-
*
|
|
1389
|
-
* The config file may contain access/refresh tokens when the OS keyring is
|
|
1390
|
-
* unavailable, so it is written via {@link writeSecretFile} so other users
|
|
1391
|
-
* on the host cannot read it.
|
|
1392
|
-
* @param config - Platform configuration to write
|
|
1393
|
-
*/
|
|
1394
|
-
function writePlatformConfig(config) {
|
|
1395
|
-
const configPath = platformConfigPath();
|
|
1396
|
-
const hasKeyringUser = config.version !== 1 && Object.values(config.users).some((u) => u?.storage === "keyring");
|
|
1397
|
-
writeSecretFile(configPath, stringifyYAML(config.version === LATEST_CONFIG_VERSION || hasProfilePlatformSettings(config) || hasScopedUserKeys(config) || hasUserEmailMetadata(config) ? toLatestForDisk(config) : config.version === V2_CONFIG_VERSION && !hasKeyringUser ? toV1ForDisk(config) : config));
|
|
1398
|
-
}
|
|
1399
|
-
function validateUUID(value, source) {
|
|
1400
|
-
const result = z.uuid().safeParse(value);
|
|
1401
|
-
if (!result.success) throw new Error(`Invalid value from ${source}: must be a valid UUID`);
|
|
1402
|
-
return result.data;
|
|
1403
|
-
}
|
|
1404
|
-
/**
|
|
1405
|
-
* Load workspace ID from command options, environment variables, or platform config.
|
|
1406
|
-
* In CLI context, env fallback is also handled by politty's arg env option.
|
|
1407
|
-
* Priority: opts/workspaceId > env/workspaceId > opts/profile > error
|
|
1408
|
-
* @param opts - Workspace and profile options
|
|
1409
|
-
* @returns Resolved workspace ID
|
|
1410
|
-
*/
|
|
1411
|
-
async function loadWorkspaceId(opts) {
|
|
1412
|
-
const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
|
|
1413
|
-
if (opts?.workspaceId) return validateUUID(opts.workspaceId, "--workspace-id option");
|
|
1414
|
-
if (process.env.TAILOR_PLATFORM_WORKSPACE_ID) return validateUUID(process.env.TAILOR_PLATFORM_WORKSPACE_ID, "TAILOR_PLATFORM_WORKSPACE_ID environment variable");
|
|
1415
|
-
if (profile) {
|
|
1416
|
-
const wsId = (await readPlatformConfig()).profiles[profile]?.workspace_id;
|
|
1417
|
-
if (!wsId) throw new Error(`Profile "${profile}" not found`);
|
|
1418
|
-
return validateUUID(wsId, `profile "${profile}"`);
|
|
1419
|
-
}
|
|
1420
|
-
throw new Error(multiline`
|
|
1421
|
-
Workspace ID not found.
|
|
1422
|
-
Please specify workspace ID via --workspace-id option or TAILOR_PLATFORM_WORKSPACE_ID environment variable.
|
|
1423
|
-
`);
|
|
1424
|
-
}
|
|
1425
|
-
/**
|
|
1426
|
-
* Load machine user name from command options, environment variables, or platform config.
|
|
1427
|
-
* In CLI context, env fallback is also handled by politty's arg env option.
|
|
1428
|
-
* Priority: opts/machineUser > env/TAILOR_PLATFORM_MACHINE_USER_NAME > opts/profile (profile default) > undefined.
|
|
1429
|
-
* An explicitly empty `opts.machineUser` is rejected with a CLIError (`MACHINE_USER_NAME_EMPTY`) rather than falling back to the env var or profile default.
|
|
1430
|
-
* When the active profile has `machine_user_override: "deny"`, an explicit value that differs from the profile's machine user throws a CLIError with code `PROFILE_MACHINE_USER_OVERRIDE_DENIED`.
|
|
1431
|
-
* @param opts - Machine user and profile options
|
|
1432
|
-
* @returns Resolved machine user name, or undefined if not set
|
|
1433
|
-
*/
|
|
1434
|
-
async function loadMachineUserName(opts) {
|
|
1435
|
-
if (opts?.machineUser === "") throw createCLIError({
|
|
1436
|
-
code: "MACHINE_USER_NAME_EMPTY",
|
|
1437
|
-
message: "Machine user name cannot be empty.",
|
|
1438
|
-
suggestion: "Pass a non-empty machine user name, or omit the option to use the environment variable or profile default."
|
|
1439
|
-
});
|
|
1440
|
-
const envMachineUser = process.env.TAILOR_PLATFORM_MACHINE_USER_NAME || void 0;
|
|
1441
|
-
const explicitMachineUser = opts?.machineUser ? {
|
|
1442
|
-
source: opts.machineUserSource ?? "option",
|
|
1443
|
-
value: opts.machineUser
|
|
1444
|
-
} : envMachineUser ? {
|
|
1445
|
-
source: "env",
|
|
1446
|
-
value: envMachineUser
|
|
1447
|
-
} : void 0;
|
|
1448
|
-
const explicit = explicitMachineUser?.value;
|
|
1449
|
-
const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
|
|
1450
|
-
if (!profile) return explicit;
|
|
1451
|
-
const entry = (await readPlatformConfig()).profiles[profile];
|
|
1452
|
-
if (!entry) {
|
|
1453
|
-
if (explicit) return explicit;
|
|
1454
|
-
throw new Error(`Profile "${profile}" not found`);
|
|
1455
|
-
}
|
|
1456
|
-
if (entry.machine_user && entry.machine_user_override === "deny") {
|
|
1457
|
-
if (explicit && explicit !== entry.machine_user) {
|
|
1458
|
-
const details = explicitMachineUser.source === "env" ? `The machine user is being set to "${explicit}" via the TAILOR_PLATFORM_MACHINE_USER_NAME environment variable, which conflicts with this profile's pinned machine user "${entry.machine_user}".` : `This profile fixes the machine user to "${entry.machine_user}" for application-data commands.`;
|
|
1459
|
-
throw createCLIError({
|
|
1460
|
-
code: "PROFILE_MACHINE_USER_OVERRIDE_DENIED",
|
|
1461
|
-
message: `Profile "${profile}" denies overriding the machine user.`,
|
|
1462
|
-
details,
|
|
1463
|
-
suggestion: `Omit the machine user option, unset TAILOR_PLATFORM_MACHINE_USER_NAME, or run 'tailor profile update ${profile} --machine-user-override allow'.`
|
|
1464
|
-
});
|
|
1465
|
-
}
|
|
1466
|
-
return entry.machine_user;
|
|
1467
|
-
}
|
|
1468
|
-
return explicit || entry.machine_user;
|
|
1469
|
-
}
|
|
1470
|
-
/**
|
|
1471
|
-
* Load access token from environment variables, command options, or platform config.
|
|
1472
|
-
* In CLI context, profile env fallback is also handled by politty's arg env option.
|
|
1473
|
-
* Priority: env/TAILOR_PLATFORM_TOKEN > env/TAILOR_TOKEN (deprecated) > opts/profile > env/profile > config/currentUser > error
|
|
1474
|
-
* @param opts - Profile options
|
|
1475
|
-
* @returns Resolved access token
|
|
1476
|
-
*/
|
|
1477
|
-
async function loadAccessToken(opts) {
|
|
1478
|
-
const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
|
|
1479
|
-
const envToken = process.env.TAILOR_PLATFORM_TOKEN ?? process.env.TAILOR_TOKEN;
|
|
1480
|
-
if (envToken && !process.env.TAILOR_PLATFORM_TOKEN) logger.warn("TAILOR_TOKEN is deprecated. Please use TAILOR_PLATFORM_TOKEN instead.");
|
|
1481
|
-
if (envToken) {
|
|
1482
|
-
rememberPlatformConfigForToken(envToken, await loadPlatformClientConfig({
|
|
1483
|
-
profile,
|
|
1484
|
-
allowMissingProfile: true
|
|
1485
|
-
}));
|
|
1486
|
-
return envToken;
|
|
1487
|
-
}
|
|
1488
|
-
const pfConfig = await readPlatformConfig();
|
|
1489
|
-
const profileEntry = profile ? pfConfig.profiles[profile] : void 0;
|
|
1490
|
-
if (profile && !profileEntry) throw new Error(`Profile "${profile}" not found`);
|
|
1491
|
-
const user = profileEntry?.user ?? pfConfig.current_user;
|
|
1492
|
-
if (!user) throw new Error(multiline`
|
|
1493
|
-
Tailor Platform token not found.
|
|
1494
|
-
Please specify token via TAILOR_PLATFORM_TOKEN environment variable or login using 'tailor login' command.
|
|
1495
|
-
`);
|
|
1496
|
-
return (await fetchLatestToken(pfConfig, user, profileEntry ? platformConfigFromProfile(profileEntry) : void 0)).accessToken;
|
|
1497
|
-
}
|
|
1498
|
-
/**
|
|
1499
|
-
* Load platform connection settings from the active profile.
|
|
1500
|
-
* @param opts - Profile options
|
|
1501
|
-
* @returns Resolved platform connection settings, or undefined for the default environment
|
|
1502
|
-
*/
|
|
1503
|
-
async function loadPlatformClientConfig(opts) {
|
|
1504
|
-
const profile = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
|
|
1505
|
-
if (!profile) return;
|
|
1506
|
-
let pfConfig;
|
|
1507
|
-
try {
|
|
1508
|
-
pfConfig = await readPlatformConfig();
|
|
1509
|
-
} catch (error) {
|
|
1510
|
-
if (opts?.allowMissingProfile) return;
|
|
1511
|
-
throw error;
|
|
1512
|
-
}
|
|
1513
|
-
const profileEntry = pfConfig.profiles[profile];
|
|
1514
|
-
if (!profileEntry) {
|
|
1515
|
-
if (opts?.allowMissingProfile) return;
|
|
1516
|
-
throw new Error(`Profile "${profile}" not found`);
|
|
1517
|
-
}
|
|
1518
|
-
return platformConfigFromProfile(profileEntry);
|
|
1519
|
-
}
|
|
1520
|
-
/**
|
|
1521
|
-
* Load the Tailor Platform Console base URL from environment variables or the active profile.
|
|
1522
|
-
* @param opts - Profile options
|
|
1523
|
-
* @returns Resolved Console base URL
|
|
1524
|
-
*/
|
|
1525
|
-
async function loadConsoleBaseUrl(opts) {
|
|
1526
|
-
return getConsoleBaseUrl(await loadPlatformClientConfig(opts));
|
|
1527
|
-
}
|
|
1528
|
-
/**
|
|
1529
|
-
* Resolve the actual token values for a user, reading from keyring or config as appropriate.
|
|
1530
|
-
* @param userEntry - User entry from the config
|
|
1531
|
-
* @param user - User identifier
|
|
1532
|
-
* @param label - User-facing identifier used in error messages
|
|
1533
|
-
* @returns Access token and optional refresh token
|
|
1534
|
-
*/
|
|
1535
|
-
async function resolveTokens(userEntry, user, label = user) {
|
|
1536
|
-
if (userEntry.storage === "keyring") {
|
|
1537
|
-
const tokens = await loadKeyringTokens(user);
|
|
1538
|
-
if (!tokens) throw new Error(multiline`
|
|
1539
|
-
Credentials not found in OS keyring for "${label}".
|
|
1540
|
-
Please run 'tailor login' and try again.
|
|
1541
|
-
`);
|
|
1542
|
-
return tokens;
|
|
1543
|
-
}
|
|
1544
|
-
return {
|
|
1545
|
-
accessToken: userEntry.access_token,
|
|
1546
|
-
refreshToken: userEntry.refresh_token
|
|
1547
|
-
};
|
|
1548
|
-
}
|
|
1549
|
-
/**
|
|
1550
|
-
* Save tokens for a user, writing to keyring by default when available.
|
|
1551
|
-
* @param config - Platform config
|
|
1552
|
-
* @param user - User identifier
|
|
1553
|
-
* @param tokens - Token data to save
|
|
1554
|
-
* @param tokens.accessToken - Access token to save
|
|
1555
|
-
* @param tokens.refreshToken - Optional refresh token to save
|
|
1556
|
-
* @param expiresAt - Token expiration date
|
|
1557
|
-
* @param opts - Optional platform and user metadata
|
|
1558
|
-
*/
|
|
1559
|
-
async function saveUserTokens(config, user, tokens, expiresAt, opts = {}) {
|
|
1560
|
-
const userKey = platformUserKey(user, opts.platformConfig);
|
|
1561
|
-
const email = opts.email ?? config.users[userKey]?.email;
|
|
1562
|
-
if (await trySaveTokensInKeyring(userKey, tokens)) config.users[userKey] = {
|
|
1563
|
-
token_expires_at: expiresAt,
|
|
1564
|
-
storage: "keyring",
|
|
1565
|
-
...email ? { email } : {}
|
|
1566
|
-
};
|
|
1567
|
-
else {
|
|
1568
|
-
if (config.users[userKey]?.storage === "keyring") await deleteKeyringTokens(userKey);
|
|
1569
|
-
config.users[userKey] = {
|
|
1570
|
-
access_token: tokens.accessToken,
|
|
1571
|
-
refresh_token: tokens.refreshToken,
|
|
1572
|
-
token_expires_at: expiresAt,
|
|
1573
|
-
storage: "file",
|
|
1574
|
-
...email ? { email } : {}
|
|
1575
|
-
};
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
/**
|
|
1579
|
-
* Delete tokens for a user from keyring if applicable.
|
|
1580
|
-
* @param config - Platform config
|
|
1581
|
-
* @param user - User identifier
|
|
1582
|
-
* @param platformConfig - Optional platform connection settings
|
|
1583
|
-
* @param opts - Token lookup options
|
|
1584
|
-
*/
|
|
1585
|
-
async function deleteUserTokens(config, user, platformConfig, opts) {
|
|
1586
|
-
const { userKey, userEntry } = findUserEntry(config, user, platformConfig, opts);
|
|
1587
|
-
if (userEntry?.storage === "keyring") await deleteKeyringTokens(userKey);
|
|
1588
|
-
delete config.users[userKey];
|
|
1589
|
-
}
|
|
1590
|
-
/**
|
|
1591
|
-
* Resolve stored tokens for a user on the selected platform.
|
|
1592
|
-
* @param config - Platform config
|
|
1593
|
-
* @param user - User name
|
|
1594
|
-
* @param platformConfig - Optional platform connection settings
|
|
1595
|
-
* @param opts - Token lookup options
|
|
1596
|
-
* @returns Stored user entry and token values, or undefined when the user is not logged in
|
|
1597
|
-
*/
|
|
1598
|
-
async function loadStoredUserTokens(config, user, platformConfig, opts) {
|
|
1599
|
-
const { userKey, userEntry } = findUserEntry(config, user, platformConfig, opts);
|
|
1600
|
-
if (!userEntry) return void 0;
|
|
1601
|
-
return {
|
|
1602
|
-
userEntry,
|
|
1603
|
-
...await resolveTokens(userEntry, userKey, user)
|
|
1604
|
-
};
|
|
1605
|
-
}
|
|
1606
|
-
function updateUserReferences(config, fromUser, toUser) {
|
|
1607
|
-
if (fromUser === toUser) return;
|
|
1608
|
-
if (config.current_user === fromUser) config.current_user = toUser;
|
|
1609
|
-
for (const profile of Object.values(config.profiles)) if (profile?.user === fromUser) profile.user = toUser;
|
|
1610
|
-
}
|
|
1611
|
-
/**
|
|
1612
|
-
* Remove a legacy alias after a canonical user ID has been written.
|
|
1613
|
-
* @param config - Platform config
|
|
1614
|
-
* @param legacyUser - Previous user key
|
|
1615
|
-
* @param canonicalUser - Canonical user key
|
|
1616
|
-
* @param platformConfig - Platform settings for scoped token storage
|
|
1617
|
-
*/
|
|
1618
|
-
async function removeLegacyUserAlias(config, legacyUser, canonicalUser, platformConfig) {
|
|
1619
|
-
if (legacyUser === canonicalUser) return;
|
|
1620
|
-
updateUserReferences(config, legacyUser, canonicalUser);
|
|
1621
|
-
const canonicalKey = platformUserKey(canonicalUser, platformConfig);
|
|
1622
|
-
const legacyKeys = /* @__PURE__ */ new Set([legacyUser, platformUserKey(legacyUser, platformConfig)]);
|
|
1623
|
-
for (const legacyKey of legacyKeys) {
|
|
1624
|
-
if (legacyKey === canonicalKey) continue;
|
|
1625
|
-
if (config.users[legacyKey]?.storage === "keyring") await deleteKeyringTokens(legacyKey);
|
|
1626
|
-
delete config.users[legacyKey];
|
|
1627
|
-
}
|
|
1628
|
-
}
|
|
1629
|
-
function shouldResolveSubjectOnRefresh(user, userEntry) {
|
|
1630
|
-
return Boolean(userEntry.email || inferEmailFromUserId(user));
|
|
1631
|
-
}
|
|
1632
|
-
/**
|
|
1633
|
-
* Fetch the latest access token, refreshing if necessary.
|
|
1634
|
-
* @param config - Platform config
|
|
1635
|
-
* @param user - User identifier
|
|
1636
|
-
* @param platformConfig - Optional platform connection settings
|
|
1637
|
-
* @returns Latest access token and the canonical user ID it is stored under
|
|
1638
|
-
* (the resolved subject when a legacy email key was migrated during refresh,
|
|
1639
|
-
* otherwise the matching config user)
|
|
1640
|
-
*/
|
|
1641
|
-
async function fetchLatestToken(config, user, platformConfig) {
|
|
1642
|
-
const { userKey: storedUser, userEntry } = findUserEntry(config, user, platformConfig);
|
|
1643
|
-
if (!userEntry) throw new Error(multiline`
|
|
1644
|
-
User "${user}" not found.
|
|
1645
|
-
Please verify your user name and login using 'tailor login' command.
|
|
1646
|
-
`);
|
|
1647
|
-
const storedConfigUser = userFromPlatformUserKey(storedUser, platformConfig);
|
|
1648
|
-
const tokens = await resolveTokens(userEntry, storedUser, user);
|
|
1649
|
-
if (new Date(userEntry.token_expires_at) > /* @__PURE__ */ new Date()) {
|
|
1650
|
-
rememberPlatformConfigForToken(tokens.accessToken, platformConfig);
|
|
1651
|
-
return {
|
|
1652
|
-
accessToken: tokens.accessToken,
|
|
1653
|
-
user: storedConfigUser
|
|
1654
|
-
};
|
|
1655
|
-
}
|
|
1656
|
-
if (!tokens.refreshToken) throw new Error(multiline`
|
|
1657
|
-
Token expired.
|
|
1658
|
-
Please run 'tailor login' and try again.
|
|
1659
|
-
`);
|
|
1660
|
-
const client = initOAuth2Client(platformConfig);
|
|
1661
|
-
let resp;
|
|
1662
|
-
try {
|
|
1663
|
-
resp = await client.refreshToken({
|
|
1664
|
-
accessToken: tokens.accessToken,
|
|
1665
|
-
refreshToken: tokens.refreshToken,
|
|
1666
|
-
expiresAt: Date.parse(userEntry.token_expires_at)
|
|
1667
|
-
});
|
|
1668
|
-
} catch {
|
|
1669
|
-
throw new Error(multiline`
|
|
1670
|
-
Failed to refresh token. Your session may have expired.
|
|
1671
|
-
Please run 'tailor login' and try again.
|
|
1672
|
-
`);
|
|
1673
|
-
}
|
|
1674
|
-
const newExpiresAt = new Date(assertDefined(resp.expiresAt, "token refresh response missing expiresAt")).toISOString();
|
|
1675
|
-
let resolvedUser = storedConfigUser;
|
|
1676
|
-
const previousEmail = userEntry.email ?? inferEmailFromUserId(user) ?? inferEmailFromUserId(storedConfigUser);
|
|
1677
|
-
let email = previousEmail;
|
|
1678
|
-
if (shouldResolveSubjectOnRefresh(user, userEntry) || shouldResolveSubjectOnRefresh(storedConfigUser, userEntry)) try {
|
|
1679
|
-
const userInfo = await fetchUserInfo(resp.accessToken, platformConfig);
|
|
1680
|
-
resolvedUser = userInfo.sub;
|
|
1681
|
-
email = userInfo.email;
|
|
1682
|
-
} catch (error) {
|
|
1683
|
-
logger.debug(`Failed to resolve refreshed token user info: ${String(error)}`);
|
|
1684
|
-
}
|
|
1685
|
-
await saveUserTokens(config, resolvedUser, {
|
|
1686
|
-
accessToken: resp.accessToken,
|
|
1687
|
-
refreshToken: resp.refreshToken ?? void 0
|
|
1688
|
-
}, newExpiresAt, {
|
|
1689
|
-
platformConfig,
|
|
1690
|
-
email
|
|
1691
|
-
});
|
|
1692
|
-
await removeLegacyUserAlias(config, user, resolvedUser, platformConfig);
|
|
1693
|
-
if (storedUser !== platformUserKey(resolvedUser, platformConfig)) {
|
|
1694
|
-
if (config.users[storedUser]?.storage === "keyring") await deleteKeyringTokens(storedUser);
|
|
1695
|
-
delete config.users[storedUser];
|
|
1696
|
-
}
|
|
1697
|
-
if (previousEmail && email && previousEmail !== email) logger.info(`Updated local user email from "${previousEmail}" to "${email}".`);
|
|
1698
|
-
writePlatformConfig(config);
|
|
1699
|
-
rememberPlatformConfigForToken(resp.accessToken, platformConfig);
|
|
1700
|
-
return {
|
|
1701
|
-
accessToken: resp.accessToken,
|
|
1702
|
-
user: resolvedUser
|
|
1703
|
-
};
|
|
1704
|
-
}
|
|
1705
|
-
const DEFAULT_CONFIG_FILENAME = "tailor.config.ts";
|
|
1706
|
-
/**
|
|
1707
|
-
* Load config path from command options, environment variables, or search parent directories.
|
|
1708
|
-
* In CLI context, env fallback is also handled by politty's arg env option.
|
|
1709
|
-
* Priority: opts/config > env/config > search parent directories
|
|
1710
|
-
* @param configPath - Optional explicit config path
|
|
1711
|
-
* @returns Resolved config path or undefined
|
|
1712
|
-
*/
|
|
1713
|
-
function loadConfigPath(configPath) {
|
|
1714
|
-
if (configPath) return configPath;
|
|
1715
|
-
if (process.env.TAILOR_CONFIG_PATH) return process.env.TAILOR_CONFIG_PATH;
|
|
1716
|
-
return findUpSync(DEFAULT_CONFIG_FILENAME);
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
//#endregion
|
|
1720
|
-
//#region src/cli/shared/mock.ts
|
|
1721
|
-
/**
|
|
1722
|
-
* Install a stub `globalThis.tailordb` so that user code loaded by the CLI
|
|
1723
|
-
* (e.g. via `createGetDB` in `@tailor-platform/sdk/kysely`) can reference
|
|
1724
|
-
* `tailordb.Client` without hitting a `ReferenceError`. The CLI never
|
|
1725
|
-
* actually executes the user code paths that issue queries, so a no-op
|
|
1726
|
-
* client suffices.
|
|
1727
|
-
*
|
|
1728
|
-
* Exposed as a function (rather than a top-level statement) so that
|
|
1729
|
-
* `package.json#sideEffects` can keep the file marked side-effect-free
|
|
1730
|
-
* without bundlers eliminating the install step.
|
|
1731
|
-
*/
|
|
1732
|
-
function installCliTailordbStub() {
|
|
1733
|
-
globalThis.tailordb = { Client: class {
|
|
1734
|
-
constructor(_config) {}
|
|
1735
|
-
async connect() {}
|
|
1736
|
-
async end() {}
|
|
1737
|
-
async queryObject() {
|
|
1738
|
-
return {};
|
|
1739
|
-
}
|
|
1740
|
-
} };
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
//#endregion
|
|
1744
|
-
//#region src/cli/shared/config-loader.ts
|
|
1745
|
-
/**
|
|
1746
|
-
* Load Tailor configuration file and associated plugins.
|
|
1747
|
-
* @param configPath - Optional explicit config path
|
|
1748
|
-
* @param options - Optional module import behavior.
|
|
1749
|
-
* @returns Loaded config, plugins, and config path
|
|
1750
|
-
*/
|
|
1751
|
-
async function loadConfig(configPath, options = {}) {
|
|
1752
|
-
installCliTailordbStub();
|
|
1753
|
-
const foundPath = loadConfigPath(configPath);
|
|
1754
|
-
if (!foundPath) throw new Error("Configuration file not found: tailor.config.ts not found in current or parent directories");
|
|
1755
|
-
const resolvedPath = path.resolve(process.cwd(), foundPath);
|
|
1756
|
-
if (!fs$1.existsSync(resolvedPath)) throw new Error(`Configuration file not found: ${configPath}`);
|
|
1757
|
-
const configUrl = pathToFileURL(resolvedPath);
|
|
1758
|
-
if (options.importNonce) configUrl.searchParams.set("tailorImportNonce", options.importNonce);
|
|
1759
|
-
const configModule = await import(configUrl.href);
|
|
1760
|
-
if (!configModule || !configModule.default) throw new Error("Invalid Tailor config module: default export not found");
|
|
1761
|
-
const validated = AppConfigSchema.safeParse(configModule.default);
|
|
1762
|
-
if (!validated.success) {
|
|
1763
|
-
const issues = validated.error.issues.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`).join("\n");
|
|
1764
|
-
throw new Error(`Invalid Tailor config in ${resolvedPath}:\n${issues}`);
|
|
1765
|
-
}
|
|
1766
|
-
const allPlugins = [];
|
|
1767
|
-
for (const value of Object.values(configModule)) if (Array.isArray(value)) {
|
|
1768
|
-
const pluginParsed = value.reduce((acc, item) => {
|
|
1769
|
-
if (!acc.success) return acc;
|
|
1770
|
-
const result = PluginConfigSchema.safeParse(item);
|
|
1771
|
-
if (result.success) acc.items.push(result.data);
|
|
1772
|
-
else acc.success = false;
|
|
1773
|
-
return acc;
|
|
1774
|
-
}, {
|
|
1775
|
-
success: true,
|
|
1776
|
-
items: []
|
|
1777
|
-
});
|
|
1778
|
-
if (pluginParsed.success && pluginParsed.items.length > 0) allPlugins.push(...pluginParsed.items);
|
|
1779
|
-
}
|
|
1780
|
-
return {
|
|
1781
|
-
config: {
|
|
1782
|
-
...configModule.default,
|
|
1783
|
-
path: resolvedPath
|
|
1784
|
-
},
|
|
1785
|
-
plugins: allPlugins
|
|
1786
|
-
};
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
671
|
//#endregion
|
|
1790
672
|
//#region src/cli/cache/hasher.ts
|
|
1791
673
|
/**
|
|
@@ -1802,7 +684,7 @@ function hashContent(content) {
|
|
|
1802
684
|
* @returns Hex-encoded SHA-256 hash of the file content
|
|
1803
685
|
*/
|
|
1804
686
|
function hashFile(filePath) {
|
|
1805
|
-
const content = fs
|
|
687
|
+
const content = fs.readFileSync(filePath);
|
|
1806
688
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
1807
689
|
}
|
|
1808
690
|
/**
|
|
@@ -1931,7 +813,10 @@ function createBundleCache(store) {
|
|
|
1931
813
|
return;
|
|
1932
814
|
}
|
|
1933
815
|
if (currentHash !== entry.inputHash) return;
|
|
1934
|
-
|
|
816
|
+
const content = store.restoreBundleContent(cacheKey);
|
|
817
|
+
const output = entry.outputFiles.find((file) => file.outputPath === cacheKey);
|
|
818
|
+
if (content === void 0 || !output || hashContent(content) !== output.contentHash) return;
|
|
819
|
+
return content;
|
|
1935
820
|
}
|
|
1936
821
|
function save(params) {
|
|
1937
822
|
const { kind, namespace, name, sourceFile, content, dependencyPaths, contextHash } = params;
|
|
@@ -1978,7 +863,7 @@ function resolveRelativePluginImportPath(pluginImportPath, baseDirs) {
|
|
|
1978
863
|
if (!pluginImportPath.startsWith(".")) return null;
|
|
1979
864
|
for (const baseDir of baseDirs) {
|
|
1980
865
|
const absolutePath = path.resolve(baseDir, pluginImportPath);
|
|
1981
|
-
if (fs
|
|
866
|
+
if (fs.existsSync(absolutePath)) return absolutePath;
|
|
1982
867
|
}
|
|
1983
868
|
return null;
|
|
1984
869
|
}
|
|
@@ -2062,13 +947,13 @@ function generatePluginExecutorFiles(executors, outputDir, typeGenerationResult,
|
|
|
2062
947
|
function generateSingleExecutorFile(info, outputDir, typeGenerationResult, sourceTypeInfoMap, baseDirs = []) {
|
|
2063
948
|
const pluginDir = sanitizePluginId$1(info.pluginId);
|
|
2064
949
|
const executorOutputDir = path.join(outputDir, pluginDir, "executors");
|
|
2065
|
-
fs
|
|
950
|
+
fs.mkdirSync(executorOutputDir, { recursive: true });
|
|
2066
951
|
const fileName = sanitizeExecutorFileName(info.executor.name);
|
|
2067
952
|
const filePath = path.join(executorOutputDir, `${fileName}.ts`);
|
|
2068
953
|
let content;
|
|
2069
954
|
if (isPluginExecutorWithFile(info.executor)) content = generateExecutorFileContentNew(info, info.executor, outputDir, typeGenerationResult, sourceTypeInfoMap, baseDirs);
|
|
2070
955
|
else content = generateExecutorFileContentLegacy(info.executor);
|
|
2071
|
-
fs
|
|
956
|
+
fs.writeFileSync(filePath, content);
|
|
2072
957
|
return filePath;
|
|
2073
958
|
}
|
|
2074
959
|
/**
|
|
@@ -2300,7 +1185,7 @@ function extractDynamicImportSpecifier(resolve) {
|
|
|
2300
1185
|
function resolvePluginBaseDir(pluginImportPath, baseDirs) {
|
|
2301
1186
|
if (pluginImportPath.startsWith(".")) {
|
|
2302
1187
|
const resolvedPath = resolveRelativePluginImportPath(pluginImportPath, baseDirs) ?? path.resolve(baseDirs[0] ?? process.cwd(), pluginImportPath);
|
|
2303
|
-
if (fs
|
|
1188
|
+
if (fs.existsSync(resolvedPath)) return fs.statSync(resolvedPath).isDirectory() ? resolvedPath : path.dirname(resolvedPath);
|
|
2304
1189
|
return path.extname(resolvedPath) ? path.dirname(resolvedPath) : resolvedPath;
|
|
2305
1190
|
}
|
|
2306
1191
|
for (const baseDir of baseDirs) try {
|
|
@@ -2387,11 +1272,11 @@ function generatePluginTypeFiles(types, outputDir) {
|
|
|
2387
1272
|
seenTypeNames.set(info.type.name, info);
|
|
2388
1273
|
const pluginDir = sanitizePluginId(info.pluginId);
|
|
2389
1274
|
const typeOutputDir = path.join(outputDir, pluginDir, "types");
|
|
2390
|
-
fs
|
|
1275
|
+
fs.mkdirSync(typeOutputDir, { recursive: true });
|
|
2391
1276
|
const fileName = `${toKebabCase(info.type.name)}.ts`;
|
|
2392
1277
|
const filePath = path.join(typeOutputDir, fileName);
|
|
2393
1278
|
const content = generateTypeFileContent(info);
|
|
2394
|
-
fs
|
|
1279
|
+
fs.writeFileSync(filePath, content);
|
|
2395
1280
|
generatedFiles.push(filePath);
|
|
2396
1281
|
const relativePath = path.relative(outputDir, filePath);
|
|
2397
1282
|
typeFilePaths.set(info.type.name, relativePath);
|
|
@@ -2513,112 +1398,18 @@ function toCamelCase(str) {
|
|
|
2513
1398
|
}
|
|
2514
1399
|
|
|
2515
1400
|
//#endregion
|
|
2516
|
-
//#region src/cli/services/
|
|
1401
|
+
//#region src/cli/services/workflow/ast-utils.ts
|
|
2517
1402
|
/**
|
|
2518
|
-
*
|
|
2519
|
-
*
|
|
2520
|
-
*
|
|
2521
|
-
* sharing the same output directory would otherwise conflict.
|
|
2522
|
-
* @param outputDir - Directory to clean
|
|
1403
|
+
* Read an import or export name from an OXC identifier or string-literal node.
|
|
1404
|
+
* @param node - Import/export name node
|
|
1405
|
+
* @returns Module binding name, or undefined for an unsupported node
|
|
2523
1406
|
*/
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
1407
|
+
function getModuleExportName(node) {
|
|
1408
|
+
if (!node || typeof node !== "object") return void 0;
|
|
1409
|
+
const exportName = node;
|
|
1410
|
+
if (exportName.name) return exportName.name;
|
|
1411
|
+
return typeof exportName.value === "string" ? exportName.value : void 0;
|
|
2527
1412
|
}
|
|
2528
|
-
|
|
2529
|
-
//#endregion
|
|
2530
|
-
//#region src/cli/shared/bundle-log-level.ts
|
|
2531
|
-
const INFO_LEVEL_CONSOLE_METHODS = [
|
|
2532
|
-
"console.info",
|
|
2533
|
-
"console.table",
|
|
2534
|
-
"console.dir",
|
|
2535
|
-
"console.dirxml",
|
|
2536
|
-
"console.count",
|
|
2537
|
-
"console.countReset",
|
|
2538
|
-
"console.time",
|
|
2539
|
-
"console.timeLog",
|
|
2540
|
-
"console.timeEnd",
|
|
2541
|
-
"console.group",
|
|
2542
|
-
"console.groupCollapsed",
|
|
2543
|
-
"console.groupEnd",
|
|
2544
|
-
"console.clear"
|
|
2545
|
-
];
|
|
2546
|
-
const DEBUG_LEVEL_CONSOLE_METHODS = [
|
|
2547
|
-
"console.debug",
|
|
2548
|
-
"console.log",
|
|
2549
|
-
"console.trace"
|
|
2550
|
-
];
|
|
2551
|
-
const WARN_LEVEL_CONSOLE_METHODS = ["console.warn"];
|
|
2552
|
-
const ERROR_LEVEL_CONSOLE_METHODS = ["console.error"];
|
|
2553
|
-
const MANUAL_PURE_FUNCTIONS_BY_LOG_LEVEL = {
|
|
2554
|
-
DEBUG: [],
|
|
2555
|
-
INFO: DEBUG_LEVEL_CONSOLE_METHODS,
|
|
2556
|
-
WARN: [...DEBUG_LEVEL_CONSOLE_METHODS, ...INFO_LEVEL_CONSOLE_METHODS],
|
|
2557
|
-
ERROR: [
|
|
2558
|
-
...DEBUG_LEVEL_CONSOLE_METHODS,
|
|
2559
|
-
...INFO_LEVEL_CONSOLE_METHODS,
|
|
2560
|
-
...WARN_LEVEL_CONSOLE_METHODS
|
|
2561
|
-
],
|
|
2562
|
-
SILENT: [
|
|
2563
|
-
...DEBUG_LEVEL_CONSOLE_METHODS,
|
|
2564
|
-
...INFO_LEVEL_CONSOLE_METHODS,
|
|
2565
|
-
...WARN_LEVEL_CONSOLE_METHODS,
|
|
2566
|
-
...ERROR_LEVEL_CONSOLE_METHODS
|
|
2567
|
-
]
|
|
2568
|
-
};
|
|
2569
|
-
function normalizeBundleLogLevel(value) {
|
|
2570
|
-
const normalized = value.trim().toUpperCase();
|
|
2571
|
-
return isLogLevel(normalized) ? normalized : void 0;
|
|
2572
|
-
}
|
|
2573
|
-
function resolveBundleLogLevel(configValue) {
|
|
2574
|
-
if (configValue === void 0) return "DEBUG";
|
|
2575
|
-
const resolved = normalizeBundleLogLevel(configValue);
|
|
2576
|
-
if (resolved) return resolved;
|
|
2577
|
-
throw new Error(`Invalid logLevel "${configValue}". Expected one of: ${LOG_LEVELS.join(", ")}`);
|
|
2578
|
-
}
|
|
2579
|
-
function manualPureFunctionsForLogLevel(logLevel) {
|
|
2580
|
-
return MANUAL_PURE_FUNCTIONS_BY_LOG_LEVEL[logLevel];
|
|
2581
|
-
}
|
|
2582
|
-
function createLogLevelTreeshakeOptions(logLevel) {
|
|
2583
|
-
const manualPureFunctions = manualPureFunctionsForLogLevel(logLevel);
|
|
2584
|
-
return manualPureFunctions.length > 0 ? { manualPureFunctions } : {};
|
|
2585
|
-
}
|
|
2586
|
-
|
|
2587
|
-
//#endregion
|
|
2588
|
-
//#region src/cli/shared/function-treeshake.ts
|
|
2589
|
-
const BASE_FUNCTION_TREESHAKE_OPTIONS = {
|
|
2590
|
-
moduleSideEffects: false,
|
|
2591
|
-
annotations: true,
|
|
2592
|
-
unknownGlobalSideEffects: false
|
|
2593
|
-
};
|
|
2594
|
-
function mergeFunctionTreeshakeOptions(fragments) {
|
|
2595
|
-
const merged = {};
|
|
2596
|
-
const manualPureFunctions = /* @__PURE__ */ new Set();
|
|
2597
|
-
for (const fragment of fragments) {
|
|
2598
|
-
Object.assign(merged, fragment);
|
|
2599
|
-
for (const name of fragment.manualPureFunctions ?? []) manualPureFunctions.add(name);
|
|
2600
|
-
}
|
|
2601
|
-
if (manualPureFunctions.size > 0) merged.manualPureFunctions = [...manualPureFunctions];
|
|
2602
|
-
else delete merged.manualPureFunctions;
|
|
2603
|
-
return merged;
|
|
2604
|
-
}
|
|
2605
|
-
function composeFunctionTreeshakeOptions(fragments = []) {
|
|
2606
|
-
return mergeFunctionTreeshakeOptions([BASE_FUNCTION_TREESHAKE_OPTIONS, ...fragments]);
|
|
2607
|
-
}
|
|
2608
|
-
|
|
2609
|
-
//#endregion
|
|
2610
|
-
//#region src/cli/shared/platform-bundle-plugin.ts
|
|
2611
|
-
const GATE = /(?<![\w$.])process\.env\.__TAILOR_PLATFORM_BUNDLE\b/g;
|
|
2612
|
-
const platformBundleDefinePlugin = {
|
|
2613
|
-
name: "tailor-platform-bundle-define",
|
|
2614
|
-
transform(code) {
|
|
2615
|
-
if (!code.includes("process.env.__TAILOR_PLATFORM_BUNDLE")) return null;
|
|
2616
|
-
return { code: code.replace(GATE, "true") };
|
|
2617
|
-
}
|
|
2618
|
-
};
|
|
2619
|
-
|
|
2620
|
-
//#endregion
|
|
2621
|
-
//#region src/cli/services/workflow/ast-utils.ts
|
|
2622
1413
|
/**
|
|
2623
1414
|
* Check if a module source is from the Tailor SDK package (including subpaths)
|
|
2624
1415
|
* @param source - Module source string
|
|
@@ -2662,7 +1453,7 @@ function getTriggerCallInfo(node, sourceText) {
|
|
|
2662
1453
|
const callee = callExpr.callee;
|
|
2663
1454
|
if (callee.type !== "MemberExpression") return null;
|
|
2664
1455
|
const memberExpr = callee;
|
|
2665
|
-
if (memberExpr.computed || memberExpr.
|
|
1456
|
+
if (memberExpr.computed || memberExpr.property.name !== "trigger" || memberExpr.object.type !== "Identifier") return null;
|
|
2666
1457
|
return {
|
|
2667
1458
|
identifierName: memberExpr.object.name,
|
|
2668
1459
|
callRange: {
|
|
@@ -2749,20 +1540,6 @@ function findStatementEnd(source, position) {
|
|
|
2749
1540
|
if (i < source.length && source[i] === "\n") i++;
|
|
2750
1541
|
return i;
|
|
2751
1542
|
}
|
|
2752
|
-
/**
|
|
2753
|
-
* Resolve a relative path from a base directory
|
|
2754
|
-
* Simple implementation that handles ./ and ../ prefixes
|
|
2755
|
-
* @param baseDir - Base directory
|
|
2756
|
-
* @param relativePath - Relative path to resolve
|
|
2757
|
-
* @returns Resolved absolute path
|
|
2758
|
-
*/
|
|
2759
|
-
function resolvePath(baseDir, relativePath) {
|
|
2760
|
-
const parts = relativePath.replace(/\\/g, "/").split("/");
|
|
2761
|
-
const baseParts = baseDir.replace(/\\/g, "/").split("/");
|
|
2762
|
-
for (const part of parts) if (part === ".") {} else if (part === "..") baseParts.pop();
|
|
2763
|
-
else baseParts.push(part);
|
|
2764
|
-
return baseParts.join("/");
|
|
2765
|
-
}
|
|
2766
1543
|
|
|
2767
1544
|
//#endregion
|
|
2768
1545
|
//#region src/cli/services/workflow/sdk-binding-collector.ts
|
|
@@ -2906,43 +1683,6 @@ function findAllJobs(program, _sourceText) {
|
|
|
2906
1683
|
walk(program);
|
|
2907
1684
|
return jobs;
|
|
2908
1685
|
}
|
|
2909
|
-
/**
|
|
2910
|
-
* Build a map from export name to job name from detected jobs
|
|
2911
|
-
* @param jobs - Detected job locations
|
|
2912
|
-
* @returns Map from export name to job name
|
|
2913
|
-
*/
|
|
2914
|
-
function buildJobNameMap(jobs) {
|
|
2915
|
-
const map = /* @__PURE__ */ new Map();
|
|
2916
|
-
for (const job of jobs) if (job.exportName) map.set(job.exportName, job.name);
|
|
2917
|
-
return map;
|
|
2918
|
-
}
|
|
2919
|
-
/**
|
|
2920
|
-
* Detect all .trigger() calls in the source code
|
|
2921
|
-
* Returns information about each trigger call for transformation
|
|
2922
|
-
* @param program - Parsed TypeScript program
|
|
2923
|
-
* @param sourceText - Source code text
|
|
2924
|
-
* @returns Detected trigger calls
|
|
2925
|
-
*/
|
|
2926
|
-
function detectTriggerCalls(program, sourceText) {
|
|
2927
|
-
const calls = [];
|
|
2928
|
-
function walk(node) {
|
|
2929
|
-
if (!node || typeof node !== "object") return;
|
|
2930
|
-
const triggerCall = getTriggerCallInfo(node, sourceText);
|
|
2931
|
-
if (triggerCall) calls.push({
|
|
2932
|
-
identifierName: triggerCall.identifierName,
|
|
2933
|
-
callRange: triggerCall.callRange,
|
|
2934
|
-
argsText: triggerCall.argsText,
|
|
2935
|
-
optionsText: triggerCall.optionsText
|
|
2936
|
-
});
|
|
2937
|
-
for (const key of Object.keys(node)) {
|
|
2938
|
-
const child = node[key];
|
|
2939
|
-
if (Array.isArray(child)) child.forEach((c) => walk(c));
|
|
2940
|
-
else if (child && typeof child === "object") walk(child);
|
|
2941
|
-
}
|
|
2942
|
-
}
|
|
2943
|
-
walk(program);
|
|
2944
|
-
return calls;
|
|
2945
|
-
}
|
|
2946
1686
|
|
|
2947
1687
|
//#endregion
|
|
2948
1688
|
//#region src/cli/services/workflow/workflow-detector.ts
|
|
@@ -2994,51 +1734,113 @@ function findAllWorkflows(program, _sourceText) {
|
|
|
2994
1734
|
walk(program);
|
|
2995
1735
|
return workflows;
|
|
2996
1736
|
}
|
|
1737
|
+
|
|
1738
|
+
//#endregion
|
|
1739
|
+
//#region src/cli/shared/trigger-context.ts
|
|
2997
1740
|
/**
|
|
2998
|
-
*
|
|
2999
|
-
* @param
|
|
3000
|
-
* @returns
|
|
3001
|
-
*/
|
|
3002
|
-
function buildWorkflowNameMap(workflows) {
|
|
3003
|
-
const map = /* @__PURE__ */ new Map();
|
|
3004
|
-
for (const workflow of workflows) if (workflow.exportName) map.set(workflow.exportName, workflow.name);
|
|
3005
|
-
return map;
|
|
3006
|
-
}
|
|
3007
|
-
/**
|
|
3008
|
-
* Detect default imports in a source file and return a map from local name to import source
|
|
3009
|
-
* @param program - Parsed TypeScript program
|
|
3010
|
-
* @returns Map from local name to import source
|
|
1741
|
+
* Normalize a source module path for trigger binding lookup.
|
|
1742
|
+
* @param filePath - Source file path or extensionless relative import path
|
|
1743
|
+
* @returns Absolute path without a JavaScript or TypeScript extension
|
|
3011
1744
|
*/
|
|
3012
|
-
function
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
1745
|
+
function normalizeFilePath(filePath) {
|
|
1746
|
+
return path.resolve(filePath.replace(/[?#].*$/, "")).replace(/\.(ts|mts|cts|js|mjs|cjs)$/, "");
|
|
1747
|
+
}
|
|
1748
|
+
function createModuleBindings(program, source) {
|
|
1749
|
+
const localBindings = /* @__PURE__ */ new Map();
|
|
1750
|
+
const exports = /* @__PURE__ */ new Map();
|
|
1751
|
+
for (const workflow of findAllWorkflows(program, source)) {
|
|
1752
|
+
const target = {
|
|
1753
|
+
kind: "workflow",
|
|
1754
|
+
name: workflow.name
|
|
1755
|
+
};
|
|
1756
|
+
if (workflow.exportName) localBindings.set(workflow.exportName, target);
|
|
1757
|
+
if (workflow.isDefaultExport) exports.set("default", target);
|
|
1758
|
+
}
|
|
1759
|
+
for (const job of findAllJobs(program, source)) if (job.exportName) localBindings.set(job.exportName, {
|
|
1760
|
+
kind: "job",
|
|
1761
|
+
name: job.name
|
|
1762
|
+
});
|
|
1763
|
+
for (const statement of program.body) {
|
|
1764
|
+
if (statement.type === "ExportDefaultDeclaration") {
|
|
1765
|
+
const declaration = statement.declaration;
|
|
1766
|
+
if (declaration?.type === "Identifier") {
|
|
1767
|
+
const target = localBindings.get(declaration.name);
|
|
1768
|
+
if (target) exports.set("default", target);
|
|
3024
1769
|
}
|
|
1770
|
+
continue;
|
|
3025
1771
|
}
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
1772
|
+
if (statement.type !== "ExportNamedDeclaration") continue;
|
|
1773
|
+
const declaration = statement.declaration;
|
|
1774
|
+
if (declaration?.type === "VariableDeclaration") for (const declarator of declaration.declarations) {
|
|
1775
|
+
const id = declarator.id;
|
|
1776
|
+
if (id?.type !== "Identifier") continue;
|
|
1777
|
+
const localName = id.name;
|
|
1778
|
+
const target = localBindings.get(localName);
|
|
1779
|
+
if (target) exports.set(localName, target);
|
|
1780
|
+
}
|
|
1781
|
+
if (statement.source) continue;
|
|
1782
|
+
for (const specifier of statement.specifiers ?? []) {
|
|
1783
|
+
const localName = getModuleExportName(specifier.local);
|
|
1784
|
+
const exportedName = getModuleExportName(specifier.exported);
|
|
1785
|
+
if (!localName || !exportedName) continue;
|
|
1786
|
+
const target = localBindings.get(localName);
|
|
1787
|
+
if (target) exports.set(exportedName, target);
|
|
3030
1788
|
}
|
|
3031
1789
|
}
|
|
3032
|
-
|
|
3033
|
-
|
|
1790
|
+
return {
|
|
1791
|
+
localBindings,
|
|
1792
|
+
exports
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Build trigger context from configured workflow source files.
|
|
1797
|
+
* @param workflowConfig - Workflow file loading configuration
|
|
1798
|
+
* @param authNamespace - Auth service namespace (optional, used for string-literal invoker expansion)
|
|
1799
|
+
* @returns Module-local workflow and job binding metadata
|
|
1800
|
+
*/
|
|
1801
|
+
async function buildTriggerContext(workflowConfig, authNamespace) {
|
|
1802
|
+
const modules = /* @__PURE__ */ new Map();
|
|
1803
|
+
if (!workflowConfig) return {
|
|
1804
|
+
modules,
|
|
1805
|
+
authNamespace
|
|
1806
|
+
};
|
|
1807
|
+
for (const file of loadFilesWithIgnores(workflowConfig)) try {
|
|
1808
|
+
const source = await fs.promises.readFile(file, "utf-8");
|
|
1809
|
+
const { program } = parseSync("input.ts", source);
|
|
1810
|
+
modules.set(normalizeFilePath(file), createModuleBindings(program, source));
|
|
1811
|
+
} catch (error) {
|
|
1812
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1813
|
+
logger.warn(`Failed to process workflow file ${file}: ${errorMessage}`, { mode: "stream" });
|
|
1814
|
+
}
|
|
1815
|
+
return {
|
|
1816
|
+
modules,
|
|
1817
|
+
authNamespace
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
function sortedTargets(bindings) {
|
|
1821
|
+
return [...bindings].toSorted(([a], [b]) => a.localeCompare(b)).map(([binding, target]) => [
|
|
1822
|
+
binding,
|
|
1823
|
+
target.kind,
|
|
1824
|
+
target.name
|
|
1825
|
+
]);
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Serialize trigger context to a deterministic cache input.
|
|
1829
|
+
* @param context - Trigger context to serialize
|
|
1830
|
+
* @returns Deterministic string, or an empty string when context is absent
|
|
1831
|
+
*/
|
|
1832
|
+
function serializeTriggerContext(context) {
|
|
1833
|
+
if (!context) return "";
|
|
1834
|
+
const modules = [...context.modules].toSorted(([a], [b]) => a.localeCompare(b)).map(([file, bindings]) => [
|
|
1835
|
+
file,
|
|
1836
|
+
sortedTargets(bindings.localBindings),
|
|
1837
|
+
sortedTargets(bindings.exports)
|
|
1838
|
+
]);
|
|
1839
|
+
return JSON.stringify(modules) + (context.authNamespace ?? "");
|
|
3034
1840
|
}
|
|
3035
1841
|
|
|
3036
1842
|
//#endregion
|
|
3037
1843
|
//#region src/cli/services/workflow/trigger-transformer.ts
|
|
3038
|
-
/**
|
|
3039
|
-
* Name of the injected runtime normalizer helper. Chosen to be unique enough
|
|
3040
|
-
* to avoid collisions with user code.
|
|
3041
|
-
*/
|
|
3042
1844
|
const NORMALIZER_IDENTIFIER = "__tailor_normalizeTriggerOptions";
|
|
3043
1845
|
/**
|
|
3044
1846
|
* Build the source text of the injected normalizer helper.
|
|
@@ -3054,336 +1856,306 @@ const NORMALIZER_IDENTIFIER = "__tailor_normalizeTriggerOptions";
|
|
|
3054
1856
|
function buildNormalizerHelperSource(authNamespace) {
|
|
3055
1857
|
return `const ${NORMALIZER_IDENTIFIER} = (o) => { if (!o) return o; const { invoker, ...rest } = o; return typeof invoker === "string" ? { ...rest, authInvoker: { namespace: ${JSON.stringify(authNamespace)}, machineUserName: invoker } } : typeof invoker === "object" ? { ...rest, authInvoker: invoker } : o; };\n`;
|
|
3056
1858
|
}
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
1859
|
+
function collectBindingNames(node, names) {
|
|
1860
|
+
if (!node || typeof node !== "object") return;
|
|
1861
|
+
switch (node.type) {
|
|
1862
|
+
case "Identifier":
|
|
1863
|
+
names.add(node.name);
|
|
1864
|
+
return;
|
|
1865
|
+
case "ObjectPattern":
|
|
1866
|
+
for (const property of node.properties) collectBindingNames(property.type === "RestElement" ? property.argument : property.value, names);
|
|
1867
|
+
return;
|
|
1868
|
+
case "ArrayPattern":
|
|
1869
|
+
for (const element of node.elements) collectBindingNames(element, names);
|
|
1870
|
+
return;
|
|
1871
|
+
case "AssignmentPattern":
|
|
1872
|
+
collectBindingNames(node.left, names);
|
|
1873
|
+
return;
|
|
1874
|
+
case "RestElement":
|
|
1875
|
+
collectBindingNames(node.argument, names);
|
|
1876
|
+
return;
|
|
1877
|
+
case "TSParameterProperty": collectBindingNames(node.parameter, names);
|
|
3073
1878
|
}
|
|
3074
|
-
return false;
|
|
3075
1879
|
}
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
* Scope-aware: references inside functions or catch clauses that shadow the name
|
|
3079
|
-
* via parameters are excluded, so only references to the original import binding
|
|
3080
|
-
* are counted.
|
|
3081
|
-
* Excludes import declarations and property names in non-computed member expressions.
|
|
3082
|
-
* @param program - The parsed AST program
|
|
3083
|
-
* @param names - Set of identifier names to count
|
|
3084
|
-
* @returns Map from identifier name to reference count
|
|
3085
|
-
*/
|
|
3086
|
-
function buildReferenceCountMap(program, names) {
|
|
3087
|
-
if (names.size === 0) return /* @__PURE__ */ new Map();
|
|
3088
|
-
const counts = /* @__PURE__ */ new Map();
|
|
3089
|
-
const shadowDepth = /* @__PURE__ */ new Map();
|
|
3090
|
-
for (const name of names) {
|
|
3091
|
-
counts.set(name, 0);
|
|
3092
|
-
shadowDepth.set(name, 0);
|
|
3093
|
-
}
|
|
3094
|
-
function walk(node, parentNode, parentKey) {
|
|
3095
|
-
if (!node || typeof node !== "object") return;
|
|
3096
|
-
const nodeType = node.type;
|
|
3097
|
-
if (nodeType === "ImportDeclaration") return;
|
|
3098
|
-
const shadowedHere = [];
|
|
3099
|
-
if (nodeType === "FunctionDeclaration" || nodeType === "FunctionExpression" || nodeType === "ArrowFunctionExpression") {
|
|
3100
|
-
const params = node.params;
|
|
3101
|
-
if (params) {
|
|
3102
|
-
for (const name of names) if (params.some((p) => containsBindingName(p, name))) {
|
|
3103
|
-
shadowDepth.set(name, (shadowDepth.get(name) ?? 0) + 1);
|
|
3104
|
-
shadowedHere.push(name);
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
}
|
|
3108
|
-
if (nodeType === "CatchClause") {
|
|
3109
|
-
const param = node.param;
|
|
3110
|
-
if (param) {
|
|
3111
|
-
for (const name of names) if (containsBindingName(param, name)) {
|
|
3112
|
-
shadowDepth.set(name, (shadowDepth.get(name) ?? 0) + 1);
|
|
3113
|
-
shadowedHere.push(name);
|
|
3114
|
-
}
|
|
3115
|
-
}
|
|
3116
|
-
}
|
|
3117
|
-
if (nodeType === "Identifier") {
|
|
3118
|
-
const identName = node.name;
|
|
3119
|
-
if (identName && names.has(identName) && (shadowDepth.get(identName) ?? 0) === 0) {
|
|
3120
|
-
const isMemberProperty = parentNode && parentNode.type === "MemberExpression" && parentKey === "property" && !parentNode.computed;
|
|
3121
|
-
const isObjectPropertyKey = parentNode && parentNode.type === "Property" && parentKey === "key" && !parentNode.shorthand && !parentNode.computed;
|
|
3122
|
-
if (!isMemberProperty && !isObjectPropertyKey) counts.set(identName, (counts.get(identName) ?? 0) + 1);
|
|
3123
|
-
}
|
|
3124
|
-
}
|
|
3125
|
-
for (const key of Object.keys(node)) {
|
|
3126
|
-
const child = node[key];
|
|
3127
|
-
if (Array.isArray(child)) child.forEach((c) => walk(c, node, key));
|
|
3128
|
-
else if (child && typeof child === "object") walk(child, node, key);
|
|
3129
|
-
}
|
|
3130
|
-
for (const name of shadowedHere) shadowDepth.set(name, (shadowDepth.get(name) ?? 0) - 1);
|
|
3131
|
-
}
|
|
3132
|
-
walk(program);
|
|
3133
|
-
return counts;
|
|
1880
|
+
function declarationNode(statement) {
|
|
1881
|
+
return statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement;
|
|
3134
1882
|
}
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
* @param localName - The local name of the default import
|
|
3144
|
-
* @param source - The source code text (used to locate the `{` in mixed imports)
|
|
3145
|
-
* @returns Range to remove, or null if the import was not found
|
|
3146
|
-
*/
|
|
3147
|
-
function findDefaultImportRemovalRange(program, localName, source) {
|
|
3148
|
-
for (const statement of program.body) {
|
|
3149
|
-
if (statement.type !== "ImportDeclaration") continue;
|
|
3150
|
-
const importDecl = statement;
|
|
3151
|
-
const specifiers = importDecl.specifiers;
|
|
3152
|
-
for (const spec of specifiers) {
|
|
3153
|
-
if (spec.type !== "ImportDefaultSpecifier") continue;
|
|
3154
|
-
const defaultSpec = spec;
|
|
3155
|
-
if (defaultSpec.local.name !== localName) continue;
|
|
3156
|
-
if (specifiers.length === 1) return {
|
|
3157
|
-
start: importDecl.start,
|
|
3158
|
-
end: importDecl.end,
|
|
3159
|
-
isFullDeclaration: true
|
|
3160
|
-
};
|
|
3161
|
-
const braceIndex = source.indexOf("{", defaultSpec.end);
|
|
3162
|
-
if (braceIndex !== -1) return {
|
|
3163
|
-
start: defaultSpec.start,
|
|
3164
|
-
end: braceIndex,
|
|
3165
|
-
isFullDeclaration: false
|
|
3166
|
-
};
|
|
3167
|
-
return null;
|
|
3168
|
-
}
|
|
1883
|
+
function collectBlockBindings(statements, names) {
|
|
1884
|
+
for (const statement of statements) {
|
|
1885
|
+
const declaration = declarationNode(statement);
|
|
1886
|
+
if (!declaration) continue;
|
|
1887
|
+
if (declaration.type === "VariableDeclaration") {
|
|
1888
|
+
if (declaration.kind === "var") continue;
|
|
1889
|
+
for (const declarator of declaration.declarations) collectBindingNames(declarator.id, names);
|
|
1890
|
+
} else if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") collectBindingNames(declaration.id, names);
|
|
3169
1891
|
}
|
|
3170
|
-
return null;
|
|
3171
1892
|
}
|
|
3172
|
-
|
|
3173
|
-
* Detect .trigger() calls for known workflows and jobs
|
|
3174
|
-
* Only detects calls where the identifier is in workflowNames or jobNames
|
|
3175
|
-
* @param program - The parsed AST program
|
|
3176
|
-
* @param sourceText - The source code text
|
|
3177
|
-
* @param workflowNames - Set of known workflow identifier names
|
|
3178
|
-
* @param jobNames - Set of known job identifier names
|
|
3179
|
-
* @returns Detected trigger call metadata
|
|
3180
|
-
*/
|
|
3181
|
-
function detectExtendedTriggerCalls(program, sourceText, workflowNames, jobNames) {
|
|
3182
|
-
const calls = [];
|
|
1893
|
+
function collectFunctionVarBindings(root, names) {
|
|
3183
1894
|
function walk(node) {
|
|
3184
1895
|
if (!node || typeof node !== "object") return;
|
|
3185
|
-
|
|
3186
|
-
if (
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
callRange: triggerCall.callRange,
|
|
3193
|
-
argsText: triggerCall.argsText,
|
|
3194
|
-
optionsText: triggerCall.optionsText
|
|
3195
|
-
});
|
|
3196
|
-
else if (isJob) calls.push({
|
|
3197
|
-
kind: "job",
|
|
3198
|
-
identifierName: triggerCall.identifierName,
|
|
3199
|
-
callRange: triggerCall.callRange,
|
|
3200
|
-
argsText: triggerCall.argsText,
|
|
3201
|
-
optionsText: triggerCall.optionsText
|
|
3202
|
-
});
|
|
1896
|
+
if (node !== root && (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "StaticBlock")) return;
|
|
1897
|
+
if (node.type === "VariableDeclaration" && node.kind === "var") for (const declarator of node.declarations) collectBindingNames(declarator.id, names);
|
|
1898
|
+
for (const key of Object.keys(node)) {
|
|
1899
|
+
if (key === "parent") continue;
|
|
1900
|
+
const child = node[key];
|
|
1901
|
+
if (Array.isArray(child)) for (const item of child) walk(item);
|
|
1902
|
+
else if (child && typeof child === "object") walk(child);
|
|
3203
1903
|
}
|
|
1904
|
+
}
|
|
1905
|
+
walk(root);
|
|
1906
|
+
}
|
|
1907
|
+
function collectScopeBindings(node) {
|
|
1908
|
+
const names = /* @__PURE__ */ new Set();
|
|
1909
|
+
if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
|
|
1910
|
+
collectBindingNames(node.id, names);
|
|
1911
|
+
for (const parameter of node.params) collectBindingNames(parameter, names);
|
|
1912
|
+
collectFunctionVarBindings(node, names);
|
|
1913
|
+
return names;
|
|
1914
|
+
}
|
|
1915
|
+
if (node.type === "BlockStatement") {
|
|
1916
|
+
collectBlockBindings(node.body, names);
|
|
1917
|
+
return names;
|
|
1918
|
+
}
|
|
1919
|
+
if (node.type === "CatchClause") {
|
|
1920
|
+
collectBindingNames(node.param, names);
|
|
1921
|
+
return names;
|
|
1922
|
+
}
|
|
1923
|
+
if (node.type === "ForStatement" || node.type === "ForInStatement" || node.type === "ForOfStatement") {
|
|
1924
|
+
const declaration = node.init ?? node.left;
|
|
1925
|
+
if (declaration?.type === "VariableDeclaration" && declaration.kind !== "var") for (const declarator of declaration.declarations) collectBindingNames(declarator.id, names);
|
|
1926
|
+
return names;
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
function addShadowedBindings(shadowedNames, bindings, targetNames) {
|
|
1930
|
+
if (!bindings) return shadowedNames;
|
|
1931
|
+
const relevantBindings = [...bindings].filter((name) => targetNames.has(name));
|
|
1932
|
+
return relevantBindings.length === 0 ? shadowedNames : /* @__PURE__ */ new Set([...shadowedNames, ...relevantBindings]);
|
|
1933
|
+
}
|
|
1934
|
+
function walkBindingAware(program, targetNames, visitor) {
|
|
1935
|
+
function walk(node, shadowedNames, parentNode, parentKey) {
|
|
1936
|
+
if (!node || typeof node !== "object" || node.type === "ImportDeclaration") return;
|
|
1937
|
+
const nestedShadowedNames = node.type === "Program" ? shadowedNames : addShadowedBindings(shadowedNames, collectScopeBindings(node), targetNames);
|
|
1938
|
+
visitor(node, nestedShadowedNames, parentNode, parentKey);
|
|
3204
1939
|
for (const key of Object.keys(node)) {
|
|
1940
|
+
if (key === "parent") continue;
|
|
3205
1941
|
const child = node[key];
|
|
3206
|
-
if (Array.isArray(child)) child
|
|
3207
|
-
else if (child && typeof child === "object") walk(child);
|
|
1942
|
+
if (Array.isArray(child)) for (const item of child) walk(item, nestedShadowedNames, node, key);
|
|
1943
|
+
else if (child && typeof child === "object") walk(child, nestedShadowedNames, node, key);
|
|
3208
1944
|
}
|
|
3209
1945
|
}
|
|
3210
|
-
walk(program);
|
|
3211
|
-
return calls;
|
|
1946
|
+
walk(program, /* @__PURE__ */ new Set());
|
|
3212
1947
|
}
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
const
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
const
|
|
3235
|
-
if (
|
|
3236
|
-
|
|
3237
|
-
workflowDefaultImportNames.add(localName);
|
|
3238
|
-
}
|
|
1948
|
+
function resolveRelativeImport(context, currentFilePath, importSource) {
|
|
1949
|
+
if (!importSource.startsWith(".")) return void 0;
|
|
1950
|
+
const currentDirectory = path.dirname(currentFilePath.replace(/[?#].*$/, ""));
|
|
1951
|
+
const modulePath = normalizeFilePath(path.resolve(currentDirectory, importSource));
|
|
1952
|
+
return context.modules.get(modulePath) ?? context.modules.get(path.join(modulePath, "index"));
|
|
1953
|
+
}
|
|
1954
|
+
function collectLocalTargets(program, context, currentFilePath) {
|
|
1955
|
+
const targets = /* @__PURE__ */ new Map();
|
|
1956
|
+
const currentModule = context.modules.get(normalizeFilePath(currentFilePath));
|
|
1957
|
+
if (currentModule) for (const [localName, target] of currentModule.localBindings) targets.set(localName, target);
|
|
1958
|
+
for (const statement of program.body) {
|
|
1959
|
+
if (statement.type !== "ImportDeclaration" || statement.importKind === "type") continue;
|
|
1960
|
+
const importSource = statement.source.value;
|
|
1961
|
+
if (typeof importSource !== "string") continue;
|
|
1962
|
+
const importedModule = resolveRelativeImport(context, currentFilePath, importSource);
|
|
1963
|
+
if (!importedModule) continue;
|
|
1964
|
+
for (const specifier of statement.specifiers) {
|
|
1965
|
+
if (specifier.type === "ImportNamespaceSpecifier") continue;
|
|
1966
|
+
if (specifier.type === "ImportSpecifier" && specifier.importKind === "type") continue;
|
|
1967
|
+
const importedName = specifier.type === "ImportDefaultSpecifier" ? "default" : getModuleExportName(specifier.imported);
|
|
1968
|
+
if (!importedName) continue;
|
|
1969
|
+
const target = importedModule.exports.get(importedName);
|
|
1970
|
+
if (!target) continue;
|
|
1971
|
+
targets.set(specifier.local.name, target);
|
|
3239
1972
|
}
|
|
3240
1973
|
}
|
|
3241
|
-
|
|
1974
|
+
return targets;
|
|
1975
|
+
}
|
|
1976
|
+
function detectTriggerCallsWithTargets(program, sourceText, targets) {
|
|
1977
|
+
const calls = [];
|
|
1978
|
+
walkBindingAware(program, new Set(targets.keys()), (node, shadowedNames) => {
|
|
1979
|
+
const triggerCall = getTriggerCallInfo(node, sourceText);
|
|
1980
|
+
if (!triggerCall || shadowedNames.has(triggerCall.identifierName)) return;
|
|
1981
|
+
const target = targets.get(triggerCall.identifierName);
|
|
1982
|
+
if (target) calls.push({
|
|
1983
|
+
...triggerCall,
|
|
1984
|
+
kind: target.kind,
|
|
1985
|
+
targetName: target.name
|
|
1986
|
+
});
|
|
1987
|
+
});
|
|
1988
|
+
return calls;
|
|
1989
|
+
}
|
|
1990
|
+
function detectResolvedTriggerCalls(program, sourceText, context, currentFilePath) {
|
|
1991
|
+
return detectTriggerCallsWithTargets(program, sourceText, collectLocalTargets(program, context, currentFilePath));
|
|
1992
|
+
}
|
|
1993
|
+
function transformFunctionTriggers(source, triggerContext, currentFilePath) {
|
|
1994
|
+
const { program } = parseSync("input.ts", source);
|
|
1995
|
+
const localTargets = collectLocalTargets(program, triggerContext, currentFilePath);
|
|
1996
|
+
const { authNamespace } = triggerContext;
|
|
1997
|
+
const allTriggerCalls = detectTriggerCallsWithTargets(program, source, localTargets);
|
|
3242
1998
|
const nestedTriggerCalls = [];
|
|
3243
1999
|
const triggerCalls = allTriggerCalls.filter((call) => {
|
|
3244
2000
|
const parent = allTriggerCalls.find((other) => other !== call && other.callRange.start <= call.callRange.start && call.callRange.end <= other.callRange.end);
|
|
3245
|
-
if (parent)
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
}
|
|
3252
|
-
return true;
|
|
2001
|
+
if (!parent) return true;
|
|
2002
|
+
nestedTriggerCalls.push({
|
|
2003
|
+
call,
|
|
2004
|
+
parent
|
|
2005
|
+
});
|
|
2006
|
+
return false;
|
|
3253
2007
|
});
|
|
3254
2008
|
for (const { call, parent } of nestedTriggerCalls) logger.warn(`Nested trigger call "${call.identifierName}.trigger(...)" inside "${parent.identifierName}.trigger(...)" cannot be converted. Move it to a separate statement and pass the result instead.`);
|
|
3255
2009
|
const replacements = [];
|
|
3256
2010
|
let needsNormalizerHelper = false;
|
|
3257
|
-
const
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
if (workflowName) {
|
|
2011
|
+
for (const call of triggerCalls) {
|
|
2012
|
+
let transformedCall;
|
|
2013
|
+
if (call.kind === "workflow") {
|
|
3261
2014
|
let optionsPart = "";
|
|
3262
2015
|
if (call.optionsText !== void 0) if (authNamespace) {
|
|
3263
2016
|
optionsPart = `, ${NORMALIZER_IDENTIFIER}(${call.optionsText})`;
|
|
3264
2017
|
needsNormalizerHelper = true;
|
|
3265
2018
|
} else optionsPart = `, ${call.optionsText}`;
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
start: call.callRange.start,
|
|
3269
|
-
end: call.callRange.end,
|
|
3270
|
-
text: transformedCall
|
|
3271
|
-
});
|
|
3272
|
-
transformedCallsPerIdentifier.set(call.identifierName, (transformedCallsPerIdentifier.get(call.identifierName) ?? 0) + 1);
|
|
3273
|
-
}
|
|
3274
|
-
} else {
|
|
3275
|
-
const jobName = jobNameMap.get(call.identifierName);
|
|
3276
|
-
if (jobName) {
|
|
2019
|
+
transformedCall = `tailor.workflow.triggerWorkflow(${JSON.stringify(call.targetName)}, ${call.argsText || "undefined"}${optionsPart})`;
|
|
2020
|
+
} else {
|
|
3277
2021
|
const optionsPart = call.optionsText !== void 0 ? `, ${call.optionsText}` : "";
|
|
3278
|
-
|
|
3279
|
-
replacements.push({
|
|
3280
|
-
start: call.callRange.start,
|
|
3281
|
-
end: call.callRange.end,
|
|
3282
|
-
text: transformedCall
|
|
3283
|
-
});
|
|
3284
|
-
transformedCallsPerIdentifier.set(call.identifierName, (transformedCallsPerIdentifier.get(call.identifierName) ?? 0) + 1);
|
|
3285
|
-
}
|
|
3286
|
-
}
|
|
3287
|
-
const refCounts = buildReferenceCountMap(program, workflowDefaultImportNames);
|
|
3288
|
-
for (const localName of workflowDefaultImportNames) {
|
|
3289
|
-
const transformedCount = transformedCallsPerIdentifier.get(localName) ?? 0;
|
|
3290
|
-
const refCount = refCounts.get(localName) ?? 0;
|
|
3291
|
-
if (refCount === 0 || transformedCount >= refCount) {
|
|
3292
|
-
const removal = findDefaultImportRemovalRange(program, localName, source);
|
|
3293
|
-
if (removal) replacements.push({
|
|
3294
|
-
start: removal.start,
|
|
3295
|
-
end: removal.isFullDeclaration ? findStatementEnd(source, removal.end) : removal.end,
|
|
3296
|
-
text: ""
|
|
3297
|
-
});
|
|
2022
|
+
transformedCall = `tailor.workflow.triggerJobFunction(${JSON.stringify(call.targetName)}, ${call.argsText || "undefined"}${optionsPart})`;
|
|
3298
2023
|
}
|
|
2024
|
+
replacements.push({
|
|
2025
|
+
start: call.callRange.start,
|
|
2026
|
+
end: call.callRange.end,
|
|
2027
|
+
text: transformedCall
|
|
2028
|
+
});
|
|
3299
2029
|
}
|
|
3300
2030
|
const transformed = applyReplacements(source, replacements);
|
|
3301
|
-
|
|
3302
|
-
|
|
2031
|
+
return needsNormalizerHelper && authNamespace ? buildNormalizerHelperSource(authNamespace) + transformed : transformed;
|
|
2032
|
+
}
|
|
2033
|
+
function createTriggerTransformPlugin(triggerContext) {
|
|
2034
|
+
if (!triggerContext) return void 0;
|
|
2035
|
+
return {
|
|
2036
|
+
name: "trigger-transform",
|
|
2037
|
+
transform: {
|
|
2038
|
+
filter: { id: { include: [/\.(ts|mts|cts|js|mjs|cjs)$/] } },
|
|
2039
|
+
handler(code, id) {
|
|
2040
|
+
if (!code.includes(".trigger(")) return null;
|
|
2041
|
+
return { code: transformFunctionTriggers(code, triggerContext, id) };
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
};
|
|
3303
2045
|
}
|
|
3304
2046
|
|
|
3305
2047
|
//#endregion
|
|
3306
|
-
//#region src/cli/shared/
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
2048
|
+
//#region src/cli/shared/bundle-log-level.ts
|
|
2049
|
+
const INFO_LEVEL_CONSOLE_METHODS = [
|
|
2050
|
+
"console.info",
|
|
2051
|
+
"console.table",
|
|
2052
|
+
"console.dir",
|
|
2053
|
+
"console.dirxml",
|
|
2054
|
+
"console.count",
|
|
2055
|
+
"console.countReset",
|
|
2056
|
+
"console.time",
|
|
2057
|
+
"console.timeLog",
|
|
2058
|
+
"console.timeEnd",
|
|
2059
|
+
"console.group",
|
|
2060
|
+
"console.groupCollapsed",
|
|
2061
|
+
"console.groupEnd",
|
|
2062
|
+
"console.clear"
|
|
2063
|
+
];
|
|
2064
|
+
const DEBUG_LEVEL_CONSOLE_METHODS = [
|
|
2065
|
+
"console.debug",
|
|
2066
|
+
"console.log",
|
|
2067
|
+
"console.trace"
|
|
2068
|
+
];
|
|
2069
|
+
const WARN_LEVEL_CONSOLE_METHODS = ["console.warn"];
|
|
2070
|
+
const ERROR_LEVEL_CONSOLE_METHODS = ["console.error"];
|
|
2071
|
+
const MANUAL_PURE_FUNCTIONS_BY_LOG_LEVEL = {
|
|
2072
|
+
DEBUG: [],
|
|
2073
|
+
INFO: DEBUG_LEVEL_CONSOLE_METHODS,
|
|
2074
|
+
WARN: [...DEBUG_LEVEL_CONSOLE_METHODS, ...INFO_LEVEL_CONSOLE_METHODS],
|
|
2075
|
+
ERROR: [
|
|
2076
|
+
...DEBUG_LEVEL_CONSOLE_METHODS,
|
|
2077
|
+
...INFO_LEVEL_CONSOLE_METHODS,
|
|
2078
|
+
...WARN_LEVEL_CONSOLE_METHODS
|
|
2079
|
+
],
|
|
2080
|
+
SILENT: [
|
|
2081
|
+
...DEBUG_LEVEL_CONSOLE_METHODS,
|
|
2082
|
+
...INFO_LEVEL_CONSOLE_METHODS,
|
|
2083
|
+
...WARN_LEVEL_CONSOLE_METHODS,
|
|
2084
|
+
...ERROR_LEVEL_CONSOLE_METHODS
|
|
2085
|
+
]
|
|
2086
|
+
};
|
|
2087
|
+
function normalizeBundleLogLevel(value) {
|
|
2088
|
+
const normalized = value.trim().toUpperCase();
|
|
2089
|
+
return isLogLevel(normalized) ? normalized : void 0;
|
|
3316
2090
|
}
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
* @returns Trigger context built from workflow sources
|
|
3323
|
-
*/
|
|
3324
|
-
async function buildTriggerContext(workflowConfig, authNamespace) {
|
|
3325
|
-
const workflowNameMap = /* @__PURE__ */ new Map();
|
|
3326
|
-
const jobNameMap = /* @__PURE__ */ new Map();
|
|
3327
|
-
const workflowFileMap = /* @__PURE__ */ new Map();
|
|
3328
|
-
if (!workflowConfig) return {
|
|
3329
|
-
workflowNameMap,
|
|
3330
|
-
jobNameMap,
|
|
3331
|
-
workflowFileMap,
|
|
3332
|
-
authNamespace
|
|
3333
|
-
};
|
|
3334
|
-
const workflowFiles = loadFilesWithIgnores(workflowConfig);
|
|
3335
|
-
for (const file of workflowFiles) try {
|
|
3336
|
-
const source = await fs$1.promises.readFile(file, "utf-8");
|
|
3337
|
-
const { program } = parseSync("input.ts", source);
|
|
3338
|
-
const workflows = findAllWorkflows(program, source);
|
|
3339
|
-
const workflowMap = buildWorkflowNameMap(workflows);
|
|
3340
|
-
for (const [exportName, workflowName] of workflowMap) workflowNameMap.set(exportName, workflowName);
|
|
3341
|
-
for (const workflow of workflows) if (workflow.isDefaultExport) {
|
|
3342
|
-
const normalizedPath = normalizeFilePath(file);
|
|
3343
|
-
workflowFileMap.set(normalizedPath, workflow.name);
|
|
3344
|
-
}
|
|
3345
|
-
const jobMap = buildJobNameMap(findAllJobs(program, source));
|
|
3346
|
-
for (const [exportName, jobName] of jobMap) jobNameMap.set(exportName, jobName);
|
|
3347
|
-
} catch (error) {
|
|
3348
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3349
|
-
logger.warn(`Failed to process workflow file ${file}: ${errorMessage}`, { mode: "stream" });
|
|
3350
|
-
continue;
|
|
3351
|
-
}
|
|
3352
|
-
return {
|
|
3353
|
-
workflowNameMap,
|
|
3354
|
-
jobNameMap,
|
|
3355
|
-
workflowFileMap,
|
|
3356
|
-
authNamespace
|
|
3357
|
-
};
|
|
2091
|
+
function resolveBundleLogLevel(configValue) {
|
|
2092
|
+
if (configValue === void 0) return "DEBUG";
|
|
2093
|
+
const resolved = normalizeBundleLogLevel(configValue);
|
|
2094
|
+
if (resolved) return resolved;
|
|
2095
|
+
throw new Error(`Invalid logLevel "${configValue}". Expected one of: ${LOG_LEVELS.join(", ")}`);
|
|
3358
2096
|
}
|
|
3359
|
-
function
|
|
3360
|
-
return
|
|
2097
|
+
function manualPureFunctionsForLogLevel(logLevel) {
|
|
2098
|
+
return MANUAL_PURE_FUNCTIONS_BY_LOG_LEVEL[logLevel];
|
|
3361
2099
|
}
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
* @param ctx - Trigger context to serialize
|
|
3366
|
-
* @returns Deterministic string representation
|
|
3367
|
-
*/
|
|
3368
|
-
function serializeTriggerContext(ctx) {
|
|
3369
|
-
if (!ctx) return "";
|
|
3370
|
-
return sortedMapToJson(ctx.workflowNameMap) + sortedMapToJson(ctx.jobNameMap) + sortedMapToJson(ctx.workflowFileMap) + (ctx.authNamespace ?? "");
|
|
2100
|
+
function createLogLevelTreeshakeOptions(logLevel) {
|
|
2101
|
+
const manualPureFunctions = manualPureFunctionsForLogLevel(logLevel);
|
|
2102
|
+
return manualPureFunctions.length > 0 ? { manualPureFunctions } : {};
|
|
3371
2103
|
}
|
|
2104
|
+
|
|
2105
|
+
//#endregion
|
|
2106
|
+
//#region src/cli/shared/function-treeshake.ts
|
|
2107
|
+
const BASE_FUNCTION_TREESHAKE_OPTIONS = {
|
|
2108
|
+
moduleSideEffects: false,
|
|
2109
|
+
annotations: true,
|
|
2110
|
+
unknownGlobalSideEffects: false
|
|
2111
|
+
};
|
|
2112
|
+
function mergeFunctionTreeshakeOptions(fragments) {
|
|
2113
|
+
const merged = {};
|
|
2114
|
+
const manualPureFunctions = /* @__PURE__ */ new Set();
|
|
2115
|
+
for (const fragment of fragments) {
|
|
2116
|
+
Object.assign(merged, fragment);
|
|
2117
|
+
for (const name of fragment.manualPureFunctions ?? []) manualPureFunctions.add(name);
|
|
2118
|
+
}
|
|
2119
|
+
if (manualPureFunctions.size > 0) merged.manualPureFunctions = [...manualPureFunctions];
|
|
2120
|
+
else delete merged.manualPureFunctions;
|
|
2121
|
+
return merged;
|
|
2122
|
+
}
|
|
2123
|
+
function composeFunctionTreeshakeOptions(fragments = []) {
|
|
2124
|
+
return mergeFunctionTreeshakeOptions([BASE_FUNCTION_TREESHAKE_OPTIONS, ...fragments]);
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
//#endregion
|
|
2128
|
+
//#region src/cli/shared/platform-bundle-plugin.ts
|
|
2129
|
+
const GATE = /(?<![\w$.])process\.env\.__TAILOR_PLATFORM_BUNDLE\b/g;
|
|
2130
|
+
const platformBundleDefinePlugin = {
|
|
2131
|
+
name: "tailor-platform-bundle-define",
|
|
2132
|
+
transform(code) {
|
|
2133
|
+
if (!code.includes("process.env.__TAILOR_PLATFORM_BUNDLE")) return null;
|
|
2134
|
+
return { code: code.replace(GATE, "true") };
|
|
2135
|
+
}
|
|
2136
|
+
};
|
|
2137
|
+
|
|
2138
|
+
//#endregion
|
|
2139
|
+
//#region src/cli/shared/virtual-entry.ts
|
|
3372
2140
|
/**
|
|
3373
|
-
* Create
|
|
3374
|
-
*
|
|
3375
|
-
* @param
|
|
3376
|
-
* @
|
|
2141
|
+
* Create an in-memory rolldown entry module with a deterministic ID.
|
|
2142
|
+
* @param name - Logical entry name
|
|
2143
|
+
* @param code - Entry module source
|
|
2144
|
+
* @param sourceType - Parser type for the generated module
|
|
2145
|
+
* @returns Rolldown input and plugin for loading the entry
|
|
3377
2146
|
*/
|
|
3378
|
-
function
|
|
3379
|
-
|
|
2147
|
+
function createVirtualEntry(name, code, sourceType = "js") {
|
|
2148
|
+
const input = `tailor-sdk-entry:${name}.${sourceType}`;
|
|
2149
|
+
const resolvedId = `\0${input}`;
|
|
3380
2150
|
return {
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
2151
|
+
input,
|
|
2152
|
+
plugin: {
|
|
2153
|
+
name: "tailor-sdk-virtual-entry",
|
|
2154
|
+
resolveId(source, importer) {
|
|
2155
|
+
return source === input && importer === void 0 ? resolvedId : null;
|
|
2156
|
+
},
|
|
2157
|
+
load(id) {
|
|
2158
|
+
return id === resolvedId ? code : null;
|
|
3387
2159
|
}
|
|
3388
2160
|
}
|
|
3389
2161
|
};
|
|
@@ -3395,7 +2167,7 @@ function createTriggerTransformPlugin(triggerContext) {
|
|
|
3395
2167
|
* Bundle a single auth hook handler.
|
|
3396
2168
|
*
|
|
3397
2169
|
* Follows the same pattern as the executor bundler:
|
|
3398
|
-
* 1. Generate an entry
|
|
2170
|
+
* 1. Generate an in-memory entry module that re-exports the handler as `main`
|
|
3399
2171
|
* 2. Bundle with rolldown + tree-shaking
|
|
3400
2172
|
* @param options - Bundle options
|
|
3401
2173
|
* @returns Map of function name to bundled code
|
|
@@ -3404,9 +2176,6 @@ async function bundleAuthHooks(options) {
|
|
|
3404
2176
|
const { configPath, authName, handlerAccessPath, env = {}, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG" } = options;
|
|
3405
2177
|
logger.newline();
|
|
3406
2178
|
logger.log(`Bundling auth hook for ${styles.info(`"${authName}"`)}`);
|
|
3407
|
-
const outputDir = path.resolve(getDistDir(), "auth-hooks");
|
|
3408
|
-
fs$1.mkdirSync(outputDir, { recursive: true });
|
|
3409
|
-
await removeStaleEntryFiles(outputDir);
|
|
3410
2179
|
let tsconfig;
|
|
3411
2180
|
try {
|
|
3412
2181
|
tsconfig = await resolveTSConfig();
|
|
@@ -3431,7 +2200,6 @@ async function bundleAuthHooks(options) {
|
|
|
3431
2200
|
prefix: sortedEnvPrefix
|
|
3432
2201
|
}),
|
|
3433
2202
|
async build(cachePlugins) {
|
|
3434
|
-
const entryPath = path.join(outputDir, `${functionName}.entry.js`);
|
|
3435
2203
|
const entryContent = multiline`
|
|
3436
2204
|
import _config from "${absoluteConfigPath}";
|
|
3437
2205
|
const __auth_hook_function = _config.${handlerAccessPath};
|
|
@@ -3440,12 +2208,13 @@ async function bundleAuthHooks(options) {
|
|
|
3440
2208
|
return await __auth_hook_function({ ...args, env });
|
|
3441
2209
|
}
|
|
3442
2210
|
`;
|
|
3443
|
-
|
|
2211
|
+
const entry = createVirtualEntry(`auth-hook:${functionName}`, entryContent);
|
|
3444
2212
|
const triggerPlugin = createTriggerTransformPlugin(triggerContext);
|
|
3445
|
-
const plugins =
|
|
2213
|
+
const plugins = [entry.plugin];
|
|
2214
|
+
if (triggerPlugin) plugins.push(triggerPlugin);
|
|
3446
2215
|
plugins.push(platformBundleDefinePlugin, ...cachePlugins);
|
|
3447
2216
|
return (await rolldown.build({
|
|
3448
|
-
input:
|
|
2217
|
+
input: entry.input,
|
|
3449
2218
|
write: false,
|
|
3450
2219
|
output: {
|
|
3451
2220
|
format: "esm",
|
|
@@ -3624,7 +2393,17 @@ function formatScriptContext(kind, context) {
|
|
|
3624
2393
|
const convertToScriptExpr = (fn, kind, context) => {
|
|
3625
2394
|
const precompiledExpr = getPrecompiledScriptExpr(fn);
|
|
3626
2395
|
if (precompiledExpr) return precompiledExpr;
|
|
3627
|
-
return assertParsableExpression(`(${stringifyFunction(fn)})({ value: _value,
|
|
2396
|
+
return assertParsableExpression(`(${stringifyFunction(fn)})(${kind === "validate" ? `{ value: _value }` : kind === "hooks.create" ? `{ input: _value, invoker: ${tailorPrincipalMap}, now: _now }` : `{ input: _value, oldValue: _oldValue, invoker: ${tailorPrincipalMap}, now: _now }`})`, formatScriptContext(kind, context));
|
|
2397
|
+
};
|
|
2398
|
+
const convertTypeHookToExpr = (fn) => {
|
|
2399
|
+
const precompiledExpr = getPrecompiledScriptExpr(fn);
|
|
2400
|
+
if (precompiledExpr) return precompiledExpr;
|
|
2401
|
+
return assertParsableExpression(`(${stringifyFunction(fn)})({ input: _input, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap}, now: _now })`, "type-hook");
|
|
2402
|
+
};
|
|
2403
|
+
const convertTypeValidateToExpr = (fn) => {
|
|
2404
|
+
const precompiledExpr = getPrecompiledScriptExpr(fn);
|
|
2405
|
+
if (precompiledExpr) return precompiledExpr;
|
|
2406
|
+
return assertParsableExpression(`(${stringifyFunction(fn)})({ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap} }, __issues)`, "type-validate");
|
|
3628
2407
|
};
|
|
3629
2408
|
/**
|
|
3630
2409
|
* Parse TailorDBField into OperatorFieldConfig.
|
|
@@ -3637,6 +2416,8 @@ function parseFieldConfig(field, context) {
|
|
|
3637
2416
|
const metadata = field.metadata;
|
|
3638
2417
|
const fieldType = field.type;
|
|
3639
2418
|
const rawRelation = field.rawRelation;
|
|
2419
|
+
if (context && context.fieldPath.length > 1 && metadata.default !== void 0) throw new Error(`Field "${context.fieldPath.join(".")}" on type "${context.typeName}": .default() cannot be used on nested inner fields`);
|
|
2420
|
+
if (context && context.fieldPath.length > 1 && metadata.hooks) throw new Error(`Field "${context.fieldPath.join(".")}" on type "${context.typeName}": .hooks() cannot be used on nested inner fields`);
|
|
3640
2421
|
const nestedFields = field.fields;
|
|
3641
2422
|
return {
|
|
3642
2423
|
type: fieldType,
|
|
@@ -3649,19 +2430,10 @@ function parseFieldConfig(field, context) {
|
|
|
3649
2430
|
});
|
|
3650
2431
|
return acc;
|
|
3651
2432
|
}, {}) } : {},
|
|
3652
|
-
validate: metadata.validate?.map((
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
} : {
|
|
3657
|
-
fn: v[0],
|
|
3658
|
-
message: v[1]
|
|
3659
|
-
};
|
|
3660
|
-
return {
|
|
3661
|
-
script: { expr: convertToScriptExpr(fn, "validate", context) },
|
|
3662
|
-
errorMessage: message
|
|
3663
|
-
};
|
|
3664
|
-
}),
|
|
2433
|
+
validate: metadata.validate?.map((fn) => ({
|
|
2434
|
+
script: { expr: convertToScriptExpr(fn, "validate", context) },
|
|
2435
|
+
errorMessage: ""
|
|
2436
|
+
})),
|
|
3665
2437
|
hooks: metadata.hooks ? {
|
|
3666
2438
|
create: metadata.hooks.create ? { expr: convertToScriptExpr(metadata.hooks.create, "hooks.create", context) } : void 0,
|
|
3667
2439
|
update: metadata.hooks.update ? { expr: convertToScriptExpr(metadata.hooks.update, "hooks.update", context) } : void 0
|
|
@@ -3807,6 +2579,36 @@ function findOmittedPermitRules(rawPermissions) {
|
|
|
3807
2579
|
});
|
|
3808
2580
|
return locations;
|
|
3809
2581
|
}
|
|
2582
|
+
/**
|
|
2583
|
+
* Check whether GraphQL exposure is fully disabled for a type, given its
|
|
2584
|
+
* effective gqlOperations (the type's own setting, falling back to the
|
|
2585
|
+
* TailorDB namespace default). `undefined` means the default of all
|
|
2586
|
+
* operations enabled, so it is never considered fully disabled.
|
|
2587
|
+
* @param gqlOperations - Effective, normalized gqlOperations configuration
|
|
2588
|
+
* @returns Whether create, update, delete, and read are all explicitly disabled
|
|
2589
|
+
*/
|
|
2590
|
+
function isGqlOperationsFullyDisabled(gqlOperations) {
|
|
2591
|
+
if (!gqlOperations) return false;
|
|
2592
|
+
return gqlOperations.create === false && gqlOperations.update === false && gqlOperations.delete === false && gqlOperations.read === false;
|
|
2593
|
+
}
|
|
2594
|
+
/**
|
|
2595
|
+
* Find missing permission configuration for a TailorDB type.
|
|
2596
|
+
*
|
|
2597
|
+
* Record-level permission is always required: TailorDB denies all record
|
|
2598
|
+
* operations for a type without it, regardless of whether the type is
|
|
2599
|
+
* exposed via GraphQL. GraphQL permission is required whenever GraphQL
|
|
2600
|
+
* exposure is enabled for the type (the default, unless every operation is
|
|
2601
|
+
* explicitly disabled via `gqlOperations`).
|
|
2602
|
+
* @param rawPermissions - Raw permissions definition for the type
|
|
2603
|
+
* @param effectiveGqlOperations - The type's own gqlOperations, falling back to the namespace default
|
|
2604
|
+
* @returns Which permission configuration, if any, is missing
|
|
2605
|
+
*/
|
|
2606
|
+
function findMissingPermissionConfig(rawPermissions, effectiveGqlOperations) {
|
|
2607
|
+
return {
|
|
2608
|
+
missingPermission: !rawPermissions.record,
|
|
2609
|
+
missingGqlPermission: !rawPermissions.gql && !isGqlOperationsFullyDisabled(effectiveGqlOperations)
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
3810
2612
|
|
|
3811
2613
|
//#endregion
|
|
3812
2614
|
//#region src/parser/service/tailordb/type-parser.ts
|
|
@@ -3901,7 +2703,12 @@ function parseTailorDBType(type, allTypeNames, rawTypes, typeSourceInfo) {
|
|
|
3901
2703
|
settings: metadata.settings ?? {},
|
|
3902
2704
|
permissions: parsePermissions(metadata.permissions),
|
|
3903
2705
|
indexes: metadata.indexes,
|
|
3904
|
-
files: metadata.files
|
|
2706
|
+
files: metadata.files,
|
|
2707
|
+
...metadata.typeHook && { typeHookExpr: {
|
|
2708
|
+
...typeof metadata.typeHook.create === "function" && { create: convertTypeHookToExpr(metadata.typeHook.create) },
|
|
2709
|
+
...typeof metadata.typeHook.update === "function" && { update: convertTypeHookToExpr(metadata.typeHook.update) }
|
|
2710
|
+
} },
|
|
2711
|
+
...typeof metadata.typeValidate === "function" && { typeValidateExpr: convertTypeValidateToExpr(metadata.typeValidate) }
|
|
3905
2712
|
};
|
|
3906
2713
|
}
|
|
3907
2714
|
/**
|
|
@@ -4092,29 +2899,35 @@ function collectScriptTargets(type) {
|
|
|
4092
2899
|
const createHook = toScriptFunction(metadata.hooks?.create);
|
|
4093
2900
|
if (createHook) targets.push({
|
|
4094
2901
|
fn: createHook,
|
|
4095
|
-
kind: "hooks"
|
|
2902
|
+
kind: "hooks.create"
|
|
4096
2903
|
});
|
|
4097
2904
|
const updateHook = toScriptFunction(metadata.hooks?.update);
|
|
4098
2905
|
if (updateHook) targets.push({
|
|
4099
2906
|
fn: updateHook,
|
|
4100
|
-
kind: "hooks"
|
|
2907
|
+
kind: "hooks.update"
|
|
4101
2908
|
});
|
|
4102
|
-
for (const validateInput of metadata.validate ?? [])
|
|
2909
|
+
for (const validateInput of metadata.validate ?? []) {
|
|
4103
2910
|
const validateFn = toScriptFunction(validateInput);
|
|
4104
2911
|
if (validateFn) targets.push({
|
|
4105
2912
|
fn: validateFn,
|
|
4106
2913
|
kind: "validate"
|
|
4107
2914
|
});
|
|
4108
|
-
} else {
|
|
4109
|
-
const validateFn = toScriptFunction(validateInput[0]);
|
|
4110
|
-
if (validateFn) targets.push({
|
|
4111
|
-
fn: validateFn,
|
|
4112
|
-
kind: "validate"
|
|
4113
|
-
});
|
|
4114
2915
|
}
|
|
4115
2916
|
if (field.type === "nested" && field.fields) for (const nestedField of Object.values(field.fields)) collectFieldTargets(nestedField);
|
|
4116
2917
|
};
|
|
4117
2918
|
for (const field of Object.values(type.fields)) collectFieldTargets(field);
|
|
2919
|
+
if (type.metadata.typeHook) for (const op of ["create", "update"]) {
|
|
2920
|
+
const fn = toScriptFunction(type.metadata.typeHook[op]);
|
|
2921
|
+
if (fn) targets.push({
|
|
2922
|
+
fn,
|
|
2923
|
+
kind: "typeHook"
|
|
2924
|
+
});
|
|
2925
|
+
}
|
|
2926
|
+
const typeValidateFn = toScriptFunction(type.metadata.typeValidate);
|
|
2927
|
+
if (typeValidateFn) targets.push({
|
|
2928
|
+
fn: typeValidateFn,
|
|
2929
|
+
kind: "typeValidate"
|
|
2930
|
+
});
|
|
4118
2931
|
return targets;
|
|
4119
2932
|
}
|
|
4120
2933
|
/**
|
|
@@ -4308,11 +3121,11 @@ function resolveNeededBindings(freeVars, sourceBindings) {
|
|
|
4308
3121
|
unresolved
|
|
4309
3122
|
};
|
|
4310
3123
|
}
|
|
4311
|
-
function buildPrecompiledExpr(bundleCode) {
|
|
3124
|
+
function buildPrecompiledExpr(bundleCode, argsObject) {
|
|
4312
3125
|
return `(() => {
|
|
4313
3126
|
const module = { exports: {} };
|
|
4314
3127
|
const exports = module.exports;
|
|
4315
|
-
${bundleCode}\n return module.exports.main(
|
|
3128
|
+
${bundleCode}\n return module.exports.main(${argsObject});\n})()`;
|
|
4316
3129
|
}
|
|
4317
3130
|
/**
|
|
4318
3131
|
* Build entry file content from already-resolved imports and declarations.
|
|
@@ -4320,32 +3133,34 @@ ${bundleCode}\n return module.exports.main({ value: _value, data: _data, invoke
|
|
|
4320
3133
|
* @param declarations - Declaration statement texts.
|
|
4321
3134
|
* @param fnSource - The function source code.
|
|
4322
3135
|
* @param sourceFilePath - Path to the source file for resolving relative imports.
|
|
3136
|
+
* @param multiArg - Whether the function accepts multiple arguments (spread via `...args`).
|
|
4323
3137
|
* @returns Entry file content string.
|
|
4324
3138
|
*/
|
|
4325
|
-
function buildMinimalEntryFromResolved(imports, declarations, fnSource, sourceFilePath) {
|
|
3139
|
+
function buildMinimalEntryFromResolved(imports, declarations, fnSource, sourceFilePath, multiArg = false) {
|
|
4326
3140
|
const sourceDir = resolve(sourceFilePath, "..").replace(/\\/g, "/");
|
|
4327
3141
|
return [
|
|
4328
3142
|
...imports.map((imp) => imp.replace(/from\s+["'](\.[^"']+)["']/g, (_match, relPath) => `from "${resolve(sourceDir, relPath).replace(/\\/g, "/")}"`)),
|
|
4329
3143
|
...declarations,
|
|
4330
|
-
`export function main(input) { return (${fnSource})(input); }`
|
|
3144
|
+
multiArg ? `export function main(...args) { return (${fnSource})(...args); }` : `export function main(input) { return (${fnSource})(input); }`
|
|
4331
3145
|
].join("\n");
|
|
4332
3146
|
}
|
|
4333
3147
|
async function bundleScriptTarget(args) {
|
|
4334
|
-
const { fn, kind, sourceFilePath, sourceBindings,
|
|
3148
|
+
const { fn, kind, sourceFilePath, sourceBindings, typeName, targetIndex, tsconfig } = args;
|
|
4335
3149
|
const context = `${kind} in ${sourceFilePath}`;
|
|
4336
3150
|
const fnSource = stringifyFunction(fn);
|
|
4337
|
-
|
|
3151
|
+
if ((kind === "typeValidate" || kind === "validate") && fn.constructor.name === "AsyncFunction") throw new Error(`${context} must be synchronous — the generated validator runs synchronously, so issues reported after an await are silently lost. Remove the async keyword.`);
|
|
3152
|
+
const argsObject = kind === "hooks.create" ? `{ input: _value, invoker: ${tailorPrincipalMap}, now: _now }` : kind === "hooks.update" ? `{ input: _value, oldValue: _oldValue, invoker: ${tailorPrincipalMap}, now: _now }` : kind === "validate" ? `{ value: _value }` : kind === "typeHook" ? `{ input: _input, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap}, now: _now }` : `{ newRecord: _newRecord, oldRecord: _oldRecord, invoker: ${tailorPrincipalMap} }, __issues`;
|
|
3153
|
+
const inlineExpr = assertParsableExpression(`(${fnSource})(${argsObject})`, context);
|
|
4338
3154
|
const freeVars = findUndefinedReferences(`const __fn = ${fnSource};`);
|
|
4339
3155
|
if (freeVars.size === 0) return inlineExpr;
|
|
4340
3156
|
const { imports, declarations, unresolved } = resolveNeededBindings(freeVars, sourceBindings);
|
|
4341
3157
|
if (unresolved.length > 0) throw new Error(`${context} captures unresolvable variables (${unresolved.join(", ")}). Hooks and validators must not reference variables that cannot be resolved from the source file.
|
|
4342
3158
|
${kind}: ${fnSource}`);
|
|
4343
|
-
const entryContent = buildMinimalEntryFromResolved(imports, declarations, fnSource, sourceFilePath);
|
|
4344
|
-
const
|
|
4345
|
-
writeFileSync(entryPath, entryContent);
|
|
3159
|
+
const entryContent = buildMinimalEntryFromResolved(imports, declarations, fnSource, sourceFilePath, kind === "typeValidate");
|
|
3160
|
+
const entry = createVirtualEntry(`tailordb-script:${typeName}:${targetIndex}`, entryContent, "ts");
|
|
4346
3161
|
const bundledCode = (await rolldown.build({
|
|
4347
|
-
plugins: [platformBundleDefinePlugin],
|
|
4348
|
-
input:
|
|
3162
|
+
plugins: [entry.plugin, platformBundleDefinePlugin],
|
|
3163
|
+
input: entry.input,
|
|
4349
3164
|
write: false,
|
|
4350
3165
|
output: {
|
|
4351
3166
|
format: "cjs",
|
|
@@ -4361,7 +3176,7 @@ async function bundleScriptTarget(args) {
|
|
|
4361
3176
|
},
|
|
4362
3177
|
logLevel: "silent"
|
|
4363
3178
|
})).output[0].code;
|
|
4364
|
-
return assertParsableExpression(buildPrecompiledExpr(bundledCode), context);
|
|
3179
|
+
return assertParsableExpression(buildPrecompiledExpr(bundledCode, argsObject), context);
|
|
4365
3180
|
}
|
|
4366
3181
|
/**
|
|
4367
3182
|
* Precompile TailorDB hooks/validators into self-contained script expressions using rolldown.
|
|
@@ -4375,27 +3190,18 @@ async function precompileTailorDBTypeScripts(type, sourceFilePath, tsconfig) {
|
|
|
4375
3190
|
const targets = collectScriptTargets(type);
|
|
4376
3191
|
if (targets.length === 0) return;
|
|
4377
3192
|
const sourceBindings = collectSourceBindings(sourceFilePath);
|
|
4378
|
-
const
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
const firstError = results.find((r) => r.status === "rejected");
|
|
4391
|
-
if (firstError) throw firstError.reason;
|
|
4392
|
-
for (const [index, result] of results.entries()) if (result.status === "fulfilled") setPrecompiledScriptExpr(assertDefined(targets[index], `bundle target at index ${index} missing`).fn, result.value);
|
|
4393
|
-
} finally {
|
|
4394
|
-
rmSync(tempDir, {
|
|
4395
|
-
recursive: true,
|
|
4396
|
-
force: true
|
|
4397
|
-
});
|
|
4398
|
-
}
|
|
3193
|
+
const results = await Promise.allSettled(targets.map((target, index) => bundleScriptTarget({
|
|
3194
|
+
fn: target.fn,
|
|
3195
|
+
kind: target.kind,
|
|
3196
|
+
sourceFilePath,
|
|
3197
|
+
sourceBindings,
|
|
3198
|
+
typeName: type.name,
|
|
3199
|
+
targetIndex: index,
|
|
3200
|
+
tsconfig
|
|
3201
|
+
})));
|
|
3202
|
+
const firstError = results.find((r) => r.status === "rejected");
|
|
3203
|
+
if (firstError) throw firstError.reason;
|
|
3204
|
+
for (const [index, result] of results.entries()) if (result.status === "fulfilled") setPrecompiledScriptExpr(assertDefined(targets[index], `bundle target at index ${index} missing`).fn, result.value);
|
|
4399
3205
|
}
|
|
4400
3206
|
|
|
4401
3207
|
//#endregion
|
|
@@ -4553,6 +3359,19 @@ function createTailorDBService(params) {
|
|
|
4553
3359
|
if (locations.length > 0) logger.warn(`TailorDB type "${typeName}" has permission rule(s) ${locations.join(", ")} in object form without an explicit "permit"; they default to "deny". Set permit: true (allow) or permit: false (deny) to silence this warning.`);
|
|
4554
3360
|
}
|
|
4555
3361
|
};
|
|
3362
|
+
const validateRequiredPermissions = () => {
|
|
3363
|
+
const errors = [];
|
|
3364
|
+
for (const fileTypes of Object.values(rawTypes)) for (const [typeName, type] of Object.entries(fileTypes)) {
|
|
3365
|
+
const effectiveGqlOperations = type.metadata.settings?.gqlOperations ?? config.gqlOperations;
|
|
3366
|
+
const { missingPermission, missingGqlPermission } = findMissingPermissionConfig(type.metadata.permissions, effectiveGqlOperations);
|
|
3367
|
+
if (!missingPermission && !missingGqlPermission) continue;
|
|
3368
|
+
const source = formatTailorDBTypeSourceInfo(typeSourceInfo[typeName]);
|
|
3369
|
+
const location = source ? ` (${source})` : "";
|
|
3370
|
+
if (missingPermission) errors.push(`TailorDB type "${typeName}"${location} has no .permission() configured. TailorDB denies all record operations for types without permission; call .permission(...) to grant access explicitly.`);
|
|
3371
|
+
if (missingGqlPermission) errors.push(`TailorDB type "${typeName}"${location} has no .gqlPermission() configured, but GraphQL operations are enabled for it. Call .gqlPermission(...) to grant GraphQL access explicitly, or disable GraphQL exposure with .features({ gqlOperations: { create: false, update: false, delete: false, read: false } }).`);
|
|
3372
|
+
}
|
|
3373
|
+
if (errors.length > 0) throw new Error(`TailorDB permission configuration errors in service "${namespace}":\n${errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
3374
|
+
};
|
|
4556
3375
|
/**
|
|
4557
3376
|
* Process plugins for a type and add generated types to rawTypes
|
|
4558
3377
|
* @param rawType - The raw TailorDB type being processed
|
|
@@ -4645,6 +3464,7 @@ function createTailorDBService(params) {
|
|
|
4645
3464
|
else await Promise.all(typeFiles.map((typeFile) => loadTypeFile(typeFile, tsconfig)));
|
|
4646
3465
|
doParseTypes();
|
|
4647
3466
|
warnOmittedPermit();
|
|
3467
|
+
validateRequiredPermissions();
|
|
4648
3468
|
return types;
|
|
4649
3469
|
})();
|
|
4650
3470
|
return loadPromise;
|
|
@@ -4686,7 +3506,10 @@ function createTailorDBService(params) {
|
|
|
4686
3506
|
hasGeneratedTypes = true;
|
|
4687
3507
|
logger.log(` Generated: ${styles.success(generatedType.name)} by namespace plugin ${styles.info(pluginId)}`);
|
|
4688
3508
|
}
|
|
4689
|
-
if (hasGeneratedTypes || hadPreviousGeneratedTypes)
|
|
3509
|
+
if (hasGeneratedTypes || hadPreviousGeneratedTypes) {
|
|
3510
|
+
doParseTypes();
|
|
3511
|
+
validateRequiredPermissions();
|
|
3512
|
+
}
|
|
4690
3513
|
}
|
|
4691
3514
|
};
|
|
4692
3515
|
}
|
|
@@ -4773,9 +3596,31 @@ function resolveBundleConcurrency() {
|
|
|
4773
3596
|
* @param worker - Async worker function
|
|
4774
3597
|
* @returns Worker results in input order
|
|
4775
3598
|
*/
|
|
4776
|
-
function withBundleConcurrency(items, worker) {
|
|
4777
|
-
const
|
|
4778
|
-
|
|
3599
|
+
async function withBundleConcurrency(items, worker) {
|
|
3600
|
+
const resultCount = items.length;
|
|
3601
|
+
const workItems = items.flatMap((item, index) => [{
|
|
3602
|
+
index,
|
|
3603
|
+
item
|
|
3604
|
+
}]);
|
|
3605
|
+
const results = [];
|
|
3606
|
+
results.length = resultCount;
|
|
3607
|
+
let nextWorkIndex = 0;
|
|
3608
|
+
let rejection;
|
|
3609
|
+
const runWorker = async () => {
|
|
3610
|
+
while (!rejection) {
|
|
3611
|
+
const workItem = workItems[nextWorkIndex++];
|
|
3612
|
+
if (workItem === void 0) return;
|
|
3613
|
+
try {
|
|
3614
|
+
results[workItem.index] = await worker(workItem.item);
|
|
3615
|
+
} catch (reason) {
|
|
3616
|
+
rejection ??= { reason };
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
};
|
|
3620
|
+
const workerCount = Math.min(resolveBundleConcurrency(), workItems.length);
|
|
3621
|
+
await Promise.all(Array.from({ length: workerCount }, runWorker));
|
|
3622
|
+
if (rejection) throw rejection.reason;
|
|
3623
|
+
return results;
|
|
4779
3624
|
}
|
|
4780
3625
|
|
|
4781
3626
|
//#endregion
|
|
@@ -4786,7 +3631,7 @@ function withBundleConcurrency(items, worker) {
|
|
|
4786
3631
|
* Two delivery paths:
|
|
4787
3632
|
* - Apply config: shipped with apply and evaluated by the platform before
|
|
4788
3633
|
* invoking user code.
|
|
4789
|
-
* - Bundle inline: interpolated into the generated
|
|
3634
|
+
* - Bundle inline: interpolated into the generated entry module and
|
|
4790
3635
|
* evaluated inside the bundled script at function entry.
|
|
4791
3636
|
*
|
|
4792
3637
|
* The principal field mapping (server → SDK) shared across services is built by
|
|
@@ -4876,7 +3721,7 @@ function buildResolverOperationHookExpr(env) {
|
|
|
4876
3721
|
* Bundle executors from the specified configuration
|
|
4877
3722
|
*
|
|
4878
3723
|
* This function:
|
|
4879
|
-
* 1. Creates entry
|
|
3724
|
+
* 1. Creates an in-memory entry module that extracts operation.body
|
|
4880
3725
|
* 2. Bundles in a single step with tree-shaking
|
|
4881
3726
|
* @param options - Bundle executor options
|
|
4882
3727
|
* @returns Map of executor name to bundled code
|
|
@@ -4911,21 +3756,18 @@ async function bundleExecutors(options) {
|
|
|
4911
3756
|
logger.debug(" No function executors to bundle");
|
|
4912
3757
|
return bundledCode;
|
|
4913
3758
|
}
|
|
4914
|
-
const outputDir = path.resolve(getDistDir(), "executors");
|
|
4915
|
-
fs$1.mkdirSync(outputDir, { recursive: true });
|
|
4916
|
-
await removeStaleEntryFiles(outputDir);
|
|
4917
3759
|
let tsconfig;
|
|
4918
3760
|
try {
|
|
4919
3761
|
tsconfig = await resolveTSConfig();
|
|
4920
3762
|
} catch {
|
|
4921
3763
|
tsconfig = void 0;
|
|
4922
3764
|
}
|
|
4923
|
-
const results = await withBundleConcurrency(executors, (executor) => bundleSingleExecutor(executor,
|
|
3765
|
+
const results = await withBundleConcurrency(executors, (executor) => bundleSingleExecutor(executor, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel));
|
|
4924
3766
|
for (const [name, code] of results) bundledCode.set(name, code);
|
|
4925
3767
|
logger.log(`${styles.success("Bundled")} ${styles.info("\"executor\"")}`);
|
|
4926
3768
|
return bundledCode;
|
|
4927
3769
|
}
|
|
4928
|
-
async function bundleSingleExecutor(executor,
|
|
3770
|
+
async function bundleSingleExecutor(executor, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
|
|
4929
3771
|
const serializedTriggerContext = serializeTriggerContext(triggerContext);
|
|
4930
3772
|
const contextHash = computeBundlerContextHash({
|
|
4931
3773
|
sourceFile: executor.sourceFile,
|
|
@@ -4941,7 +3783,6 @@ async function bundleSingleExecutor(executor, outputDir, tsconfig, triggerContex
|
|
|
4941
3783
|
sourceFile: executor.sourceFile,
|
|
4942
3784
|
contextHash,
|
|
4943
3785
|
async build(cachePlugins) {
|
|
4944
|
-
const entryPath = path.join(outputDir, `${executor.name}.entry.js`);
|
|
4945
3786
|
const entryContent = multiline`
|
|
4946
3787
|
import _internalExecutor from "${path.resolve(executor.sourceFile)}";
|
|
4947
3788
|
|
|
@@ -4952,12 +3793,13 @@ async function bundleSingleExecutor(executor, outputDir, tsconfig, triggerContex
|
|
|
4952
3793
|
|
|
4953
3794
|
export { __executor_function as main };
|
|
4954
3795
|
`;
|
|
4955
|
-
|
|
3796
|
+
const entry = createVirtualEntry(`executor:${executor.name}`, entryContent);
|
|
4956
3797
|
const triggerPlugin = createTriggerTransformPlugin(triggerContext);
|
|
4957
|
-
const plugins =
|
|
3798
|
+
const plugins = [entry.plugin];
|
|
3799
|
+
if (triggerPlugin) plugins.push(triggerPlugin);
|
|
4958
3800
|
plugins.push(platformBundleDefinePlugin, ...cachePlugins);
|
|
4959
3801
|
return (await rolldown.build({
|
|
4960
|
-
input:
|
|
3802
|
+
input: entry.input,
|
|
4961
3803
|
write: false,
|
|
4962
3804
|
output: {
|
|
4963
3805
|
format: "esm",
|
|
@@ -5040,8 +3882,6 @@ async function bundleHttpAdapters(adapters, cache, bundleLogLevel = "DEBUG") {
|
|
|
5040
3882
|
};
|
|
5041
3883
|
logger.newline();
|
|
5042
3884
|
logger.log(`Bundling ${styles.highlight(adapters.length.toString())} files for ${styles.info("\"http-adapter\"")}`);
|
|
5043
|
-
const outputDir = path.resolve(getDistDir(), "http-adapters");
|
|
5044
|
-
fs$1.mkdirSync(outputDir, { recursive: true });
|
|
5045
3885
|
let tsconfig;
|
|
5046
3886
|
try {
|
|
5047
3887
|
tsconfig = await resolveTSConfig();
|
|
@@ -5053,7 +3893,7 @@ async function bundleHttpAdapters(adapters, cache, bundleLogLevel = "DEBUG") {
|
|
|
5053
3893
|
adapter,
|
|
5054
3894
|
kind
|
|
5055
3895
|
}));
|
|
5056
|
-
}), ({ adapter, kind }) => bundleAdapterScript(adapter, kind,
|
|
3896
|
+
}), ({ adapter, kind }) => bundleAdapterScript(adapter, kind, tsconfig, cache, bundleLogLevel));
|
|
5057
3897
|
const bundledInputs = /* @__PURE__ */ new Map();
|
|
5058
3898
|
const bundledOutputs = /* @__PURE__ */ new Map();
|
|
5059
3899
|
for (const [name, kind, code] of results) if (kind === "input") bundledInputs.set(name, code);
|
|
@@ -5064,7 +3904,7 @@ async function bundleHttpAdapters(adapters, cache, bundleLogLevel = "DEBUG") {
|
|
|
5064
3904
|
bundledOutputs
|
|
5065
3905
|
};
|
|
5066
3906
|
}
|
|
5067
|
-
async function bundleAdapterScript(adapter, kind,
|
|
3907
|
+
async function bundleAdapterScript(adapter, kind, tsconfig, cache, bundleLogLevel = "DEBUG") {
|
|
5068
3908
|
const contextHash = computeBundlerContextHash({
|
|
5069
3909
|
sourceFile: adapter.sourceFile,
|
|
5070
3910
|
serializedTriggerContext: kind === "input" ? adapter.methods.join(",") : "",
|
|
@@ -5080,9 +3920,11 @@ async function bundleAdapterScript(adapter, kind, outputDir, tsconfig, cache, bu
|
|
|
5080
3920
|
sourceFile: adapter.sourceFile,
|
|
5081
3921
|
contextHash,
|
|
5082
3922
|
async build(cachePlugins) {
|
|
5083
|
-
const entryPath = path.join(outputDir, `${adapter.name}.${kind}.entry.js`);
|
|
5084
3923
|
const absoluteSourcePath = path.resolve(adapter.sourceFile);
|
|
3924
|
+
const entryContent = kind === "input" ? buildInputEntry(absoluteSourcePath, adapter.methods, GRAPHQL_WEB_MODULE) : buildOutputEntry(absoluteSourcePath);
|
|
3925
|
+
const entry = createVirtualEntry(`http-adapter:${adapter.name}:${kind}`, entryContent);
|
|
5085
3926
|
const plugins = [
|
|
3927
|
+
entry.plugin,
|
|
5086
3928
|
{
|
|
5087
3929
|
name: "http-adapter-reject-node-imports",
|
|
5088
3930
|
resolveId(source) {
|
|
@@ -5106,30 +3948,21 @@ async function bundleAdapterScript(adapter, kind, outputDir, tsconfig, cache, bu
|
|
|
5106
3948
|
},
|
|
5107
3949
|
...cachePlugins
|
|
5108
3950
|
];
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
transform: { target: "es2017" },
|
|
5125
|
-
treeshake: composeFunctionTreeshakeOptions([createLogLevelTreeshakeOptions(bundleLogLevel)]),
|
|
5126
|
-
logLevel: "silent"
|
|
5127
|
-
})).output[0].code;
|
|
5128
|
-
} finally {
|
|
5129
|
-
try {
|
|
5130
|
-
fs$1.rmSync(entryPath, { force: true });
|
|
5131
|
-
} catch {}
|
|
5132
|
-
}
|
|
3951
|
+
const bundled = (await rolldown.build({
|
|
3952
|
+
input: entry.input,
|
|
3953
|
+
write: false,
|
|
3954
|
+
output: {
|
|
3955
|
+
format: "iife",
|
|
3956
|
+
sourcemap: false,
|
|
3957
|
+
minify: true,
|
|
3958
|
+
codeSplitting: false
|
|
3959
|
+
},
|
|
3960
|
+
tsconfig,
|
|
3961
|
+
plugins,
|
|
3962
|
+
transform: { target: "es2017" },
|
|
3963
|
+
treeshake: composeFunctionTreeshakeOptions([createLogLevelTreeshakeOptions(bundleLogLevel)]),
|
|
3964
|
+
logLevel: "silent"
|
|
3965
|
+
})).output[0].code;
|
|
5133
3966
|
const byteLength = Buffer.byteLength(bundled, "utf8");
|
|
5134
3967
|
if (byteLength > ADAPTER_BUNDLE_ERROR_BYTES) throw new Error(`HTTP adapter "${adapter.name}" ${kind} script is ${byteLength} bytes, exceeding the ${ADAPTER_BUNDLE_ERROR_BYTES} byte limit`);
|
|
5135
3968
|
if (byteLength > ADAPTER_BUNDLE_WARN_BYTES) logger.warn(`HTTP adapter "${adapter.name}" ${kind} script is ${byteLength} bytes, larger than the recommended ${ADAPTER_BUNDLE_WARN_BYTES} byte limit`);
|
|
@@ -5328,7 +4161,7 @@ async function loadResolver(resolverFilePath) {
|
|
|
5328
4161
|
*
|
|
5329
4162
|
* This function:
|
|
5330
4163
|
* 1. Uses a transform plugin to add validation wrapper during bundling
|
|
5331
|
-
* 2. Creates entry
|
|
4164
|
+
* 2. Creates an in-memory entry module
|
|
5332
4165
|
* 3. Bundles in a single step with tree-shaking
|
|
5333
4166
|
* @param namespace - Resolver namespace name
|
|
5334
4167
|
* @param config - Resolver file loading configuration
|
|
@@ -5359,21 +4192,18 @@ async function bundleResolvers(namespace, config, triggerContext, cache, inlineS
|
|
|
5359
4192
|
sourceFile: file
|
|
5360
4193
|
});
|
|
5361
4194
|
}
|
|
5362
|
-
const outputDir = path.resolve(getDistDir(), "resolvers");
|
|
5363
|
-
fs$1.mkdirSync(outputDir, { recursive: true });
|
|
5364
|
-
await removeStaleEntryFiles(outputDir);
|
|
5365
4195
|
let tsconfig;
|
|
5366
4196
|
try {
|
|
5367
4197
|
tsconfig = await resolveTSConfig();
|
|
5368
4198
|
} catch {
|
|
5369
4199
|
tsconfig = void 0;
|
|
5370
4200
|
}
|
|
5371
|
-
const results = await withBundleConcurrency(resolvers, (resolver) => bundleSingleResolver(namespace, resolver,
|
|
4201
|
+
const results = await withBundleConcurrency(resolvers, (resolver) => bundleSingleResolver(namespace, resolver, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel));
|
|
5372
4202
|
for (const [name, code] of results) bundledCode.set(name, code);
|
|
5373
4203
|
logger.log(`${styles.success("Bundled")} ${styles.info(`"${namespace}"`)}`);
|
|
5374
4204
|
return bundledCode;
|
|
5375
4205
|
}
|
|
5376
|
-
async function bundleSingleResolver(namespace, resolver,
|
|
4206
|
+
async function bundleSingleResolver(namespace, resolver, tsconfig, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
|
|
5377
4207
|
const serializedTriggerContext = serializeTriggerContext(triggerContext);
|
|
5378
4208
|
const contextHash = computeBundlerContextHash({
|
|
5379
4209
|
sourceFile: resolver.sourceFile,
|
|
@@ -5390,7 +4220,6 @@ async function bundleSingleResolver(namespace, resolver, outputDir, tsconfig, tr
|
|
|
5390
4220
|
sourceFile: resolver.sourceFile,
|
|
5391
4221
|
contextHash,
|
|
5392
4222
|
async build(cachePlugins) {
|
|
5393
|
-
const entryPath = path.join(outputDir, `${resolver.name}.entry.js`);
|
|
5394
4223
|
const entryContent = multiline`
|
|
5395
4224
|
import _internalResolver from "${path.resolve(resolver.sourceFile)}";
|
|
5396
4225
|
import { t } from "@tailor-platform/sdk";
|
|
@@ -5417,12 +4246,13 @@ async function bundleSingleResolver(namespace, resolver, outputDir, tsconfig, tr
|
|
|
5417
4246
|
|
|
5418
4247
|
export { $tailor_resolver_body as main };
|
|
5419
4248
|
`;
|
|
5420
|
-
|
|
4249
|
+
const entry = createVirtualEntry(`resolver:${resolver.name}`, entryContent);
|
|
5421
4250
|
const triggerPlugin = createTriggerTransformPlugin(triggerContext);
|
|
5422
|
-
const plugins =
|
|
4251
|
+
const plugins = [entry.plugin];
|
|
4252
|
+
if (triggerPlugin) plugins.push(triggerPlugin);
|
|
5423
4253
|
plugins.push(platformBundleDefinePlugin, ...cachePlugins);
|
|
5424
4254
|
return (await rolldown.build({
|
|
5425
|
-
input:
|
|
4255
|
+
input: entry.input,
|
|
5426
4256
|
write: false,
|
|
5427
4257
|
output: {
|
|
5428
4258
|
format: "esm",
|
|
@@ -5511,36 +4341,15 @@ function assertUniqueResolverNames(resolvers, namespace) {
|
|
|
5511
4341
|
*/
|
|
5512
4342
|
function findVariableDeclarationsByName(program) {
|
|
5513
4343
|
const declarations = /* @__PURE__ */ new Map();
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
end: varDecl.end
|
|
5523
|
-
});
|
|
5524
|
-
}
|
|
5525
|
-
}
|
|
5526
|
-
if (nodeType === "ExportNamedDeclaration") {
|
|
5527
|
-
const exportDecl = node;
|
|
5528
|
-
const declaration = exportDecl.declaration;
|
|
5529
|
-
if (declaration?.type === "VariableDeclaration") {
|
|
5530
|
-
const varDecl = declaration;
|
|
5531
|
-
for (const decl of varDecl.declarations) if (decl.id.type === "Identifier" && decl.id.name) declarations.set(decl.id.name, {
|
|
5532
|
-
start: exportDecl.start,
|
|
5533
|
-
end: exportDecl.end
|
|
5534
|
-
});
|
|
5535
|
-
}
|
|
5536
|
-
}
|
|
5537
|
-
for (const key of Object.keys(node)) {
|
|
5538
|
-
const child = node[key];
|
|
5539
|
-
if (Array.isArray(child)) child.forEach((c) => walk(c));
|
|
5540
|
-
else if (child && typeof child === "object") walk(child);
|
|
5541
|
-
}
|
|
4344
|
+
for (const statement of program.body) {
|
|
4345
|
+
const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
|
|
4346
|
+
if (declaration?.type !== "VariableDeclaration") continue;
|
|
4347
|
+
const variableDeclaration = declaration;
|
|
4348
|
+
for (const declarator of variableDeclaration.declarations) if (declarator.id.type === "Identifier") declarations.set(declarator.id.name, {
|
|
4349
|
+
start: statement.start,
|
|
4350
|
+
end: statement.end
|
|
4351
|
+
});
|
|
5542
4352
|
}
|
|
5543
|
-
walk(program);
|
|
5544
4353
|
return declarations;
|
|
5545
4354
|
}
|
|
5546
4355
|
/**
|
|
@@ -5567,48 +4376,32 @@ function findWorkflowDefaultExport(program) {
|
|
|
5567
4376
|
}
|
|
5568
4377
|
/**
|
|
5569
4378
|
* Transform workflow source code
|
|
5570
|
-
* - Transform .trigger() calls to tailor.workflow.triggerJobFunction()
|
|
5571
4379
|
* - Other jobs: remove entire variable declaration
|
|
5572
4380
|
* @param source - The source code to transform
|
|
5573
4381
|
* @param targetJobName - The name of the target job (from job config)
|
|
5574
4382
|
* @param targetJobExportName - The export name of the target job (optional, for enhanced detection)
|
|
5575
4383
|
* @param otherJobExportNames - Export names of other jobs to remove (optional, for enhanced detection)
|
|
5576
|
-
* @param allJobsMap - Map from export name to job name for trigger transformation (optional)
|
|
5577
4384
|
* @returns Transformed workflow source code
|
|
5578
4385
|
*/
|
|
5579
|
-
function transformWorkflowSource(source, targetJobName, targetJobExportName, otherJobExportNames
|
|
4386
|
+
function transformWorkflowSource(source, targetJobName, targetJobExportName, otherJobExportNames) {
|
|
5580
4387
|
const { program } = parseSync("input.ts", source);
|
|
5581
4388
|
const detectedJobs = findAllJobs(program, source);
|
|
5582
4389
|
if (!detectedJobs.some((j) => j.name === targetJobName)) return source;
|
|
5583
|
-
const jobNameMap = allJobsMap ?? buildJobNameMap(detectedJobs);
|
|
5584
4390
|
const allDeclarations = findVariableDeclarationsByName(program);
|
|
5585
|
-
const triggerCalls = detectTriggerCalls(program, source);
|
|
5586
4391
|
const replacements = [];
|
|
5587
|
-
const
|
|
5588
|
-
const isInsideRemovedRange = (pos) => {
|
|
5589
|
-
return removedRanges.some((r) => pos >= r.start && pos < r.end);
|
|
5590
|
-
};
|
|
5591
|
-
const isAlreadyMarkedForRemoval = (start) => {
|
|
5592
|
-
return removedRanges.some((r) => r.start === start);
|
|
5593
|
-
};
|
|
4392
|
+
const removedStarts = /* @__PURE__ */ new Set();
|
|
5594
4393
|
for (const job of detectedJobs) {
|
|
5595
4394
|
if (job.name === targetJobName) continue;
|
|
5596
|
-
if (job.statementRange && !
|
|
4395
|
+
if (job.statementRange && !removedStarts.has(job.statementRange.start)) {
|
|
5597
4396
|
const endPos = findStatementEnd(source, job.statementRange.end);
|
|
5598
|
-
|
|
5599
|
-
start: job.statementRange.start,
|
|
5600
|
-
end: endPos
|
|
5601
|
-
});
|
|
4397
|
+
removedStarts.add(job.statementRange.start);
|
|
5602
4398
|
replacements.push({
|
|
5603
4399
|
start: job.statementRange.start,
|
|
5604
4400
|
end: endPos,
|
|
5605
4401
|
text: ""
|
|
5606
4402
|
});
|
|
5607
4403
|
} else if (!job.statementRange) {
|
|
5608
|
-
|
|
5609
|
-
start: job.bodyValueRange.start,
|
|
5610
|
-
end: job.bodyValueRange.end
|
|
5611
|
-
});
|
|
4404
|
+
removedStarts.add(job.bodyValueRange.start);
|
|
5612
4405
|
replacements.push({
|
|
5613
4406
|
start: job.bodyValueRange.start,
|
|
5614
4407
|
end: job.bodyValueRange.end,
|
|
@@ -5619,12 +4412,9 @@ function transformWorkflowSource(source, targetJobName, targetJobExportName, oth
|
|
|
5619
4412
|
if (otherJobExportNames) for (const exportName of otherJobExportNames) {
|
|
5620
4413
|
if (exportName === targetJobExportName) continue;
|
|
5621
4414
|
const declRange = allDeclarations.get(exportName);
|
|
5622
|
-
if (declRange && !
|
|
4415
|
+
if (declRange && !removedStarts.has(declRange.start)) {
|
|
5623
4416
|
const endPos = findStatementEnd(source, declRange.end);
|
|
5624
|
-
|
|
5625
|
-
start: declRange.start,
|
|
5626
|
-
end: endPos
|
|
5627
|
-
});
|
|
4417
|
+
removedStarts.add(declRange.start);
|
|
5628
4418
|
replacements.push({
|
|
5629
4419
|
start: declRange.start,
|
|
5630
4420
|
end: endPos,
|
|
@@ -5633,31 +4423,15 @@ function transformWorkflowSource(source, targetJobName, targetJobExportName, oth
|
|
|
5633
4423
|
}
|
|
5634
4424
|
}
|
|
5635
4425
|
const workflowExport = findWorkflowDefaultExport(program);
|
|
5636
|
-
if (workflowExport && !
|
|
4426
|
+
if (workflowExport && !removedStarts.has(workflowExport.start)) {
|
|
5637
4427
|
const endPos = findStatementEnd(source, workflowExport.end);
|
|
5638
|
-
|
|
5639
|
-
start: workflowExport.start,
|
|
5640
|
-
end: endPos
|
|
5641
|
-
});
|
|
4428
|
+
removedStarts.add(workflowExport.start);
|
|
5642
4429
|
replacements.push({
|
|
5643
4430
|
start: workflowExport.start,
|
|
5644
4431
|
end: endPos,
|
|
5645
4432
|
text: ""
|
|
5646
4433
|
});
|
|
5647
4434
|
}
|
|
5648
|
-
for (const call of triggerCalls) {
|
|
5649
|
-
if (isInsideRemovedRange(call.callRange.start)) continue;
|
|
5650
|
-
const jobName = jobNameMap.get(call.identifierName);
|
|
5651
|
-
if (jobName) {
|
|
5652
|
-
const optionsPart = call.optionsText !== void 0 ? `, ${call.optionsText}` : "";
|
|
5653
|
-
const transformedCall = `tailor.workflow.triggerJobFunction("${jobName}", ${call.argsText || "undefined"}${optionsPart})`;
|
|
5654
|
-
replacements.push({
|
|
5655
|
-
start: call.callRange.start,
|
|
5656
|
-
end: call.callRange.end,
|
|
5657
|
-
text: transformedCall
|
|
5658
|
-
});
|
|
5659
|
-
}
|
|
5660
|
-
}
|
|
5661
4435
|
return applyReplacements(source, replacements);
|
|
5662
4436
|
}
|
|
5663
4437
|
|
|
@@ -5666,7 +4440,7 @@ function transformWorkflowSource(source, targetJobName, targetJobExportName, oth
|
|
|
5666
4440
|
function safeRealpath(p) {
|
|
5667
4441
|
const resolved = path.resolve(p);
|
|
5668
4442
|
try {
|
|
5669
|
-
return fs
|
|
4443
|
+
return fs.realpathSync(resolved);
|
|
5670
4444
|
} catch (e) {
|
|
5671
4445
|
logger.debug(`realpathSync failed for ${resolved}: ${e instanceof Error ? e.message : e}`);
|
|
5672
4446
|
return resolved;
|
|
@@ -5678,7 +4452,7 @@ function safeRealpath(p) {
|
|
|
5678
4452
|
* This function:
|
|
5679
4453
|
* 1. Detects which jobs are actually used (mainJobs + their dependencies)
|
|
5680
4454
|
* 2. Uses a transform plugin to transform trigger calls during bundling
|
|
5681
|
-
* 3. Creates entry
|
|
4455
|
+
* 3. Creates an in-memory entry module and bundles with tree-shaking
|
|
5682
4456
|
*
|
|
5683
4457
|
* Returns metadata about which jobs each workflow uses.
|
|
5684
4458
|
* @param allJobs - All available job infos
|
|
@@ -5699,22 +4473,16 @@ async function bundleWorkflowJobs(allJobs, mainJobNames, env = {}, triggerContex
|
|
|
5699
4473
|
bundledCode: /* @__PURE__ */ new Map()
|
|
5700
4474
|
};
|
|
5701
4475
|
}
|
|
5702
|
-
const { usedJobs, mainJobDeps } = await filterUsedJobs(allJobs, mainJobNames);
|
|
4476
|
+
const { usedJobs, mainJobDeps } = await filterUsedJobs(allJobs, mainJobNames, triggerContext);
|
|
5703
4477
|
logger.newline();
|
|
5704
4478
|
logger.log(`Bundling ${styles.highlight(usedJobs.length.toString())} files for ${styles.info("\"workflow-job\"")}`);
|
|
5705
|
-
const outputDir = path.resolve(getDistDir(), "workflow-jobs");
|
|
5706
|
-
fs$1.mkdirSync(outputDir, { recursive: true });
|
|
5707
|
-
const currentJobNames = new Set(usedJobs.map((j) => j.name));
|
|
5708
|
-
const existingFiles = fs$1.readdirSync(outputDir);
|
|
5709
|
-
for (const file of existingFiles) if (file.endsWith(".js") && !currentJobNames.has(path.basename(file, ".js"))) fs$1.rmSync(path.join(outputDir, file), { force: true });
|
|
5710
|
-
else if (file.endsWith(".js.map") && !currentJobNames.has(path.basename(file, ".js.map"))) fs$1.rmSync(path.join(outputDir, file), { force: true });
|
|
5711
4479
|
let tsconfig;
|
|
5712
4480
|
try {
|
|
5713
4481
|
tsconfig = await resolveTSConfig();
|
|
5714
4482
|
} catch {
|
|
5715
4483
|
tsconfig = void 0;
|
|
5716
4484
|
}
|
|
5717
|
-
const results = await withBundleConcurrency(usedJobs, (job) => bundleSingleJob(job, usedJobs,
|
|
4485
|
+
const results = await withBundleConcurrency(usedJobs, (job) => bundleSingleJob(job, usedJobs, tsconfig, env, triggerContext, cache, inlineSourcemap, bundleLogLevel));
|
|
5718
4486
|
const bundledCode = /* @__PURE__ */ new Map();
|
|
5719
4487
|
for (const [name, code] of results) bundledCode.set(name, code);
|
|
5720
4488
|
logger.log(`${styles.success("Bundled")} ${styles.info("\"workflow-job\"")}`);
|
|
@@ -5733,9 +4501,10 @@ async function bundleWorkflowJobs(allJobs, mainJobNames, env = {}, triggerContex
|
|
|
5733
4501
|
* Also returns a map of mainJob -> all jobs it depends on (for metadata).
|
|
5734
4502
|
* @param allJobs - All available job infos
|
|
5735
4503
|
* @param mainJobNames - Names of main jobs
|
|
4504
|
+
* @param triggerContext - Module binding metadata for resolving trigger targets
|
|
5736
4505
|
* @returns Used jobs and main job dependency map
|
|
5737
4506
|
*/
|
|
5738
|
-
async function filterUsedJobs(allJobs, mainJobNames) {
|
|
4507
|
+
async function filterUsedJobs(allJobs, mainJobNames, triggerContext) {
|
|
5739
4508
|
if (allJobs.length === 0 || mainJobNames.length === 0) return {
|
|
5740
4509
|
usedJobs: [],
|
|
5741
4510
|
mainJobDeps: {}
|
|
@@ -5746,26 +4515,19 @@ async function filterUsedJobs(allJobs, mainJobNames) {
|
|
|
5746
4515
|
existing.push(job);
|
|
5747
4516
|
jobsBySourceFile.set(job.sourceFile, existing);
|
|
5748
4517
|
}
|
|
5749
|
-
const exportNameToJobName = /* @__PURE__ */ new Map();
|
|
5750
|
-
for (const job of allJobs) exportNameToJobName.set(job.exportName, job.name);
|
|
5751
4518
|
const dependencies = /* @__PURE__ */ new Map();
|
|
5752
4519
|
const fileResults = await Promise.all(Array.from(jobsBySourceFile.entries()).map(async ([sourceFile, jobs]) => {
|
|
5753
4520
|
try {
|
|
5754
|
-
const source = await fs
|
|
4521
|
+
const source = await fs.promises.readFile(sourceFile, "utf-8");
|
|
5755
4522
|
const { program } = parseSync("input.ts", source);
|
|
5756
4523
|
const detectedJobs = findAllJobs(program, source);
|
|
5757
|
-
const
|
|
5758
|
-
for (const detected of detectedJobs) if (detected.exportName) localExportNameToJobName.set(detected.exportName, detected.name);
|
|
5759
|
-
const triggerCalls = detectTriggerCalls(program, source);
|
|
4524
|
+
const triggerCalls = detectResolvedTriggerCalls(program, source, triggerContext, sourceFile);
|
|
5760
4525
|
const jobDependencies = [];
|
|
5761
4526
|
for (const job of jobs) {
|
|
5762
4527
|
const detectedJob = detectedJobs.find((d) => d.name === job.name);
|
|
5763
4528
|
if (!detectedJob) continue;
|
|
5764
4529
|
const jobDeps = /* @__PURE__ */ new Set();
|
|
5765
|
-
for (const call of triggerCalls) if (call.callRange.start >= detectedJob.bodyValueRange.start && call.callRange.end <= detectedJob.bodyValueRange.end)
|
|
5766
|
-
const triggeredJobName = localExportNameToJobName.get(call.identifierName) || exportNameToJobName.get(call.identifierName);
|
|
5767
|
-
if (triggeredJobName) jobDeps.add(triggeredJobName);
|
|
5768
|
-
}
|
|
4530
|
+
for (const call of triggerCalls) if (call.kind === "job" && call.callRange.start >= detectedJob.bodyValueRange.start && call.callRange.end <= detectedJob.bodyValueRange.end) jobDeps.add(call.targetName);
|
|
5769
4531
|
if (jobDeps.size > 0) jobDependencies.push({
|
|
5770
4532
|
jobName: job.name,
|
|
5771
4533
|
deps: jobDeps
|
|
@@ -5796,7 +4558,7 @@ async function filterUsedJobs(allJobs, mainJobNames) {
|
|
|
5796
4558
|
mainJobDeps
|
|
5797
4559
|
};
|
|
5798
4560
|
}
|
|
5799
|
-
async function bundleSingleJob(job, allJobs,
|
|
4561
|
+
async function bundleSingleJob(job, allJobs, tsconfig, env, triggerContext, cache, inlineSourcemap, bundleLogLevel = "DEBUG") {
|
|
5800
4562
|
const serializedTriggerContext = serializeTriggerContext(triggerContext);
|
|
5801
4563
|
const sortedEnvPrefix = JSON.stringify(Object.fromEntries(Object.entries(env).toSorted(([a], [b]) => a.localeCompare(b))));
|
|
5802
4564
|
const contextHash = computeBundlerContextHash({
|
|
@@ -5814,7 +4576,6 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerCo
|
|
|
5814
4576
|
sourceFile: job.sourceFile,
|
|
5815
4577
|
contextHash,
|
|
5816
4578
|
async build(cachePlugins) {
|
|
5817
|
-
const entryPath = path.join(outputDir, `${job.name}.entry.js`);
|
|
5818
4579
|
const absoluteSourcePath = path.resolve(job.sourceFile);
|
|
5819
4580
|
const entryContent = multiline`
|
|
5820
4581
|
import { ${job.exportName} } from "${absoluteSourcePath}";
|
|
@@ -5825,12 +4586,11 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerCo
|
|
|
5825
4586
|
return await ${job.exportName}.body(input, { env, invoker });
|
|
5826
4587
|
}
|
|
5827
4588
|
`;
|
|
5828
|
-
|
|
5829
|
-
const otherJobExportNames = allJobs.filter((j) => j.name !== job.name).map((j) => j.exportName);
|
|
5830
|
-
const allJobsMap = /* @__PURE__ */ new Map();
|
|
5831
|
-
for (const j of allJobs) allJobsMap.set(j.exportName, j.name);
|
|
4589
|
+
const entry = createVirtualEntry(`workflow-job:${job.name}`, entryContent);
|
|
5832
4590
|
const resolvedSourceFile = safeRealpath(job.sourceFile);
|
|
4591
|
+
const otherJobExportNames = allJobs.filter((candidate) => candidate.name !== job.name && safeRealpath(candidate.sourceFile) === resolvedSourceFile).map((j) => j.exportName);
|
|
5833
4592
|
const plugins = [
|
|
4593
|
+
entry.plugin,
|
|
5834
4594
|
{
|
|
5835
4595
|
name: "workflow-transform",
|
|
5836
4596
|
transform: {
|
|
@@ -5838,8 +4598,8 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerCo
|
|
|
5838
4598
|
handler(code, id) {
|
|
5839
4599
|
if (!code.includes("createWorkflowJob") && !code.includes("createWorkflow") && !code.includes(".trigger(")) return null;
|
|
5840
4600
|
let transformed = code;
|
|
5841
|
-
if (safeRealpath(id) === resolvedSourceFile) transformed = transformWorkflowSource(code, job.name, job.exportName, otherJobExportNames
|
|
5842
|
-
if (
|
|
4601
|
+
if (safeRealpath(id) === resolvedSourceFile) transformed = transformWorkflowSource(code, job.name, job.exportName, otherJobExportNames);
|
|
4602
|
+
if (transformed.includes(".trigger(")) transformed = transformFunctionTriggers(transformed, triggerContext, id);
|
|
5843
4603
|
if (transformed === code) return null;
|
|
5844
4604
|
return { code: transformed };
|
|
5845
4605
|
}
|
|
@@ -5849,7 +4609,7 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerCo
|
|
|
5849
4609
|
...cachePlugins
|
|
5850
4610
|
];
|
|
5851
4611
|
return (await rolldown.build({
|
|
5852
|
-
input:
|
|
4612
|
+
input: entry.input,
|
|
5853
4613
|
write: false,
|
|
5854
4614
|
output: {
|
|
5855
4615
|
format: "esm",
|
|
@@ -5908,7 +4668,7 @@ const RetryPolicySchema = z.strictObject({
|
|
|
5908
4668
|
});
|
|
5909
4669
|
const ConcurrencyPolicySchema = z.strictObject({ maxConcurrentExecutions: z.number().int().min(1).max(1e3).describe("Maximum number of concurrent executions (1-1000)") });
|
|
5910
4670
|
const ExecutionPolicyNameSchema = z.string().regex(/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/, "Invalid execution policy name: must match [a-z0-9-] (3-63 chars; must start and end with [a-z0-9])").describe("Workspace-unique execution policy name embedded in the resource TRN");
|
|
5911
|
-
const ExecutionPolicyKeySchema = z.string().regex(/^[a-z0-9][a-z0-9_:.-]{0,62}[a-z0-9*]$/, "Invalid execution policy key: must match [a-z0-9_:.-] (2-64 chars; must start with [a-z0-9] and end with [a-z0-9] or a trailing '*')").describe("Execution policy key passed to triggerJobFunction's executionPolicyKey option");
|
|
4671
|
+
const ExecutionPolicyKeySchema = z.string().regex(/^[a-z0-9][a-z0-9_:.-]{0,62}[a-z0-9*]$/, "Invalid execution policy key: must match [a-z0-9_:.-] (2-64 chars; must start with [a-z0-9] and end with [a-z0-9] or a trailing '*')").describe("Execution policy key passed to startJobFunction's (or its frozen alias triggerJobFunction's) executionPolicyKey option");
|
|
5912
4672
|
const WorkflowJobFunctionExecutionPolicySchema = z.strictObject({
|
|
5913
4673
|
name: ExecutionPolicyNameSchema,
|
|
5914
4674
|
key: ExecutionPolicyKeySchema,
|
|
@@ -6665,5 +5425,5 @@ async function loadApplication(params) {
|
|
|
6665
5425
|
}
|
|
6666
5426
|
|
|
6667
5427
|
//#endregion
|
|
6668
|
-
export {
|
|
6669
|
-
//# sourceMappingURL=application-
|
|
5428
|
+
export { fetchAllTolerant as A, initOperatorClient as B, createBundleCache as C, closeConnectionPool as D, hashFile as E, getConsoleBaseUrl as F, byName as G, normalizeBaseUrl as H, getOAuth2ClientId as I, LOG_LEVELS as K, getOrNull as L, fetchPaged as M, fetchPlatformMachineUserToken as N, defaultPlatformBaseUrl as O, fetchUserInfo as P, getPlatformBaseUrl as R, hasGenerationHooks as S, hashContent as T, rememberPlatformConfigForToken as U, isDefaultPlatform as V, resolveStaticWebsiteUrls as W, platformBundleDefinePlugin as _, resolveInlineSourcemap as a, resolveBundleLogLevel as b, WorkflowJobSchema as c, INVOKER_EXPR as d, buildExecutorArgsExpr as f, stringifyFunction as g, assertUniqueTailorDBTypeNamesWithExternal as h, resolverBundleKey as i, fetchMachineUserToken as j, fetchAll as k, ResolverSchema as l, assertUniqueLocalTailorDBTypeNames as m, generatePluginFilesIfNeeded as n, getApplicationAuthNamespace as o, buildResolverOperationHookExpr as p, loadApplication as r, WorkflowJobFunctionExecutionPolicySchema as s, defineApplication as t, HTTP_METHODS as u, composeFunctionTreeshakeOptions as v, getDistDir as w, getPluginGenerationDependencies as x, createLogLevelTreeshakeOptions as y, initOAuth2Client as z };
|
|
5429
|
+
//# sourceMappingURL=application-BJXRpQj5.mjs.map
|