@tailor-platform/sdk 2.27.0 → 2.29.0
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 +109 -0
- package/README.md +0 -8
- package/agent-skills/tailor/SKILL.md +4 -2
- package/dist/application-dalGHdrI.mjs +1 -0
- package/dist/application-rWYDAfNU.mjs +200 -0
- package/dist/application-rWYDAfNU.mjs.map +1 -0
- package/dist/cli/cache/bundle-cache.d.mts +1 -0
- package/dist/cli/commands/executor/get.d.mts +2 -1
- package/dist/cli/commands/executor/jobs.d.mts +2 -1
- package/dist/cli/commands/executor/trigger.d.mts +2 -1
- package/dist/cli/commands/function/get.d.mts +2 -1
- package/dist/cli/commands/function/list.d.mts +2 -1
- package/dist/cli/commands/organization/folder/create.d.mts +2 -1
- package/dist/cli/commands/organization/folder/delete.d.mts +2 -1
- package/dist/cli/commands/organization/folder/get.d.mts +2 -1
- package/dist/cli/commands/organization/folder/list.d.mts +2 -1
- package/dist/cli/commands/organization/folder/update.d.mts +2 -1
- package/dist/cli/commands/organization/get.d.mts +2 -1
- package/dist/cli/commands/organization/tree.d.mts +2 -1
- package/dist/cli/commands/organization/update.d.mts +2 -1
- package/dist/cli/commands/show.d.mts +2 -1
- package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +2 -1
- package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +3 -1
- package/dist/cli/commands/workflow/executions.d.mts +2 -1
- package/dist/cli/commands/workflow/get.d.mts +2 -1
- package/dist/cli/commands/workflow/start.d.mts +2 -1
- package/dist/cli/commands/workspace/app/health.d.mts +2 -1
- package/dist/cli/commands/workspace/app/list.d.mts +2 -1
- package/dist/cli/commands/workspace/create.d.mts +5 -2
- package/dist/cli/commands/workspace/delete.d.mts +2 -1
- package/dist/cli/commands/workspace/get.d.mts +2 -1
- package/dist/cli/commands/workspace/restore.d.mts +2 -1
- package/dist/cli/commands/workspace/user/invite.d.mts +2 -1
- package/dist/cli/commands/workspace/user/list.d.mts +2 -1
- package/dist/cli/commands/workspace/user/remove.d.mts +2 -1
- package/dist/cli/commands/workspace/user/update.d.mts +2 -1
- package/dist/cli/lib.d.mts +1 -1
- package/dist/cli/lib.mjs +1 -1
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/main.d.mts +7 -0
- package/dist/cli/main.mjs +55 -42
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/query/index.d.mts +2 -1
- package/dist/cli/shared/args.d.mts +4 -2
- package/dist/cli/shared/client.d.mts +6 -1
- package/dist/cli/shared/context.d.mts +2 -1
- package/dist/cli/shared/logger.d.mts +5 -2
- package/dist/cli/shared/readonly-guard.d.mts +2 -1
- package/dist/cli/shared/seed-context.d.mts +5 -0
- package/dist/cli/shared/type-generator.d.mts +2 -1
- package/dist/cli/ts-hook.mjs +15 -11
- package/dist/completion/zsh-worker.zsh +275 -19
- package/dist/configure/config/types.d.mts +80 -1
- package/dist/configure/services/aigateway/types.d.mts +2 -1
- package/dist/configure/services/auth/types.d.mts +2 -1
- package/dist/configure/services/executor/executor.d.mts +2 -1
- package/dist/configure/services/executor/operation.d.mts +2 -1
- package/dist/configure/services/executor/trigger/event.d.mts +2 -1
- package/dist/configure/services/executor/trigger/schedule.d.mts +2 -1
- package/dist/configure/services/http-adapter/http-adapter.d.mts +2 -1
- package/dist/configure/services/idp/permission.d.mts +2 -1
- package/dist/configure/services/idp/types.d.mts +2 -1
- package/dist/configure/services/resolver/permission.d.mts +2 -1
- package/dist/configure/services/resolver/resolver.d.mts +2 -1
- package/dist/configure/services/secrets/types.d.mts +2 -1
- package/dist/configure/services/staticwebsite/types.d.mts +2 -1
- package/dist/configure/services/tailordb/permission.d.mts +2 -1
- package/dist/configure/services/tailordb/schema.d.mts +4 -3
- package/dist/configure/services/tailordb/types.d.mts +12 -1
- package/dist/configure/services/workflow/execution-policy.types.d.mts +2 -1
- package/dist/configure/services/workflow/job.d.mts +2 -1
- package/dist/configure/services/workflow/wait-point.d.mts +2 -1
- package/dist/configure/types/field.types.d.mts +2 -1
- package/dist/configure/types/permission-operand.types.d.mts +2 -1
- package/dist/configure/types/type.d.mts +2 -1
- package/dist/{crashreport-DFpRn-LS.mjs → crashreport-BAG8c7hB.mjs} +4 -4
- package/dist/{crashreport-DFpRn-LS.mjs.map → crashreport-BAG8c7hB.mjs.map} +1 -1
- package/dist/crashreport-BcXuocwI.mjs +1 -0
- package/dist/{errors-CpvSChyL.mjs → errors-D3qpNHR6.mjs} +2 -2
- package/dist/{errors-CpvSChyL.mjs.map → errors-D3qpNHR6.mjs.map} +1 -1
- package/dist/kysely-type-BWMfV301.mjs +48 -0
- package/dist/kysely-type-BWMfV301.mjs.map +1 -0
- package/dist/logger-DuaT2zY3.mjs +9 -0
- package/dist/logger-DuaT2zY3.mjs.map +1 -0
- package/dist/package-json-D1BRkjtr.mjs +1 -0
- package/dist/parser/service/tailordb/types.d.mts +3 -1
- package/dist/plugin/builtin/enum-constants/index.d.mts +2 -1
- package/dist/plugin/builtin/file-utils/index.d.mts +2 -1
- package/dist/plugin/builtin/kysely-type/index.d.mts +2 -1
- package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
- package/dist/plugin/builtin/seed/index.d.mts +11 -1
- package/dist/plugin/builtin/seed/index.mjs +1 -1
- package/dist/register-ts-hook-CkfKqhJT.mjs +1014 -0
- package/dist/register-ts-hook-CkfKqhJT.mjs.map +1 -0
- package/dist/runtime/aigateway.mjs +1 -1
- package/dist/runtime/authconnection.mjs +1 -1
- package/dist/runtime/context.d.mts +2 -1
- package/dist/runtime/context.mjs +1 -1
- package/dist/runtime/date.d.mts +2 -1
- package/dist/runtime/field-parse.d.mts +2 -1
- package/dist/runtime/file.d.mts +2 -1
- package/dist/runtime/file.mjs +1 -1
- package/dist/runtime/globals.d.mts +2 -1
- package/dist/runtime/iconv.mjs +1 -1
- package/dist/runtime/idp.mjs +1 -1
- package/dist/runtime/logger.mjs +1 -1
- package/dist/runtime/secretmanager.mjs +1 -1
- package/dist/runtime/types.d.mts +2 -1
- package/dist/runtime/workflow.d.mts +2 -1
- package/dist/runtime/workflow.mjs +1 -1
- package/dist/schema-B6hJQWp6.mjs.map +1 -1
- package/dist/seed/index.d.mts +17 -4
- package/dist/seed/index.mjs +6 -6
- package/dist/seed/index.mjs.map +1 -1
- package/dist/seed-C3P_T_Eh.mjs.map +1 -1
- package/dist/{service-Cc7OY0Oh.mjs → service-CETr0AKY.mjs} +3 -3
- package/dist/service-CETr0AKY.mjs.map +1 -0
- package/dist/service-DREWLYxD.mjs +1 -0
- package/dist/{service-DFmt9UJP.mjs → service-b1g22NN9.mjs} +2 -2
- package/dist/{service-DFmt9UJP.mjs.map → service-b1g22NN9.mjs.map} +1 -1
- package/dist/service_pb-Bbu2QSIr.mjs +2 -0
- package/dist/service_pb-Bbu2QSIr.mjs.map +1 -0
- package/dist/service_pb-urxAIYHT.mjs +1 -0
- package/dist/tailor-proto/src/tailor/v1/events_pb.d.mts +53 -1
- package/dist/tailor-proto/src/tailor/v1/events_resource_pb.d.mts +33 -0
- package/dist/tailor-proto/src/tailor/v1/function_resource_pb.d.mts +0 -8
- package/dist/tailor-proto/src/tailor/v1/service_pb.d.mts +18 -1
- package/dist/tailor-proto/src/tailor/v1/tailordb_resource_pb.d.mts +4 -0
- package/dist/telemetry-DpkFGBmu.mjs +1 -0
- package/dist/{telemetry-Bklv9kQY.mjs → telemetry-Xk7J829P.mjs} +2 -2
- package/dist/{telemetry-Bklv9kQY.mjs.map → telemetry-Xk7J829P.mjs.map} +1 -1
- package/dist/types/tailordb.generated.d.mts +2 -0
- package/dist/vitest/index.mjs +1 -1
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mock-kysely.d.mts +2 -1
- package/dist/vitest/mocks/aigateway.d.mts +2 -1
- package/dist/vitest/mocks/authconnection.d.mts +2 -1
- package/dist/vitest/mocks/file.d.mts +2 -1
- package/dist/vitest/mocks/iconv.d.mts +2 -1
- package/dist/vitest/mocks/idp.d.mts +2 -1
- package/dist/vitest/mocks/logger.d.mts +2 -1
- package/dist/vitest/mocks/secretmanager.d.mts +2 -1
- package/dist/vitest/mocks/tailordb-pglite.d.mts +2 -1
- package/dist/vitest/mocks/tailordb.d.mts +2 -1
- package/dist/vitest/mocks/workflow.d.mts +3 -1
- package/dist/vitest/pglite-kysely.d.mts +2 -1
- package/dist/vitest/workflow-local.d.mts +2 -1
- package/dist/{workspace_resource_pb-ChWejcj0.mjs → workspace_resource_pb-CaoM2npP.mjs} +2 -2
- package/dist/{workspace_resource_pb-ChWejcj0.mjs.map → workspace_resource_pb-CaoM2npP.mjs.map} +1 -1
- package/docs/cli/application.md +21 -3
- package/docs/cli/data.md +105 -0
- package/docs/cli/secret.md +6 -6
- package/docs/cli/tailordb.md +52 -16
- package/docs/cli/user.md +37 -7
- package/docs/cli/workspace.md +9 -1
- package/docs/cli-reference.md +36 -3
- package/docs/configuration.md +62 -2
- package/docs/github-actions.md +179 -13
- package/docs/multi-environment.md +24 -0
- package/docs/plugin/custom.md +1 -1
- package/docs/services/idp.md +1 -1
- package/docs/services/resolver.md +1 -58
- package/docs/services/tailordb-migration.md +80 -12
- package/docs/services/tailordb.md +17 -4
- package/docs/services/workflow.md +2 -2
- package/docs/testing.md +3 -2
- package/package.json +22 -22
- package/dist/application-DFeuELRo.mjs +0 -200
- package/dist/application-DFeuELRo.mjs.map +0 -1
- package/dist/application-nT_4a5HO.mjs +0 -1
- package/dist/crashreport-CBaRJl5h.mjs +0 -1
- package/dist/kysely-type-RDX59E1j.mjs +0 -48
- package/dist/kysely-type-RDX59E1j.mjs.map +0 -1
- package/dist/logger-Db-YRTwK.mjs +0 -9
- package/dist/logger-Db-YRTwK.mjs.map +0 -1
- package/dist/package-json-CmqhDJ__.mjs +0 -1
- package/dist/register-ts-hook-D6KXrGrB.mjs +0 -1009
- package/dist/register-ts-hook-D6KXrGrB.mjs.map +0 -1
- package/dist/service-Cc7OY0Oh.mjs.map +0 -1
- package/dist/service-sH2MZ4_W.mjs +0 -1
- package/dist/service_pb-Dw2ilc1O.mjs +0 -2
- package/dist/service_pb-Dw2ilc1O.mjs.map +0 -1
- package/dist/service_pb-GDOqfwb6.mjs +0 -1
- package/dist/telemetry-DXHP3VVi.mjs +0 -1
|
@@ -17,6 +17,44 @@ export type LogLevelInput = LogLevel | (string & {});
|
|
|
17
17
|
* referencing in a later version.
|
|
18
18
|
*/
|
|
19
19
|
export type AllowedRuntimeGlobals = Record<string, true | string[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Switches that control how unused code is removed from bundled functions.
|
|
22
|
+
*
|
|
23
|
+
* Most of them ask whether something has side effects, so `true` keeps it and `false` lets
|
|
24
|
+
* it be removed when its result is unused. `annotations` is the exception: it asks whether
|
|
25
|
+
* to honor annotations, so `true` removes more. Each switch overrides the value the SDK
|
|
26
|
+
* uses by default.
|
|
27
|
+
*/
|
|
28
|
+
export interface TreeshakeOptions {
|
|
29
|
+
/**
|
|
30
|
+
* `true` assumes that importing a module may have effects beyond its exports, so the
|
|
31
|
+
* module is kept even when all of its exports are unused; `false` leaves such a module
|
|
32
|
+
* out of the bundle.
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
moduleSideEffects?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* `true` honors `@__PURE__` and `@__NO_SIDE_EFFECTS__` comment annotations when deciding
|
|
38
|
+
* whether a call can be removed; `false` ignores them.
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
annotations?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* `true` treats reading a global the bundle does not define as having an effect, so the
|
|
44
|
+
* read is kept even when its result is unused; `false` lets it be removed.
|
|
45
|
+
* @default false
|
|
46
|
+
*/
|
|
47
|
+
unknownGlobalSideEffects?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* `true` treats reading a property (`config.db.company`) as having an effect, so the
|
|
50
|
+
* read is kept even when its result is unused. `false` removes unused calls whose
|
|
51
|
+
* arguments only read properties, which keeps modules a function never reaches out of
|
|
52
|
+
* the bundle. It also removes a read that exists only for its effect, such as a getter
|
|
53
|
+
* with a side effect or a read that throws when the object is `undefined`.
|
|
54
|
+
* @default true
|
|
55
|
+
*/
|
|
56
|
+
propertyReadSideEffects?: boolean;
|
|
57
|
+
}
|
|
20
58
|
/** Options for how `defineConfig()` bundles functions. */
|
|
21
59
|
export interface BuildOptions {
|
|
22
60
|
/**
|
|
@@ -24,6 +62,18 @@ export interface BuildOptions {
|
|
|
24
62
|
* @default true
|
|
25
63
|
*/
|
|
26
64
|
inlineSourcemap?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Embed the source text of installed packages (everything under `node_modules`) in
|
|
67
|
+
* the inline sourcemap. By default only your own source text is embedded, which
|
|
68
|
+
* makes bundled functions much smaller. Set `true` to embed all installed packages,
|
|
69
|
+
* or list package names (such as `["kysely", "@scope/lib"]`) to embed only those.
|
|
70
|
+
* A listed package's own dependencies are not embedded unless they are listed too. Error positions still resolve to the
|
|
71
|
+
* original files and lines; `tailor function run` shows a code snippet for a frame
|
|
72
|
+
* inside an installed package only when that package's file exists locally.
|
|
73
|
+
* Only applies when `inlineSourcemap` is enabled.
|
|
74
|
+
* @default false
|
|
75
|
+
*/
|
|
76
|
+
inlineSourcemapDependencySources?: boolean | string[];
|
|
27
77
|
/**
|
|
28
78
|
* Controls which `console.*` and `logger.*` (from `@tailor-platform/sdk/runtime`)
|
|
29
79
|
* calls remain in bundled functions. `logger.setAttributes` has no severity and
|
|
@@ -46,6 +96,13 @@ export interface BuildOptions {
|
|
|
46
96
|
* }
|
|
47
97
|
*/
|
|
48
98
|
allowedRuntimeGlobals?: AllowedRuntimeGlobals;
|
|
99
|
+
/**
|
|
100
|
+
* Overrides how unused code is removed from bundled functions. Unset switches keep the
|
|
101
|
+
* SDK's defaults.
|
|
102
|
+
* @example
|
|
103
|
+
* treeshake: { propertyReadSideEffects: false }
|
|
104
|
+
*/
|
|
105
|
+
treeshake?: TreeshakeOptions;
|
|
49
106
|
}
|
|
50
107
|
/** Value an `env` entry resolves to at runtime. */
|
|
51
108
|
export type EnvValue = string | number | boolean;
|
|
@@ -208,5 +265,27 @@ export interface AppConfig<Auth extends AuthConfig = AuthConfig, Idp extends IdP
|
|
|
208
265
|
* @example defaultDateRepresentation: "temporal"
|
|
209
266
|
*/
|
|
210
267
|
defaultDateRepresentation?: "temporal" | "date";
|
|
268
|
+
/**
|
|
269
|
+
* Whether `deploy` puts the TailorDB namespaces that have pending migrations
|
|
270
|
+
* into maintenance mode while it applies them. In maintenance mode, the
|
|
271
|
+
* generated GraphQL create, update, delete, and bulk upsert operations of
|
|
272
|
+
* every table in those namespaces are disabled and record events are not
|
|
273
|
+
* published; `read` keeps its setting.
|
|
274
|
+
*
|
|
275
|
+
* - `false`: no maintenance mode. While migrations run, the SDK does
|
|
276
|
+
* not guarantee what concurrent requests observe: tables can be in an
|
|
277
|
+
* intermediate schema, and record writes made by migration scripts publish
|
|
278
|
+
* events to the executors of the previous deploy.
|
|
279
|
+
* - `"migration"` (default): maintenance mode from the first pending migration
|
|
280
|
+
* until the last one completes.
|
|
281
|
+
* - `"deploy"`: maintenance mode from the first pending migration until the
|
|
282
|
+
* deploy has applied every other change, including resolvers, executors,
|
|
283
|
+
* and workflows.
|
|
284
|
+
*
|
|
285
|
+
* A deploy without pending migrations never enters maintenance mode.
|
|
286
|
+
* @example maintenanceMode: "migration"
|
|
287
|
+
*/
|
|
288
|
+
maintenanceMode?: false | "migration" | "deploy";
|
|
211
289
|
}
|
|
212
|
-
//#endregion
|
|
290
|
+
//#endregion
|
|
291
|
+
export {};
|
|
@@ -11,4 +11,5 @@ export type AIGatewayServiceInput = Omit<AIGatewayInput, "authNamespace"> & {
|
|
|
11
11
|
};
|
|
12
12
|
/** Type accepted by `AppConfig.aiGateways`. Only values returned by `defineAIGateway()` satisfy this. */
|
|
13
13
|
export type AIGatewayConfig = AIGatewayInput & AIGatewayDefinitionBrand;
|
|
14
|
-
//#endregion
|
|
14
|
+
//#endregion
|
|
15
|
+
export {};
|
|
@@ -169,4 +169,5 @@ export type AuthExternalConfig = {
|
|
|
169
169
|
type AuthServiceInputLoose = AuthServiceInput<any, any, any, string, any>;
|
|
170
170
|
export type AuthOwnConfig = DefinedAuth<string, AuthServiceInputLoose>;
|
|
171
171
|
export type AuthConfig = AuthOwnConfig | AuthExternalConfig;
|
|
172
|
-
//#endregion
|
|
172
|
+
//#endregion
|
|
173
|
+
export {};
|
|
@@ -46,4 +46,5 @@ export type WorkflowOperation<Args, W extends Workflow = Workflow> = Omit<Workfl
|
|
|
46
46
|
invoker?: MachineUserName;
|
|
47
47
|
} & WorkflowArgsProperty<Args, W>;
|
|
48
48
|
export type Operation<Args> = FunctionOperation<Args> | GqlOperation<Args> | WebhookOperation<Args> | WorkflowOperation<Args>;
|
|
49
|
-
//#endregion
|
|
49
|
+
//#endregion
|
|
50
|
+
export {};
|
|
@@ -513,4 +513,5 @@ type WorkflowJobExecutionTriggerOptions<K extends WorkflowJobExecutionEventKind[
|
|
|
513
513
|
* @returns Workflow job execution trigger
|
|
514
514
|
*/
|
|
515
515
|
export declare function workflowJobExecutionTrigger<const K extends [WorkflowJobExecutionEventKind, ...WorkflowJobExecutionEventKind[]]>(options: WorkflowJobExecutionTriggerOptions<K>): WorkflowJobExecutionTrigger<WorkflowJobExecutionMultiArgs<K>>;
|
|
516
|
-
//#endregion
|
|
516
|
+
//#endregion
|
|
517
|
+
export {};
|
|
@@ -120,4 +120,5 @@ export type HttpAdapter<Input extends HttpAdapterInput = HttpAdapterInput> = Omi
|
|
|
120
120
|
* });
|
|
121
121
|
*/
|
|
122
122
|
export declare function createHttpAdapter<const Input extends HttpAdapterInput>(config: HttpAdapter<Input>): HttpAdapter<Input>;
|
|
123
|
-
//#endregion
|
|
123
|
+
//#endregion
|
|
124
|
+
export {};
|
|
@@ -60,4 +60,5 @@ export type ResolverPermissionPolicy<User extends object = InferredAttributes> =
|
|
|
60
60
|
* const permission: ResolverPermission = "allowAnonymous";
|
|
61
61
|
*/
|
|
62
62
|
export type ResolverPermission<User extends object = InferredAttributes> = readonly ResolverPermissionPolicy<User>[] | "allowAnonymous";
|
|
63
|
-
//#endregion
|
|
63
|
+
//#endregion
|
|
64
|
+
export {};
|
|
@@ -84,4 +84,5 @@ export declare function createResolver<Input extends Record<string, TailorAnyFie
|
|
|
84
84
|
permission?: ResolverPermission;
|
|
85
85
|
}>): ResolverReturn<Input, Output>;
|
|
86
86
|
export type ResolverConfig = ReturnType<typeof createResolver<any, any>>;
|
|
87
|
-
//#endregion
|
|
87
|
+
//#endregion
|
|
88
|
+
export {};
|
|
@@ -6,4 +6,5 @@ export type StaticWebsiteDefinitionBrand = {
|
|
|
6
6
|
};
|
|
7
7
|
/** Type accepted by `AppConfig.staticWebsites`. Only values returned by `defineStaticWebSite()` satisfy this. */
|
|
8
8
|
export type StaticWebsiteConfig = StaticWebsiteInput & StaticWebsiteDefinitionBrand;
|
|
9
|
-
//#endregion
|
|
9
|
+
//#endregion
|
|
10
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { InferFieldsOutput, IsUnion, TypeLevelError, UnionToIntersection, output } from "../../../types/helpers.mjs";
|
|
2
2
|
import { FieldOptions, FieldOutput, FieldValidateInput, TailorFieldType, TailorToTs } from "../../types/field.types.mjs";
|
|
3
3
|
import { InferredAttributes } from "../../../runtime/types.mjs";
|
|
4
|
-
import { DBFieldMetadata, DefinedDBFieldMetadata, ExcludeDefaultedDBFields, ExcludeHookedDBFields, ExcludeNestedDBFields, Hook, IndexDef, RelationType, SerialConfig, TailorDBField as TailorDBField$1, TailorDBType as TailorDBType$1, TypeFeatures, TypeHook, TypeValidateFn, UpdateHookFn } from "./types.mjs";
|
|
4
|
+
import { DBFieldMetadata, DefinedDBFieldMetadata, ExcludeDefaultedDBFields, ExcludeHookedDBFields, ExcludeNestedDBFields, ExcludeSerialDBFields, Hook, IndexDef, RelationType, SerialConfig, TailorDBField as TailorDBField$1, TailorDBType as TailorDBType$1, TypeFeatures, TypeHook, TypeValidateFn, UpdateHookFn } from "./types.mjs";
|
|
5
5
|
import { AllowedValues, AllowedValuesOutput } from "../../types/field.mjs";
|
|
6
6
|
import { FieldParseArgs } from "../../../runtime/field-parse.mjs";
|
|
7
7
|
import { PluginConfigs, PluginFieldExtensions } from "../../../plugin/types.mjs";
|
|
@@ -426,7 +426,7 @@ declare function _enum<const V extends AllowedValues, const Opt extends FieldOpt
|
|
|
426
426
|
* @example db.object({ street: db.string(), city: db.string(), zip: db.string() })
|
|
427
427
|
* @example db.object({ name: db.string() }, { optional: true })
|
|
428
428
|
*/
|
|
429
|
-
declare function object<const F extends Record<string, TailorAnyDBField> & ExcludeNestedDBFields<F> & ExcludeHookedDBFields<F> & ExcludeDefaultedDBFields<F>, const Opt extends FieldOptions>(fields: F, options?: Opt): TailorDBField<{
|
|
429
|
+
declare function object<const F extends Record<string, TailorAnyDBField> & ExcludeNestedDBFields<F> & ExcludeHookedDBFields<F> & ExcludeDefaultedDBFields<F> & ExcludeSerialDBFields<F>, const Opt extends FieldOptions>(fields: F, options?: Opt): TailorDBField<{
|
|
430
430
|
type: "nested";
|
|
431
431
|
array: Opt extends {
|
|
432
432
|
array: true;
|
|
@@ -506,4 +506,5 @@ export declare const db: {
|
|
|
506
506
|
};
|
|
507
507
|
};
|
|
508
508
|
};
|
|
509
|
-
//#endregion
|
|
509
|
+
//#endregion
|
|
510
|
+
export {};
|
|
@@ -6,6 +6,11 @@ import { DBFieldMetadata as DBFieldMetadata$1, GqlOperationsInput, RawPermission
|
|
|
6
6
|
export type SerialConfig<T extends "string" | "integer" = "string" | "integer"> = Prettify<{
|
|
7
7
|
start: number;
|
|
8
8
|
maxValue?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Number records without gaps: a number is used only by a create that
|
|
11
|
+
* commits. Creates of the table that are numbered run one at a time.
|
|
12
|
+
*/
|
|
13
|
+
gapless?: boolean;
|
|
9
14
|
} & (T extends "string" ? {
|
|
10
15
|
format?: string;
|
|
11
16
|
} : object)>;
|
|
@@ -184,6 +189,11 @@ export type ExcludeDefaultedDBFields<T extends Record<string, TailorAnyDBField>>
|
|
|
184
189
|
array: boolean;
|
|
185
190
|
default: true;
|
|
186
191
|
}, any> ? never : T[K]; };
|
|
192
|
+
export type ExcludeSerialDBFields<T extends Record<string, TailorAnyDBField>> = { [K in keyof T]: T[K] extends TailorDBField<{
|
|
193
|
+
type: TailorFieldType;
|
|
194
|
+
array: boolean;
|
|
195
|
+
serial: true;
|
|
196
|
+
}, any> ? never : T[K]; };
|
|
187
197
|
export interface TypeFeatures {
|
|
188
198
|
/** Custom plural form of the table name for GraphQL */
|
|
189
199
|
pluralForm?: string;
|
|
@@ -223,4 +233,5 @@ type TailorDBExternalConfig = {
|
|
|
223
233
|
export type TailorDBServiceInput = {
|
|
224
234
|
[namespace: string]: TailorDBServiceConfigInput | TailorDBExternalConfig;
|
|
225
235
|
};
|
|
226
|
-
//#endregion
|
|
236
|
+
//#endregion
|
|
237
|
+
export {};
|
|
@@ -98,4 +98,5 @@ interface CreateWorkflowJobConfig<Name extends string, I, O> {
|
|
|
98
98
|
* });
|
|
99
99
|
*/
|
|
100
100
|
export declare function createWorkflowJob<const Name extends string, I = undefined, O = undefined>(config: CreateWorkflowJobConfig<Name, I, O>): WorkflowJob<Name, I, Awaited<O>>;
|
|
101
|
-
//#endregion
|
|
101
|
+
//#endregion
|
|
102
|
+
export {};
|
|
@@ -127,4 +127,5 @@ export declare function createWaitPoint<Payload = undefined, Result = undefined>
|
|
|
127
127
|
* // For 2-level access, use destructured export with JSDoc attached to the export itself.
|
|
128
128
|
*/
|
|
129
129
|
export declare function createWaitPoints<T extends Record<string, WaitPointInstance<any, any> | ParameterizedWaitPointInstance<any, any, any>>>(builder: (define: DefineFn) => T): T;
|
|
130
|
-
//#endregion
|
|
130
|
+
//#endregion
|
|
131
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as e,
|
|
1
|
+
import{a as e,o as t}from"./logger-DuaT2zY3.mjs";import{t as n}from"./package-json-C690ceex.mjs";import{n as r}from"./user-agent-vdHYF3QL.mjs";import{r as i,t as a}from"./secret-file-C9wp_FCX.mjs";import*as o from"pathe";import*as s from"node:fs";import{isCI as c}from"std-env";import{parseYAML as l}from"confbox";import{xdgConfig as u}from"xdg-basedir";import*as d from"node:crypto";import*as f from"node:os";function parseCrashReportConfig(){if(c)return{localEnabled:!1,remoteEnabled:!1,localDir:``};let e=(process.env.TAILOR_CRASH_REPORTS_LOCAL??`on`).toLowerCase()!==`off`,t=(process.env.TAILOR_CRASH_REPORTS_REMOTE??`off`).toLowerCase()===`on`,n=u?o.join(u,`tailor-platform`,`crash-reports`):``;return{localEnabled:e&&n!==``,remoteEnabled:t,localDir:n}}const p=`--- JSON ---`,m=`.crash.log`;function formatCrashReport(e){return[`Crash Report: ${e.id}`,`Timestamp: ${e.timestamp}`,`Error Type: ${e.errorType}`,``,`--- Environment ---`,`SDK Version: ${e.sdkVersion}`,`Node Version: ${e.nodeVersion}`,`OS: ${e.osPlatform} ${e.osRelease}`,`Arch: ${e.arch}`,``,`--- Command ---`,`Command: ${e.command}`,`Arguments: ${JSON.stringify(e.argv)}`,``,`--- Error ---`,`Name: ${e.errorName}`,`Message: ${e.errorMessage}`,``,`--- Stack Trace ---`,e.stackTrace||`(no stack trace available)`,``,p,JSON.stringify(e),``].join(`
|
|
2
2
|
`)}function generateFilename(e){return`${e.timestamp.replace(/[:.]/g,`-`)}-${e.id.slice(0,8)}${m}`}function cleanupOldFiles(e){try{let t=s.readdirSync(e).filter(e=>e.endsWith(m)).toSorted().toReversed();for(let n of t.slice(10))s.unlinkSync(o.join(e,n))}catch{}}function writeCrashReport(e,t){try{a(t);let n=generateFilename(e),r=o.join(t,n),s=formatCrashReport(e);return i(r,s),cleanupOldFiles(t),r}catch{return}}async function sendCrashReport(e,t){try{let n=process.env.TAILOR_CRASH_REPORT_ENDPOINT||`https://sdk-error-tracking-926vh9t4cl.erp.dev/query`,r=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`,"User-Agent":t},body:JSON.stringify({query:`
|
|
3
3
|
mutation SubmitCrashReport(
|
|
4
4
|
$id: String!
|
|
@@ -37,6 +37,6 @@ mutation SubmitCrashReport(
|
|
|
37
37
|
success
|
|
38
38
|
}
|
|
39
39
|
}`,variables:e}),signal:AbortSignal.timeout(5e3)});if(!r.ok)return!1;let i=await r.json();return!i.errors?.length&&i.data?.submitCrashReport.success===!0}catch{return!1}}const h=f.homedir(),g=/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi,_=/\b[0-9a-fA-F]{32,}\b/g,v=/\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/g,y=/(?:\/(?:[\w.@\- ]+\/)+[\w.@\- ]+)/g,b=/(?:[A-Za-z]:\\(?:[\w.@\- ]+\\)+[\w.@\- ]+)/g,x=/[?&][^?\s]*/g,S=/\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/,C=/^[A-Za-z]:\\/,w=`packages/sdk/`;function lastSegment(e,t){return e.split(t).pop()??e}function sanitizeStackTrace(e){let t=e.search(/\n\s+at /),n;return n=t===-1?sanitizeMessage(e):sanitizeMessage(e.slice(0,t))+e.slice(t),n=n.replace(y,e=>{let t=e.indexOf(w);return t===-1?e.startsWith(h)?`~/<redacted>/${lastSegment(e,`/`)}`:`<external>/${lastSegment(e,`/`)}`:e.slice(t)}),n=n.replace(b,e=>{let t=e.replace(/\\/g,`/`),n=t.indexOf(w);return n===-1?`<external>/${lastSegment(e,`\\`)}`:t.slice(n)}),n}function sanitizeMessage(e){let t=e;return t=t.replace(/\nRequest:\s*[\s\S]*$/,`
|
|
40
|
-
Request: <redacted>`),t=t.replace(g,`<uuid>`),t=t.replace(_,`<redacted>`),t=t.replace(v,`<email>`),t=t.replace(x,`?<redacted>`),t=t.replace(y,e=>`<path>/${lastSegment(e,`/`)}`),t=t.replace(b,e=>`<path>/${lastSegment(e,`\\`)}`),t}function sanitizeArgv(e){let t=[],n=!1;for(let r of e){if(n){if(!r.startsWith(`-`)){t.push(`<redacted>`),n=!1;continue}n=!1}if(r.startsWith(`-`)){let e=r.indexOf(`=`);if(e!==-1){t.push(`${r.slice(0,e)}=<redacted>`);continue}t.push(r),n=!0
|
|
41
|
-
`))}a.remoteEnabled&&await sendCrashReport(s,r(o))}catch{}}function initCrashReporting(){let
|
|
42
|
-
//# sourceMappingURL=crashreport-
|
|
40
|
+
Request: <redacted>`),t=t.replace(g,`<uuid>`),t=t.replace(_,`<redacted>`),t=t.replace(v,`<email>`),t=t.replace(x,`?<redacted>`),t=t.replace(y,e=>`<path>/${lastSegment(e,`/`)}`),t=t.replace(b,e=>`<path>/${lastSegment(e,`\\`)}`),t}function sanitizeArgv(e){let t=[],n=!1;for(let r of e){if(n){if(!r.startsWith(`-`)){t.push(`<redacted>`),n=!1;continue}n=!1}if(r.startsWith(`-`)){let e=r.indexOf(`=`);if(e!==-1){t.push(`${r.slice(0,e)}=<redacted>`);continue}t.push(r),n=!0}else r.startsWith(`/`)&&r.includes(`/`,1)||C.test(r)?t.push(`<path>`):S.test(r)?t.push(`<email>`):t.push(r)}return t}function parseCommand(){let e=process.argv.slice(2),t=[];for(let n of e){if(n.startsWith(`-`)||t.length>=3)break;t.push(n)}return t.join(` `)||`<unknown>`}function buildCrashReport(e){let{error:n,sdkVersion:r,errorType:i}=e,a=n instanceof Error,o=a?n.message:String(n),s=a&&n.stack?n.stack:``,c=a?n.name:`UnknownError`,l=readCurrentUser();return{id:d.randomUUID(),timestamp:(/* @__PURE__ */ new Date()).toISOString(),sdkVersion:r,nodeVersion:process.version,osPlatform:process.platform,osRelease:f.release(),arch:process.arch,command:sanitizeMessage(t(parseCommand())),argv:sanitizeArgv(process.argv.map(t)),errorName:t(c),errorMessage:sanitizeMessage(t(o)),stackTrace:sanitizeStackTrace(t(s)),errorType:i,userId:l?.id?t(l.id):null,userEmail:l?.email?t(l.email):null}}function readCurrentUser(){try{if(!u)return null;let e=o.join(u,`tailor-platform`,`config.yaml`);if(!s.existsSync(e))return null;let t=l(s.readFileSync(e,`utf-8`)),n=t?.current_user??null;if(!n)return null;let r=t.users?.[n]?.email;return{id:n,email:typeof r==`string`?r:legacyEmail(n)}}catch{return null}}function legacyEmail(e){return e.includes(`@`)?e:null}async function reportCrash(t,i){try{let a=parseCrashReportConfig();if(!a.localEnabled&&!a.remoteEnabled)return;let o=(await n()).version??`unknown`,s=buildCrashReport({error:t,sdkVersion:o,errorType:i});if(a.localEnabled){let t=writeCrashReport(s,a.localDir);t&&e.log([``,`An unexpected error occurred. A crash report has been saved to:`,` ${t}`,``,`To submit this report:`,` tailor crashreport send --file "${t}"`].join(`
|
|
41
|
+
`))}a.remoteEnabled&&await sendCrashReport(s,r(o))}catch{}}function initCrashReporting(){let t=parseCrashReportConfig();if(!t.localEnabled&&!t.remoteEnabled)return;let handleFatal=(t,n)=>{let r=t instanceof Error?t.message:String(t);e.error(r),reportCrash(t,n).finally(()=>{process.exit(1)})};process.on(`uncaughtException`,e=>handleFatal(e,`uncaughtException`)),process.on(`unhandledRejection`,e=>handleFatal(e,`unhandledRejection`))}export{p as a,m as i,reportCrash as n,parseCrashReportConfig as o,sendCrashReport as r,initCrashReporting as t};
|
|
42
|
+
//# sourceMappingURL=crashreport-BAG8c7hB.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crashreport-DFpRn-LS.mjs","names":["redactSecrets","logger","readPackageJson","userAgentFromVersion","writeSecretFile","ensureSecretDir","path","fs","crypto","os","isCI","localEnabled","remoteEnabled","localDir","xdgConfig","JSON_FOOTER_MARKER","CRASH_LOG_EXTENSION","report","files","dir","f","file","filename","filePath","content","endpoint","response","ua","data","HOME_DIR","UUID_PATTERN","LONG_HEX_PATTERN","EMAIL_PATTERN","ABSOLUTE_PATH_PATTERN","WINDOWS_PATH_PATTERN","URL_QUERY_PATTERN","EMAIL_TEST_PATTERN","WINDOWS_DRIVE_TEST_PATTERN","SDK_PACKAGE_MARKER","separator","firstFrameIndex","stack","result","sdkIndex","match","normalized","message","redactNext","arg","argv","eqIndex","args","commandParts","options","isError","error","rawMessage","rawStack","errorName","currentUser","configPath","raw","parseYAML","email","user","config","sdkVersion","errorType","reason"],"sources":["../src/cli/crashreport/config.ts","../src/cli/crashreport/writer.ts","../src/cli/crashreport/sender.ts","../src/cli/crashreport/sanitize.ts","../src/cli/crashreport/report.ts","../src/cli/crashreport/index.ts"],"sourcesContent":["import * as path from \"pathe\";\nimport { isCI } from \"std-env\";\nimport { xdgConfig } from \"xdg-basedir\";\n\nexport interface CrashReportConfig {\n readonly localEnabled: boolean;\n readonly remoteEnabled: boolean;\n readonly localDir: string;\n}\n\n/**\n * Parse crash report configuration from environment variables.\n * Local crash log writing is enabled by default (opt-out via TAILOR_CRASH_REPORTS_LOCAL=off).\n * Remote sending is disabled by default (opt-in via TAILOR_CRASH_REPORTS_REMOTE=on).\n * Both are auto-disabled in CI environments.\n * @returns Crash report configuration\n */\nexport function parseCrashReportConfig(): CrashReportConfig {\n if (isCI) {\n return {\n localEnabled: false,\n remoteEnabled: false,\n localDir: \"\",\n };\n }\n\n const localEnabled = (process.env.TAILOR_CRASH_REPORTS_LOCAL ?? \"on\").toLowerCase() !== \"off\";\n const remoteEnabled = (process.env.TAILOR_CRASH_REPORTS_REMOTE ?? \"off\").toLowerCase() === \"on\";\n const localDir = xdgConfig ? path.join(xdgConfig, \"tailor-platform\", \"crash-reports\") : \"\";\n\n return {\n localEnabled: localEnabled && localDir !== \"\",\n remoteEnabled,\n localDir,\n };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"pathe\";\nimport { ensureSecretDir, writeSecretFile } from \"#/cli/shared/secret-file\";\nimport type { CrashReport } from \"./report\";\n\nconst MAX_CRASH_FILES = 10;\n\n/** Marker line that separates human-readable content from the JSON footer. */\nexport const JSON_FOOTER_MARKER = \"--- JSON ---\";\n\n/** File extension for crash log files. */\nexport const CRASH_LOG_EXTENSION = \".crash.log\";\n\n/**\n * Format a CrashReport as human-readable text for local crash log files.\n * @param report - Crash report to format\n * @returns Formatted text content\n */\nexport function formatCrashReport(report: CrashReport): string {\n const lines = [\n `Crash Report: ${report.id}`,\n `Timestamp: ${report.timestamp}`,\n `Error Type: ${report.errorType}`,\n \"\",\n \"--- Environment ---\",\n `SDK Version: ${report.sdkVersion}`,\n `Node Version: ${report.nodeVersion}`,\n `OS: ${report.osPlatform} ${report.osRelease}`,\n `Arch: ${report.arch}`,\n \"\",\n \"--- Command ---\",\n `Command: ${report.command}`,\n `Arguments: ${JSON.stringify(report.argv)}`,\n \"\",\n \"--- Error ---\",\n `Name: ${report.errorName}`,\n `Message: ${report.errorMessage}`,\n \"\",\n \"--- Stack Trace ---\",\n report.stackTrace || \"(no stack trace available)\",\n \"\",\n JSON_FOOTER_MARKER,\n JSON.stringify(report),\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate a filename for a crash log file.\n * Format: {timestamp}-{shortId}.crash.log\n * @param report - Crash report to generate filename for\n * @returns Filename string\n */\nfunction generateFilename(report: CrashReport): string {\n const safeTimestamp = report.timestamp.replace(/[:.]/g, \"-\");\n const shortId = report.id.slice(0, 8);\n return `${safeTimestamp}-${shortId}${CRASH_LOG_EXTENSION}`;\n}\n\n/**\n * Remove old crash log files, keeping only the most recent ones.\n * @param dir - Crash log directory\n */\nfunction cleanupOldFiles(dir: string): void {\n try {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(CRASH_LOG_EXTENSION))\n .toSorted()\n .toReversed();\n\n for (const file of files.slice(MAX_CRASH_FILES)) {\n fs.unlinkSync(path.join(dir, file));\n }\n } catch {\n // Best-effort cleanup, ignore errors\n }\n}\n\n/**\n * Write a crash report to a local file.\n * Creates the directory if it doesn't exist. Keeps only the last 10 crash files.\n * Never throws - returns the file path on success or undefined on failure.\n * @param report - Crash report to write\n * @param dir - Directory to write the crash log file to\n * @returns File path on success, undefined on failure\n */\nexport function writeCrashReport(report: CrashReport, dir: string): string | undefined {\n try {\n ensureSecretDir(dir);\n\n const filename = generateFilename(report);\n const filePath = path.join(dir, filename);\n const content = formatCrashReport(report);\n\n writeSecretFile(filePath, content);\n cleanupOldFiles(dir);\n\n return filePath;\n } catch {\n return undefined;\n }\n}\n","import type { CrashReport } from \"./report\";\n\nconst SEND_TIMEOUT_MS = 5000;\nconst PRODUCTION_ENDPOINT = \"https://sdk-error-tracking-926vh9t4cl.erp.dev/query\";\n\nconst SUBMIT_MUTATION = `\nmutation SubmitCrashReport(\n $id: String!\n $timestamp: String!\n $sdkVersion: String!\n $nodeVersion: String!\n $osPlatform: String!\n $osRelease: String!\n $arch: String!\n $command: String!\n $argv: [String]\n $errorName: String!\n $errorMessage: String!\n $stackTrace: String\n $errorType: String!\n $userId: String\n $userEmail: String\n) {\n submitCrashReport(\n id: $id\n timestamp: $timestamp\n sdkVersion: $sdkVersion\n nodeVersion: $nodeVersion\n osPlatform: $osPlatform\n osRelease: $osRelease\n arch: $arch\n command: $command\n argv: $argv\n errorName: $errorName\n errorMessage: $errorMessage\n stackTrace: $stackTrace\n errorType: $errorType\n userId: $userId\n userEmail: $userEmail\n ) {\n success\n }\n}`;\n\n/**\n * Send a crash report to the remote endpoint via GraphQL mutation.\n * Best-effort: never throws, returns boolean success.\n * @param report - Crash report to send\n * @param ua - User-Agent header value\n * @returns true if the request succeeded, false otherwise\n */\nexport async function sendCrashReport(report: CrashReport, ua: string): Promise<boolean> {\n try {\n const endpoint = process.env.TAILOR_CRASH_REPORT_ENDPOINT || PRODUCTION_ENDPOINT;\n const response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": ua,\n },\n body: JSON.stringify({\n query: SUBMIT_MUTATION,\n variables: report,\n }),\n signal: AbortSignal.timeout(SEND_TIMEOUT_MS),\n });\n\n if (!response.ok) return false;\n\n const data = (await response.json()) as {\n errors?: unknown[];\n data?: { submitCrashReport: { success: boolean } };\n };\n if (data.errors?.length) return false;\n return data.data?.submitCrashReport.success === true;\n } catch {\n return false;\n }\n}\n","import * as os from \"node:os\";\n\nconst HOME_DIR = os.homedir();\n\n// Patterns for sanitization (global variants for use with .replace())\nconst UUID_PATTERN = /\\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\b/gi;\nconst LONG_HEX_PATTERN = /\\b[0-9a-fA-F]{32,}\\b/g;\nconst EMAIL_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/g;\nconst ABSOLUTE_PATH_PATTERN = /(?:\\/(?:[\\w.@\\- ]+\\/)+[\\w.@\\- ]+)/g;\nconst WINDOWS_PATH_PATTERN = /(?:[A-Za-z]:\\\\(?:[\\w.@\\- ]+\\\\)+[\\w.@\\- ]+)/g;\nconst URL_QUERY_PATTERN = /[?&][^?\\s]*/g;\n\n// Non-global variants for single-match .test() calls (avoids lastIndex state issues)\nconst EMAIL_TEST_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/;\nconst WINDOWS_DRIVE_TEST_PATTERN = /^[A-Za-z]:\\\\/;\n\n// SDK package path marker for relative paths\nconst SDK_PACKAGE_MARKER = \"packages/sdk/\";\n\nfunction lastSegment(filePath: string, separator: string): string {\n return filePath.split(separator).pop() ?? filePath;\n}\n\n/**\n * Sanitize a stack trace by replacing absolute paths with relative SDK paths.\n * External paths are replaced with `<external>/filename.ext`.\n * Home directories are replaced with `~/<redacted>/`.\n * @param stack - Raw stack trace string\n * @returns Sanitized stack trace\n */\nexport function sanitizeStackTrace(stack: string): string {\n // V8 stack traces start with \"ErrorType: message\\n at ...\".\n // The error message may span multiple lines before the first \" at \" frame.\n // Apply message sanitization to all message lines so secrets embedded in\n // multiline error messages are redacted consistently with errorMessage.\n const firstFrameIndex = stack.search(/\\n\\s+at /);\n let result: string;\n if (firstFrameIndex !== -1) {\n result = sanitizeMessage(stack.slice(0, firstFrameIndex)) + stack.slice(firstFrameIndex);\n } else {\n result = sanitizeMessage(stack);\n }\n\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => {\n const sdkIndex = match.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return match.slice(sdkIndex);\n }\n\n if (match.startsWith(HOME_DIR)) {\n return `~/<redacted>/${lastSegment(match, \"/\")}`;\n }\n\n return `<external>/${lastSegment(match, \"/\")}`;\n });\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => {\n const normalized = match.replace(/\\\\/g, \"/\");\n const sdkIndex = normalized.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return normalized.slice(sdkIndex);\n }\n return `<external>/${lastSegment(match, \"\\\\\")}`;\n });\n return result;\n}\n\n/**\n * Sanitize an error message by redacting sensitive information.\n * Redacts: UUIDs, long hex tokens, email addresses, absolute paths, URL query strings.\n * @param message - Raw error message\n * @returns Sanitized error message\n */\nexport function sanitizeMessage(message: string): string {\n let result = message;\n // Strip serialized request/response bodies that may contain secrets\n result = result.replace(/\\nRequest:\\s*[\\s\\S]*$/, \"\\nRequest: <redacted>\");\n result = result.replace(UUID_PATTERN, \"<uuid>\");\n result = result.replace(LONG_HEX_PATTERN, \"<redacted>\");\n result = result.replace(EMAIL_PATTERN, \"<email>\");\n result = result.replace(URL_QUERY_PATTERN, \"?<redacted>\");\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"/\")}`);\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"\\\\\")}`);\n\n return result;\n}\n\n/**\n * Sanitize process.argv by keeping command/subcommand names and redacting\n * values of sensitive flags.\n * @param argv - Raw process.argv array\n * @returns Sanitized argv array\n */\nexport function sanitizeArgv(argv: string[]): string[] {\n const result: string[] = [];\n let redactNext = false;\n\n for (const arg of argv) {\n if (redactNext) {\n // If the next token is itself a flag, treat it as a new flag rather\n // than consuming it as the previous flag's value. This avoids leaking\n // the *next* flag's value (e.g., `--verbose --workspace-id secret`\n // would otherwise expose `secret`).\n if (!arg.startsWith(\"-\")) {\n result.push(\"<redacted>\");\n redactNext = false;\n continue;\n }\n redactNext = false;\n }\n\n if (arg.startsWith(\"-\")) {\n // --flag=value: keep flag name, redact value\n const eqIndex = arg.indexOf(\"=\");\n if (eqIndex !== -1) {\n result.push(`${arg.slice(0, eqIndex)}=<redacted>`);\n continue;\n }\n\n // --flag / -f: keep flag name, redact next arg as its value\n result.push(arg);\n redactNext = true;\n continue;\n }\n\n // Redact absolute paths\n if (arg.startsWith(\"/\") && arg.includes(\"/\", 1)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact Windows-style absolute paths\n if (WINDOWS_DRIVE_TEST_PATTERN.test(arg)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact email addresses\n if (EMAIL_TEST_PATTERN.test(arg)) {\n result.push(\"<email>\");\n continue;\n }\n\n result.push(arg);\n }\n\n return result;\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport { parseYAML } from \"confbox\";\nimport * as path from \"pathe\";\nimport { xdgConfig } from \"xdg-basedir\";\nimport { redactSecrets } from \"#/cli/shared/logger\";\nimport { sanitizeArgv, sanitizeMessage, sanitizeStackTrace } from \"./sanitize\";\n\nexport type ErrorType = \"uncaughtException\" | \"unhandledRejection\" | \"handledError\";\n\nexport interface CrashReport {\n id: string;\n timestamp: string;\n sdkVersion: string;\n nodeVersion: string;\n osPlatform: string;\n osRelease: string;\n arch: string;\n command: string;\n argv: string[];\n errorName: string;\n errorMessage: string;\n stackTrace: string;\n errorType: ErrorType;\n userId: string | null;\n userEmail: string | null;\n}\n\ninterface BuildCrashReportOptions {\n error: unknown;\n sdkVersion: string;\n errorType: ErrorType;\n}\n\n// Maximum subcommand depth to keep (e.g., \"tailordb migrate generate\" = 3 tokens).\n// Positional arguments beyond this are potentially sensitive user input.\n// Accepted trade-off: plain-text positional args that don't match known patterns\n// (UUIDs, hex tokens, emails, paths) pass through to `command` and `argv`.\n// Full redaction would require embedding the CLI command tree here, which is fragile.\nconst MAX_COMMAND_TOKENS = 3;\n\n/**\n * Parse the command name from process.argv.\n * Extracts up to MAX_COMMAND_TOKENS non-flag arguments after the script name.\n * @returns Parsed command string\n */\nfunction parseCommand(): string {\n const args = process.argv.slice(2);\n const commandParts: string[] = [];\n for (const arg of args) {\n if (arg.startsWith(\"-\") || commandParts.length >= MAX_COMMAND_TOKENS) break;\n commandParts.push(arg);\n }\n return commandParts.join(\" \") || \"<unknown>\";\n}\n\n/**\n * Build a CrashReport data structure from an error and context.\n * All sensitive data is sanitized before inclusion: `redactSecrets` masks every registered\n * secret first, then the pattern-based sanitizers below strip known-shape values (UUIDs,\n * long hex, emails, paths) from what's left. `redactSecrets` must run first — the path\n * sanitizer intentionally keeps a path's basename (e.g. `/home/user/.../key.json` becomes\n * `<path>/key.json`), so a registered secret shaped like a path would have that basename\n * survive if the pattern sanitizer ran on it first. This report is written to a local file\n * (and optionally sent remotely) outside the CLI's normal stderr path.\n * @param options - Error, SDK version, and crash type\n * @returns Sanitized crash report\n */\nexport function buildCrashReport(options: BuildCrashReportOptions): CrashReport {\n const { error, sdkVersion, errorType } = options;\n\n const isError = error instanceof Error;\n const rawMessage = isError ? error.message : String(error);\n const rawStack = isError && error.stack ? error.stack : \"\";\n const errorName = isError ? error.name : \"UnknownError\";\n\n const currentUser = readCurrentUser();\n\n return {\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n sdkVersion,\n nodeVersion: process.version,\n osPlatform: process.platform,\n osRelease: os.release(),\n arch: process.arch,\n command: sanitizeMessage(redactSecrets(parseCommand())),\n argv: sanitizeArgv(process.argv.map(redactSecrets)),\n errorName: redactSecrets(errorName),\n errorMessage: sanitizeMessage(redactSecrets(rawMessage)),\n stackTrace: sanitizeStackTrace(redactSecrets(rawStack)),\n errorType,\n userId: currentUser?.id ? redactSecrets(currentUser.id) : null,\n userEmail: currentUser?.email ? redactSecrets(currentUser.email) : null,\n };\n}\n\ntype CurrentUser = {\n id: string;\n email: string | null;\n};\n\n/**\n * Read current_user from Tailor Platform config without side effects.\n * Unlike readPlatformConfig(), this never triggers migration or logs warnings.\n * @returns The current user ID and email, or null if unavailable\n */\nfunction readCurrentUser(): CurrentUser | null {\n try {\n if (!xdgConfig) return null;\n const configPath = path.join(xdgConfig, \"tailor-platform\", \"config.yaml\");\n if (!fs.existsSync(configPath)) return null;\n const raw = parseYAML(fs.readFileSync(configPath, \"utf-8\")) as {\n current_user?: string | null;\n users?: Record<string, { email?: unknown } | undefined>;\n };\n // parseYAML returns null for empty documents\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n const currentUser = raw?.current_user ?? null;\n if (!currentUser) return null;\n const email = raw.users?.[currentUser]?.email;\n return {\n id: currentUser,\n email: typeof email === \"string\" ? email : legacyEmail(currentUser),\n };\n } catch {\n return null;\n }\n}\n\nfunction legacyEmail(user: string): string | null {\n return user.includes(\"@\") ? user : null;\n}\n","import { logger } from \"#/cli/shared/logger\";\nimport { readPackageJson } from \"#/cli/shared/package-json\";\nimport { userAgentFromVersion } from \"#/cli/shared/user-agent\";\nimport { parseCrashReportConfig } from \"./config\";\nimport { buildCrashReport, type ErrorType } from \"./report\";\nimport { sendCrashReport } from \"./sender\";\nimport { writeCrashReport } from \"./writer\";\n\n/**\n * Report an unexpected crash. Writes a local crash log file and optionally\n * sends the report to a remote endpoint. Displays a user-facing message\n * with the crash log path and a command to submit the report.\n *\n * Never throws - all errors are silently caught.\n * @param error - The error that caused the crash\n * @param errorType - How the error was caught\n */\nexport async function reportCrash(error: unknown, errorType: ErrorType): Promise<void> {\n try {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const packageJson = await readPackageJson();\n const sdkVersion = packageJson.version ?? \"unknown\";\n\n const report = buildCrashReport({ error, sdkVersion, errorType });\n\n if (config.localEnabled) {\n const filePath = writeCrashReport(report, config.localDir);\n if (filePath) {\n logger.log(\n [\n \"\",\n \"An unexpected error occurred. A crash report has been saved to:\",\n ` ${filePath}`,\n \"\",\n \"To submit this report:\",\n ` tailor crashreport send --file \"${filePath}\"`,\n ].join(\"\\n\"),\n );\n }\n }\n\n if (config.remoteEnabled) {\n const ua = userAgentFromVersion(sdkVersion);\n await sendCrashReport(report, ua);\n }\n } catch {\n // Never throw from crash reporting\n }\n}\n\n/**\n * Register global uncaughtException and unhandledRejection handlers.\n * These catch errors outside the normal cleanup flow (e.g., during\n * argument parsing). Should be called once at CLI startup before runMain.\n */\nexport function initCrashReporting(): void {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const handleFatal = (error: unknown, errorType: ErrorType) => {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(message);\n void reportCrash(error, errorType).finally(() => {\n process.exit(1);\n });\n };\n\n process.on(\"uncaughtException\", (error) => handleFatal(error, \"uncaughtException\"));\n process.on(\"unhandledRejection\", (reason) => handleFatal(reason, \"unhandledRejection\"));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"crashreport-BAG8c7hB.mjs","names":["logger","redactSecrets","readPackageJson","userAgentFromVersion","writeSecretFile","ensureSecretDir","path","fs","crypto","os","isCI","localEnabled","remoteEnabled","localDir","xdgConfig","JSON_FOOTER_MARKER","CRASH_LOG_EXTENSION","report","files","dir","f","file","filename","filePath","content","endpoint","response","ua","data","HOME_DIR","UUID_PATTERN","LONG_HEX_PATTERN","EMAIL_PATTERN","ABSOLUTE_PATH_PATTERN","WINDOWS_PATH_PATTERN","URL_QUERY_PATTERN","EMAIL_TEST_PATTERN","WINDOWS_DRIVE_TEST_PATTERN","SDK_PACKAGE_MARKER","separator","firstFrameIndex","stack","result","sdkIndex","match","normalized","message","redactNext","arg","argv","eqIndex","args","commandParts","options","isError","error","rawMessage","rawStack","errorName","currentUser","configPath","raw","parseYAML","email","user","config","sdkVersion","errorType","reason"],"sources":["../src/cli/crashreport/config.ts","../src/cli/crashreport/writer.ts","../src/cli/crashreport/sender.ts","../src/cli/crashreport/sanitize.ts","../src/cli/crashreport/report.ts","../src/cli/crashreport/index.ts"],"sourcesContent":["import * as path from \"pathe\";\nimport { isCI } from \"std-env\";\nimport { xdgConfig } from \"xdg-basedir\";\n\nexport interface CrashReportConfig {\n readonly localEnabled: boolean;\n readonly remoteEnabled: boolean;\n readonly localDir: string;\n}\n\n/**\n * Parse crash report configuration from environment variables.\n * Local crash log writing is enabled by default (opt-out via TAILOR_CRASH_REPORTS_LOCAL=off).\n * Remote sending is disabled by default (opt-in via TAILOR_CRASH_REPORTS_REMOTE=on).\n * Both are auto-disabled in CI environments.\n * @returns Crash report configuration\n */\nexport function parseCrashReportConfig(): CrashReportConfig {\n if (isCI) {\n return {\n localEnabled: false,\n remoteEnabled: false,\n localDir: \"\",\n };\n }\n\n const localEnabled = (process.env.TAILOR_CRASH_REPORTS_LOCAL ?? \"on\").toLowerCase() !== \"off\";\n const remoteEnabled = (process.env.TAILOR_CRASH_REPORTS_REMOTE ?? \"off\").toLowerCase() === \"on\";\n const localDir = xdgConfig ? path.join(xdgConfig, \"tailor-platform\", \"crash-reports\") : \"\";\n\n return {\n localEnabled: localEnabled && localDir !== \"\",\n remoteEnabled,\n localDir,\n };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"pathe\";\nimport { ensureSecretDir, writeSecretFile } from \"#/cli/shared/secret-file\";\nimport type { CrashReport } from \"./report\";\n\nconst MAX_CRASH_FILES = 10;\n\n/** Marker line that separates human-readable content from the JSON footer. */\nexport const JSON_FOOTER_MARKER = \"--- JSON ---\";\n\n/** File extension for crash log files. */\nexport const CRASH_LOG_EXTENSION = \".crash.log\";\n\n/**\n * Format a CrashReport as human-readable text for local crash log files.\n * @param report - Crash report to format\n * @returns Formatted text content\n */\nexport function formatCrashReport(report: CrashReport): string {\n const lines = [\n `Crash Report: ${report.id}`,\n `Timestamp: ${report.timestamp}`,\n `Error Type: ${report.errorType}`,\n \"\",\n \"--- Environment ---\",\n `SDK Version: ${report.sdkVersion}`,\n `Node Version: ${report.nodeVersion}`,\n `OS: ${report.osPlatform} ${report.osRelease}`,\n `Arch: ${report.arch}`,\n \"\",\n \"--- Command ---\",\n `Command: ${report.command}`,\n `Arguments: ${JSON.stringify(report.argv)}`,\n \"\",\n \"--- Error ---\",\n `Name: ${report.errorName}`,\n `Message: ${report.errorMessage}`,\n \"\",\n \"--- Stack Trace ---\",\n report.stackTrace || \"(no stack trace available)\",\n \"\",\n JSON_FOOTER_MARKER,\n JSON.stringify(report),\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate a filename for a crash log file.\n * Format: {timestamp}-{shortId}.crash.log\n * @param report - Crash report to generate filename for\n * @returns Filename string\n */\nfunction generateFilename(report: CrashReport): string {\n const safeTimestamp = report.timestamp.replace(/[:.]/g, \"-\");\n const shortId = report.id.slice(0, 8);\n return `${safeTimestamp}-${shortId}${CRASH_LOG_EXTENSION}`;\n}\n\n/**\n * Remove old crash log files, keeping only the most recent ones.\n * @param dir - Crash log directory\n */\nfunction cleanupOldFiles(dir: string): void {\n try {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(CRASH_LOG_EXTENSION))\n .toSorted()\n .toReversed();\n\n for (const file of files.slice(MAX_CRASH_FILES)) {\n fs.unlinkSync(path.join(dir, file));\n }\n } catch {\n // Best-effort cleanup, ignore errors\n }\n}\n\n/**\n * Write a crash report to a local file.\n * Creates the directory if it doesn't exist. Keeps only the last 10 crash files.\n * Never throws - returns the file path on success or undefined on failure.\n * @param report - Crash report to write\n * @param dir - Directory to write the crash log file to\n * @returns File path on success, undefined on failure\n */\nexport function writeCrashReport(report: CrashReport, dir: string): string | undefined {\n try {\n ensureSecretDir(dir);\n\n const filename = generateFilename(report);\n const filePath = path.join(dir, filename);\n const content = formatCrashReport(report);\n\n writeSecretFile(filePath, content);\n cleanupOldFiles(dir);\n\n return filePath;\n } catch {\n return undefined;\n }\n}\n","import type { CrashReport } from \"./report\";\n\nconst SEND_TIMEOUT_MS = 5000;\nconst PRODUCTION_ENDPOINT = \"https://sdk-error-tracking-926vh9t4cl.erp.dev/query\";\n\nconst SUBMIT_MUTATION = `\nmutation SubmitCrashReport(\n $id: String!\n $timestamp: String!\n $sdkVersion: String!\n $nodeVersion: String!\n $osPlatform: String!\n $osRelease: String!\n $arch: String!\n $command: String!\n $argv: [String]\n $errorName: String!\n $errorMessage: String!\n $stackTrace: String\n $errorType: String!\n $userId: String\n $userEmail: String\n) {\n submitCrashReport(\n id: $id\n timestamp: $timestamp\n sdkVersion: $sdkVersion\n nodeVersion: $nodeVersion\n osPlatform: $osPlatform\n osRelease: $osRelease\n arch: $arch\n command: $command\n argv: $argv\n errorName: $errorName\n errorMessage: $errorMessage\n stackTrace: $stackTrace\n errorType: $errorType\n userId: $userId\n userEmail: $userEmail\n ) {\n success\n }\n}`;\n\n/**\n * Send a crash report to the remote endpoint via GraphQL mutation.\n * Best-effort: never throws, returns boolean success.\n * @param report - Crash report to send\n * @param ua - User-Agent header value\n * @returns true if the request succeeded, false otherwise\n */\nexport async function sendCrashReport(report: CrashReport, ua: string): Promise<boolean> {\n try {\n const endpoint = process.env.TAILOR_CRASH_REPORT_ENDPOINT || PRODUCTION_ENDPOINT;\n const response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": ua,\n },\n body: JSON.stringify({\n query: SUBMIT_MUTATION,\n variables: report,\n }),\n signal: AbortSignal.timeout(SEND_TIMEOUT_MS),\n });\n\n if (!response.ok) return false;\n\n const data = (await response.json()) as {\n errors?: unknown[];\n data?: { submitCrashReport: { success: boolean } };\n };\n if (data.errors?.length) return false;\n return data.data?.submitCrashReport.success === true;\n } catch {\n return false;\n }\n}\n","import * as os from \"node:os\";\n\nconst HOME_DIR = os.homedir();\n\n// Patterns for sanitization (global variants for use with .replace())\nconst UUID_PATTERN = /\\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\b/gi;\nconst LONG_HEX_PATTERN = /\\b[0-9a-fA-F]{32,}\\b/g;\nconst EMAIL_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/g;\nconst ABSOLUTE_PATH_PATTERN = /(?:\\/(?:[\\w.@\\- ]+\\/)+[\\w.@\\- ]+)/g;\nconst WINDOWS_PATH_PATTERN = /(?:[A-Za-z]:\\\\(?:[\\w.@\\- ]+\\\\)+[\\w.@\\- ]+)/g;\nconst URL_QUERY_PATTERN = /[?&][^?\\s]*/g;\n\n// Non-global variants for single-match .test() calls (avoids lastIndex state issues)\nconst EMAIL_TEST_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/;\nconst WINDOWS_DRIVE_TEST_PATTERN = /^[A-Za-z]:\\\\/;\n\n// SDK package path marker for relative paths\nconst SDK_PACKAGE_MARKER = \"packages/sdk/\";\n\nfunction lastSegment(filePath: string, separator: string): string {\n return filePath.split(separator).pop() ?? filePath;\n}\n\n/**\n * Sanitize a stack trace by replacing absolute paths with relative SDK paths.\n * External paths are replaced with `<external>/filename.ext`.\n * Home directories are replaced with `~/<redacted>/`.\n * @param stack - Raw stack trace string\n * @returns Sanitized stack trace\n */\nexport function sanitizeStackTrace(stack: string): string {\n // V8 stack traces start with \"ErrorType: message\\n at ...\".\n // The error message may span multiple lines before the first \" at \" frame.\n // Apply message sanitization to all message lines so secrets embedded in\n // multiline error messages are redacted consistently with errorMessage.\n const firstFrameIndex = stack.search(/\\n\\s+at /);\n let result: string;\n if (firstFrameIndex !== -1) {\n result = sanitizeMessage(stack.slice(0, firstFrameIndex)) + stack.slice(firstFrameIndex);\n } else {\n result = sanitizeMessage(stack);\n }\n\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => {\n const sdkIndex = match.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return match.slice(sdkIndex);\n }\n\n if (match.startsWith(HOME_DIR)) {\n return `~/<redacted>/${lastSegment(match, \"/\")}`;\n }\n\n return `<external>/${lastSegment(match, \"/\")}`;\n });\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => {\n const normalized = match.replace(/\\\\/g, \"/\");\n const sdkIndex = normalized.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return normalized.slice(sdkIndex);\n }\n return `<external>/${lastSegment(match, \"\\\\\")}`;\n });\n return result;\n}\n\n/**\n * Sanitize an error message by redacting sensitive information.\n * Redacts: UUIDs, long hex tokens, email addresses, absolute paths, URL query strings.\n * @param message - Raw error message\n * @returns Sanitized error message\n */\nexport function sanitizeMessage(message: string): string {\n let result = message;\n // Strip serialized request/response bodies that may contain secrets\n result = result.replace(/\\nRequest:\\s*[\\s\\S]*$/, \"\\nRequest: <redacted>\");\n result = result.replace(UUID_PATTERN, \"<uuid>\");\n result = result.replace(LONG_HEX_PATTERN, \"<redacted>\");\n result = result.replace(EMAIL_PATTERN, \"<email>\");\n result = result.replace(URL_QUERY_PATTERN, \"?<redacted>\");\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"/\")}`);\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"\\\\\")}`);\n\n return result;\n}\n\n/**\n * Sanitize process.argv by keeping command/subcommand names and redacting\n * values of sensitive flags.\n * @param argv - Raw process.argv array\n * @returns Sanitized argv array\n */\nexport function sanitizeArgv(argv: string[]): string[] {\n const result: string[] = [];\n let redactNext = false;\n\n for (const arg of argv) {\n if (redactNext) {\n // If the next token is itself a flag, treat it as a new flag rather\n // than consuming it as the previous flag's value. This avoids leaking\n // the *next* flag's value (e.g., `--verbose --workspace-id secret`\n // would otherwise expose `secret`).\n if (!arg.startsWith(\"-\")) {\n result.push(\"<redacted>\");\n redactNext = false;\n continue;\n }\n redactNext = false;\n }\n\n if (arg.startsWith(\"-\")) {\n // --flag=value: keep flag name, redact value\n const eqIndex = arg.indexOf(\"=\");\n if (eqIndex !== -1) {\n result.push(`${arg.slice(0, eqIndex)}=<redacted>`);\n continue;\n }\n\n // --flag / -f: keep flag name, redact next arg as its value\n result.push(arg);\n redactNext = true;\n continue;\n }\n\n // Redact absolute paths\n if (arg.startsWith(\"/\") && arg.includes(\"/\", 1)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact Windows-style absolute paths\n if (WINDOWS_DRIVE_TEST_PATTERN.test(arg)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact email addresses\n if (EMAIL_TEST_PATTERN.test(arg)) {\n result.push(\"<email>\");\n continue;\n }\n\n result.push(arg);\n }\n\n return result;\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport { parseYAML } from \"confbox\";\nimport * as path from \"pathe\";\nimport { xdgConfig } from \"xdg-basedir\";\nimport { redactSecrets } from \"#/cli/shared/logger\";\nimport { sanitizeArgv, sanitizeMessage, sanitizeStackTrace } from \"./sanitize\";\n\nexport type ErrorType = \"uncaughtException\" | \"unhandledRejection\" | \"handledError\";\n\nexport interface CrashReport {\n id: string;\n timestamp: string;\n sdkVersion: string;\n nodeVersion: string;\n osPlatform: string;\n osRelease: string;\n arch: string;\n command: string;\n argv: string[];\n errorName: string;\n errorMessage: string;\n stackTrace: string;\n errorType: ErrorType;\n userId: string | null;\n userEmail: string | null;\n}\n\ninterface BuildCrashReportOptions {\n error: unknown;\n sdkVersion: string;\n errorType: ErrorType;\n}\n\n// Maximum subcommand depth to keep (e.g., \"tailordb migrate generate\" = 3 tokens).\n// Positional arguments beyond this are potentially sensitive user input.\n// Accepted trade-off: plain-text positional args that don't match known patterns\n// (UUIDs, hex tokens, emails, paths) pass through to `command` and `argv`.\n// Full redaction would require embedding the CLI command tree here, which is fragile.\nconst MAX_COMMAND_TOKENS = 3;\n\n/**\n * Parse the command name from process.argv.\n * Extracts up to MAX_COMMAND_TOKENS non-flag arguments after the script name.\n * @returns Parsed command string\n */\nfunction parseCommand(): string {\n const args = process.argv.slice(2);\n const commandParts: string[] = [];\n for (const arg of args) {\n if (arg.startsWith(\"-\") || commandParts.length >= MAX_COMMAND_TOKENS) break;\n commandParts.push(arg);\n }\n return commandParts.join(\" \") || \"<unknown>\";\n}\n\n/**\n * Build a CrashReport data structure from an error and context.\n * All sensitive data is sanitized before inclusion: `redactSecrets` masks every registered\n * secret first, then the pattern-based sanitizers below strip known-shape values (UUIDs,\n * long hex, emails, paths) from what's left. `redactSecrets` must run first — the path\n * sanitizer intentionally keeps a path's basename (e.g. `/home/user/.../key.json` becomes\n * `<path>/key.json`), so a registered secret shaped like a path would have that basename\n * survive if the pattern sanitizer ran on it first. This report is written to a local file\n * (and optionally sent remotely) outside the CLI's normal stderr path.\n * @param options - Error, SDK version, and crash type\n * @returns Sanitized crash report\n */\nexport function buildCrashReport(options: BuildCrashReportOptions): CrashReport {\n const { error, sdkVersion, errorType } = options;\n\n const isError = error instanceof Error;\n const rawMessage = isError ? error.message : String(error);\n const rawStack = isError && error.stack ? error.stack : \"\";\n const errorName = isError ? error.name : \"UnknownError\";\n\n const currentUser = readCurrentUser();\n\n return {\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n sdkVersion,\n nodeVersion: process.version,\n osPlatform: process.platform,\n osRelease: os.release(),\n arch: process.arch,\n command: sanitizeMessage(redactSecrets(parseCommand())),\n argv: sanitizeArgv(process.argv.map(redactSecrets)),\n errorName: redactSecrets(errorName),\n errorMessage: sanitizeMessage(redactSecrets(rawMessage)),\n stackTrace: sanitizeStackTrace(redactSecrets(rawStack)),\n errorType,\n userId: currentUser?.id ? redactSecrets(currentUser.id) : null,\n userEmail: currentUser?.email ? redactSecrets(currentUser.email) : null,\n };\n}\n\ntype CurrentUser = {\n id: string;\n email: string | null;\n};\n\n/**\n * Read current_user from Tailor Platform config without side effects.\n * Unlike readPlatformConfig(), this never triggers migration or logs warnings.\n * @returns The current user ID and email, or null if unavailable\n */\nfunction readCurrentUser(): CurrentUser | null {\n try {\n if (!xdgConfig) return null;\n const configPath = path.join(xdgConfig, \"tailor-platform\", \"config.yaml\");\n if (!fs.existsSync(configPath)) return null;\n const raw = parseYAML(fs.readFileSync(configPath, \"utf-8\")) as {\n current_user?: string | null;\n users?: Record<string, { email?: unknown } | undefined>;\n };\n // parseYAML returns null for empty documents\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n const currentUser = raw?.current_user ?? null;\n if (!currentUser) return null;\n const email = raw.users?.[currentUser]?.email;\n return {\n id: currentUser,\n email: typeof email === \"string\" ? email : legacyEmail(currentUser),\n };\n } catch {\n return null;\n }\n}\n\nfunction legacyEmail(user: string): string | null {\n return user.includes(\"@\") ? user : null;\n}\n","import { logger } from \"#/cli/shared/logger\";\nimport { readPackageJson } from \"#/cli/shared/package-json\";\nimport { userAgentFromVersion } from \"#/cli/shared/user-agent\";\nimport { parseCrashReportConfig } from \"./config\";\nimport { buildCrashReport, type ErrorType } from \"./report\";\nimport { sendCrashReport } from \"./sender\";\nimport { writeCrashReport } from \"./writer\";\n\n/**\n * Report an unexpected crash. Writes a local crash log file and optionally\n * sends the report to a remote endpoint. Displays a user-facing message\n * with the crash log path and a command to submit the report.\n *\n * Never throws - all errors are silently caught.\n * @param error - The error that caused the crash\n * @param errorType - How the error was caught\n */\nexport async function reportCrash(error: unknown, errorType: ErrorType): Promise<void> {\n try {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const packageJson = await readPackageJson();\n const sdkVersion = packageJson.version ?? \"unknown\";\n\n const report = buildCrashReport({ error, sdkVersion, errorType });\n\n if (config.localEnabled) {\n const filePath = writeCrashReport(report, config.localDir);\n if (filePath) {\n logger.log(\n [\n \"\",\n \"An unexpected error occurred. A crash report has been saved to:\",\n ` ${filePath}`,\n \"\",\n \"To submit this report:\",\n ` tailor crashreport send --file \"${filePath}\"`,\n ].join(\"\\n\"),\n );\n }\n }\n\n if (config.remoteEnabled) {\n const ua = userAgentFromVersion(sdkVersion);\n await sendCrashReport(report, ua);\n }\n } catch {\n // Never throw from crash reporting\n }\n}\n\n/**\n * Register global uncaughtException and unhandledRejection handlers.\n * These catch errors outside the normal cleanup flow (e.g., during\n * argument parsing). Should be called once at CLI startup before runMain.\n */\nexport function initCrashReporting(): void {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const handleFatal = (error: unknown, errorType: ErrorType) => {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(message);\n void reportCrash(error, errorType).finally(() => {\n process.exit(1);\n });\n };\n\n process.on(\"uncaughtException\", (error) => handleFatal(error, \"uncaughtException\"));\n process.on(\"unhandledRejection\", (reason) => handleFatal(reason, \"unhandledRejection\"));\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{n as reportCrash}from"./crashreport-BAG8c7hB.mjs";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{c as e}from"./logger-DuaT2zY3.mjs";const t=/^[A-Za-z0-9_./:=@+-]+$/,n=/^[A-Za-z0-9_./:=@+\\-]+$/,r=/[%$"`“”„\p{Cc}]|\\$|^~(?:[\\/]|$)/u,i=/"|\s.*\\$|^~(?:[\\/]|$)/;function quotePosixArg(e){return t.test(e)?e:`'${e.replaceAll(`'`,`'"'"'`)}'`}function isPowerShellBare(e){return n.test(e)&&!/^(@|[+-]?\.?\d)/.test(e)&&e!==`--`&&!/^-[^-].*[.:]/.test(e)}function quotePowerShellArg(e){return isPowerShellBare(e)?e:`'${e.replace(/['‘’‚‛]/g,`$&$&`)}'`}function quoteCmdArg(e){if(n.test(e)&&!e.endsWith(`\\`))return e;let t=`"`,r=0;for(let n of e)n===`\\`?r++:(t+=n===`"`?`${`\\`.repeat(r*2)}""`:`${`\\`.repeat(r)}${n===`%`?`%%cd:~,%`:n}`,r=0);return`${t}${`\\`.repeat(r*2)}"`}function formatWindowsCommandLines(e){if(e.every(e=>isPowerShellBare(e)||e!==``&&!r.test(e)))return{kind:`shared`,commandLine:e.map(e=>isPowerShellBare(e)?e:`"${e}"`).join(` `)};let t=e.map(quoteCmdArg).join(` `);return{kind:`perShell`,powershell:e.some(e=>e===``||i.test(e))?`cmd /d /s /c ${quotePowerShellArg(t)}`:e.map(quotePowerShellArg).join(` `),cmd:t}}function formatShellCommandLines(e){return process.platform===`win32`?formatWindowsCommandLines(e):{kind:`shared`,commandLine:e.map(quotePosixArg).join(` `)}}function formatCommandHint(e){let t=formatShellCommandLines([e.command,...e.args]);return t.kind===`shared`?`\`${t.commandLine}\``:`\`${t.powershell}\` in PowerShell or \`${t.cmd}\` in cmd.exe`}function formatError(t){let n=[e.error(`Error${t.code?` [${t.code}]`:``}: ${t.message}`)];return t.details&&n.push(`\n ${e.dim(`Details:`)} ${t.details.split(`
|
|
2
2
|
`).join(`
|
|
3
3
|
`)}`),t.suggestion&&n.push(`\n ${e.info(`Suggestion:`)} ${t.suggestion.split(`
|
|
4
4
|
`).join(`
|
|
5
5
|
`)}`),t.command&&n.push(`\n ${e.dim(`Help:`)} Run \`tailor ${t.command} --help\` for usage information.`),t.next&&n.push(`\n ${e.info(`Next:`)} Run ${formatCommandHint(t.next)}.`),n.join(``)}function CLIError(e){let t=Error(e.message,e.cause===void 0?void 0:{cause:e.cause});return t.name=`CLIError`,t.code=e.code,t.details=e.details,t.suggestion=e.suggestion,t.command=e.command,t.next=e.next,t.context=e.context,t.format=()=>formatError(t),t}function isCLIError(e){return e instanceof Error&&e.name===`CLIError`}function errorSummary(e){return isCLIError(e)?[e.message,e.details,e.suggestion].filter(Boolean).join(`
|
|
6
6
|
`):e instanceof Error?e.message:String(e)}function internalError(e,t){return Error(e,t)}function toError(e){return e instanceof Error?e:Error(String(e),{cause:e})}const a=/does not provide an export named '(?!default')([^']+)'/;function typeOnlyImportHint(e){if(!(e instanceof SyntaxError))return;let t=a.exec(e.message)?.[1];if(t)return`If '${t}' is a type, import it with \`import type\` (or the inline \`type\` modifier). The CLI runs TypeScript by stripping types from each file in isolation, so type-only exports do not exist at runtime. Set "verbatimModuleSyntax": true in tsconfig.json to catch this at typecheck.`}export{isCLIError as a,internalError as i,errorSummary as n,toError as o,formatCommandHint as r,typeOnlyImportHint as s,CLIError as t};
|
|
7
|
-
//# sourceMappingURL=errors-
|
|
7
|
+
//# sourceMappingURL=errors-D3qpNHR6.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors-CpvSChyL.mjs","names":["styles","POSIX_BARE_ARG","CMD_BARE_ARG","WINDOWS_DOUBLE_QUOTE_UNSAFE","POWERSHELL_VERSION_SENSITIVE","value","quoted","backslashes","char","argv","cmd","commandLines","action","parts","error","options","message","MISSING_NAMED_EXPORT_PATTERN","name"],"sources":["../src/cli/shared/shell-quote.ts","../src/cli/shared/errors.ts"],"sourcesContent":["/**\n * Command lines for a suggested command: one line when every supported shell reads it the same\n * way, otherwise one line per Windows shell.\n */\nexport type ShellCommandLines =\n | { kind: \"shared\"; commandLine: string }\n | { kind: \"perShell\"; powershell: string; cmd: string };\n\nconst POSIX_BARE_ARG = /^[A-Za-z0-9_./:=@+-]+$/;\nconst CMD_BARE_ARG = /^[A-Za-z0-9_./:=@+\\\\-]+$/;\n// PowerShell expands `$` and backtick escapes inside double quotes and reads `“ ” „` as `\"`,\n// cmd.exe expands `%` even inside quotes, a `\"` or a trailing backslash changes where the\n// quoted argument ends, and a leading `~` is version-sensitive (below).\nconst WINDOWS_DOUBLE_QUOTE_UNSAFE = /[%$\"`“”„\\p{Cc}]|\\\\$|^~(?:[\\\\/]|$)/u;\n// PowerShell versions pass these to programs differently: Windows PowerShell 5.1 and 7.3+\n// disagree on quotes and trailing backslashes, and 7.6+ expands a leading `~` inside the `.ps1`\n// shim however it was quoted.\nconst POWERSHELL_VERSION_SENSITIVE = /\"|\\s.*\\\\$|^~(?:[\\\\/]|$)/;\n\nfunction quotePosixArg(value: string): string {\n if (POSIX_BARE_ARG.test(value)) return value;\n return `'${value.replaceAll(\"'\", `'\"'\"'`)}'`;\n}\n\nfunction isPowerShellBare(value: string): boolean {\n // A leading `@` splats, a lone `--` ends parameters, a leading digit reads as a number, and a\n // single-dash token with `.` or `:` reads as parameter syntax.\n return (\n CMD_BARE_ARG.test(value) &&\n !/^(@|[+-]?\\.?\\d)/.test(value) &&\n value !== \"--\" &&\n !/^-[^-].*[.:]/.test(value)\n );\n}\n\nfunction quotePowerShellArg(value: string): string {\n if (isPowerShellBare(value)) return value;\n return `'${value.replace(/['‘’‚‛]/g, \"$&$&\")}'`;\n}\n\nfunction quoteCmdArg(value: string): string {\n // Quoting a trailing backslash keeps a command routed through `cmd /c` from ending in one,\n // which PowerShell 7 doubles when it quotes the line.\n if (CMD_BARE_ARG.test(value) && !value.endsWith(\"\\\\\")) return value;\n let quoted = '\"';\n let backslashes = 0;\n for (const char of value) {\n if (char === \"\\\\\") {\n backslashes++;\n continue;\n }\n if (char === '\"') {\n quoted += `${\"\\\\\".repeat(backslashes * 2)}\"\"`;\n } else {\n // `%%cd:~,%` reads back as a literal `%` without cmd.exe expanding a `%NAME%` around it.\n quoted += `${\"\\\\\".repeat(backslashes)}${char === \"%\" ? \"%%cd:~,%\" : char}`;\n }\n backslashes = 0;\n }\n return `${quoted}${\"\\\\\".repeat(backslashes * 2)}\"`;\n}\n\nfunction formatWindowsCommandLines(argv: readonly string[]): ShellCommandLines {\n if (\n argv.every(\n (value) =>\n isPowerShellBare(value) || (value !== \"\" && !WINDOWS_DOUBLE_QUOTE_UNSAFE.test(value)),\n )\n ) {\n return {\n kind: \"shared\",\n commandLine: argv.map((value) => (isPowerShellBare(value) ? value : `\"${value}\"`)).join(\" \"),\n };\n }\n const cmd = argv.map(quoteCmdArg).join(\" \");\n // Both PowerShell versions pass a `cmd` argument the same legacy way, so routing the cmd.exe\n // line through `cmd /c` keeps the arguments identical where their native quoting differs.\n const powershell = argv.some((value) => value === \"\" || POWERSHELL_VERSION_SENSITIVE.test(value))\n ? `cmd /d /s /c ${quotePowerShellArg(cmd)}`\n : argv.map(quotePowerShellArg).join(\" \");\n return { kind: \"perShell\", powershell, cmd };\n}\n\n/**\n * Render an argv array as command lines that the current platform's shells run with every\n * argument delivered unchanged\n * @param {readonly string[]} argv - Executable name followed by its arguments\n * @returns {ShellCommandLines} One shared command line, or one per Windows shell\n */\nexport function formatShellCommandLines(argv: readonly string[]): ShellCommandLines {\n if (process.platform === \"win32\") return formatWindowsCommandLines(argv);\n return { kind: \"shared\", commandLine: argv.map(quotePosixArg).join(\" \") };\n}\n","import { styles } from \"./logger\";\nimport { formatShellCommandLines } from \"./shell-quote\";\nimport type { Jsonifiable } from \"type-fest\";\n\n/**\n * Options for creating a CLI error\n */\ninterface CLIErrorOptions {\n /** Stable machine-readable failure code, such as `WORKSPACE_NOT_FOUND`. */\n code: string;\n message: string;\n details?: string;\n suggestion?: string;\n command?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, Jsonifiable | undefined>>;\n cause?: unknown;\n}\n\nexport type CLIErrorNextAction = {\n /** Executable name, such as `tailor`. */\n command: string;\n /** Arguments passed directly to the executable. */\n args: readonly string[];\n};\n\n/**\n * CLI error interface with formatted output\n */\nexport interface CLIError extends Error {\n readonly code: string;\n readonly details?: string;\n readonly suggestion?: string;\n readonly command?: string;\n readonly next?: CLIErrorNextAction;\n readonly context?: Readonly<Record<string, Jsonifiable | undefined>>;\n format(): string;\n}\n\ntype CLIErrorInternal = Error & {\n code: string;\n details?: string;\n suggestion?: string;\n command?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, Jsonifiable | undefined>>;\n format(): string;\n};\n\n/**\n * Render a command as a user-facing hint that can be copied into the current platform's shells:\n * one backticked command line, or on Windows, when no single quoting suits both shells, a\n * PowerShell and a cmd.exe line (`` `…` in PowerShell or `…` in cmd.exe ``)\n * @param {CLIErrorNextAction} action - Executable and arguments to suggest\n * @returns {string} The command part of the hint, to follow a verb such as \"Run\"\n */\nexport function formatCommandHint(action: CLIErrorNextAction): string {\n const commandLines = formatShellCommandLines([action.command, ...action.args]);\n if (commandLines.kind === \"shared\") {\n return `\\`${commandLines.commandLine}\\``;\n }\n return `\\`${commandLines.powershell}\\` in PowerShell or \\`${commandLines.cmd}\\` in cmd.exe`;\n}\n\n/**\n * Format CLI error for output\n * @param error - CLIError instance to format\n * @returns Formatted error message\n */\nfunction formatError(error: CLIError): string {\n const parts: string[] = [\n styles.error(`Error${error.code ? ` [${error.code}]` : \"\"}: ${error.message}`),\n ];\n\n if (error.details) {\n parts.push(`\\n ${styles.dim(\"Details:\")} ${error.details.split(\"\\n\").join(\"\\n \")}`);\n }\n\n if (error.suggestion) {\n parts.push(`\\n ${styles.info(\"Suggestion:\")} ${error.suggestion.split(\"\\n\").join(\"\\n \")}`);\n }\n\n if (error.command) {\n parts.push(\n `\\n ${styles.dim(\"Help:\")} Run \\`tailor ${error.command} --help\\` for usage information.`,\n );\n }\n\n if (error.next) {\n parts.push(`\\n ${styles.info(\"Next:\")} Run ${formatCommandHint(error.next)}.`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Create a CLI error with formatted output\n * @param options - Options to construct a CLIError\n * @returns Constructed CLIError instance\n */\nexport function CLIError(options: CLIErrorOptions): CLIError {\n const error = new Error(\n options.message,\n options.cause === undefined ? undefined : { cause: options.cause },\n ) as CLIErrorInternal;\n error.name = \"CLIError\";\n error.code = options.code;\n error.details = options.details;\n error.suggestion = options.suggestion;\n error.command = options.command;\n error.next = options.next;\n error.context = options.context;\n error.format = () => formatError(error);\n return error;\n}\n\n/**\n * Type guard to check if an error is a CLIError\n * @param error - Error to check\n * @returns True if the error is a CLIError\n */\nexport function isCLIError(error: unknown): error is CLIError {\n return error instanceof Error && error.name === \"CLIError\";\n}\n\n/**\n * Summarize a failure as plain text for reports that carry a single error string.\n * CLI errors contribute their details and suggestion so the remediation survives.\n * @param error - Failure to summarize\n * @returns Message followed by any details and suggestion, one per line\n */\nexport function errorSummary(error: unknown): string {\n if (isCLIError(error)) {\n return [error.message, error.details, error.suggestion].filter(Boolean).join(\"\\n\");\n }\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * Create an error for an SDK invariant violation that no user action can fix.\n * The result is a plain Error, so JSON output reports it as `UNEXPECTED_ERROR`\n * and crash reporting treats it like any other unexpected failure.\n * @param message - Description of the violated invariant\n * @param options - Standard Error options, such as `cause`\n * @returns Plain Error instance\n */\nexport function internalError(message: string, options?: ErrorOptions): Error {\n return new Error(message, options);\n}\n\n/**\n * Convert a caught value into an Error, keeping Error instances as-is\n * @param value - Caught value\n * @returns The value itself when it is an Error, otherwise an Error of its string form with the value as `cause`\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value), { cause: value });\n}\n\nconst MISSING_NAMED_EXPORT_PATTERN = /does not provide an export named '(?!default')([^']+)'/;\n\n/**\n * Suggest `import type` when importing user code fails on a missing named\n * export. The CLI strips types from each file in isolation, so a type-only\n * export does not exist at runtime and a plain import of it fails to link.\n * @param error - Error thrown while importing user modules\n * @returns Suggestion text, or undefined when the error is not that failure\n */\nexport function typeOnlyImportHint(error: unknown): string | undefined {\n if (!(error instanceof SyntaxError)) return undefined;\n const name = MISSING_NAMED_EXPORT_PATTERN.exec(error.message)?.[1];\n if (!name) return undefined;\n return (\n `If '${name}' is a type, import it with \\`import type\\` (or the inline \\`type\\` modifier). ` +\n \"The CLI runs TypeScript by stripping types from each file in isolation, so type-only exports do not exist at runtime. \" +\n 'Set \"verbatimModuleSyntax\": true in tsconfig.json to catch this at typecheck.'\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"errors-D3qpNHR6.mjs","names":["styles","POSIX_BARE_ARG","CMD_BARE_ARG","WINDOWS_DOUBLE_QUOTE_UNSAFE","POWERSHELL_VERSION_SENSITIVE","value","quoted","backslashes","char","argv","cmd","commandLines","action","parts","error","options","message","MISSING_NAMED_EXPORT_PATTERN","name"],"sources":["../src/cli/shared/shell-quote.ts","../src/cli/shared/errors.ts"],"sourcesContent":["/**\n * Command lines for a suggested command: one line when every supported shell reads it the same\n * way, otherwise one line per Windows shell.\n */\nexport type ShellCommandLines =\n | { kind: \"shared\"; commandLine: string }\n | { kind: \"perShell\"; powershell: string; cmd: string };\n\nconst POSIX_BARE_ARG = /^[A-Za-z0-9_./:=@+-]+$/;\nconst CMD_BARE_ARG = /^[A-Za-z0-9_./:=@+\\\\-]+$/;\n// PowerShell expands `$` and backtick escapes inside double quotes and reads `“ ” „` as `\"`,\n// cmd.exe expands `%` even inside quotes, a `\"` or a trailing backslash changes where the\n// quoted argument ends, and a leading `~` is version-sensitive (below).\nconst WINDOWS_DOUBLE_QUOTE_UNSAFE = /[%$\"`“”„\\p{Cc}]|\\\\$|^~(?:[\\\\/]|$)/u;\n// PowerShell versions pass these to programs differently: Windows PowerShell 5.1 and 7.3+\n// disagree on quotes and trailing backslashes, and 7.6+ expands a leading `~` inside the `.ps1`\n// shim however it was quoted.\nconst POWERSHELL_VERSION_SENSITIVE = /\"|\\s.*\\\\$|^~(?:[\\\\/]|$)/;\n\nfunction quotePosixArg(value: string): string {\n if (POSIX_BARE_ARG.test(value)) return value;\n return `'${value.replaceAll(\"'\", `'\"'\"'`)}'`;\n}\n\nfunction isPowerShellBare(value: string): boolean {\n // A leading `@` splats, a lone `--` ends parameters, a leading digit reads as a number, and a\n // single-dash token with `.` or `:` reads as parameter syntax.\n return (\n CMD_BARE_ARG.test(value) &&\n !/^(@|[+-]?\\.?\\d)/.test(value) &&\n value !== \"--\" &&\n !/^-[^-].*[.:]/.test(value)\n );\n}\n\nfunction quotePowerShellArg(value: string): string {\n if (isPowerShellBare(value)) return value;\n return `'${value.replace(/['‘’‚‛]/g, \"$&$&\")}'`;\n}\n\nfunction quoteCmdArg(value: string): string {\n // Quoting a trailing backslash keeps a command routed through `cmd /c` from ending in one,\n // which PowerShell 7 doubles when it quotes the line.\n if (CMD_BARE_ARG.test(value) && !value.endsWith(\"\\\\\")) return value;\n let quoted = '\"';\n let backslashes = 0;\n for (const char of value) {\n if (char === \"\\\\\") {\n backslashes++;\n continue;\n }\n if (char === '\"') {\n quoted += `${\"\\\\\".repeat(backslashes * 2)}\"\"`;\n } else {\n // `%%cd:~,%` reads back as a literal `%` without cmd.exe expanding a `%NAME%` around it.\n quoted += `${\"\\\\\".repeat(backslashes)}${char === \"%\" ? \"%%cd:~,%\" : char}`;\n }\n backslashes = 0;\n }\n return `${quoted}${\"\\\\\".repeat(backslashes * 2)}\"`;\n}\n\nfunction formatWindowsCommandLines(argv: readonly string[]): ShellCommandLines {\n if (\n argv.every(\n (value) =>\n isPowerShellBare(value) || (value !== \"\" && !WINDOWS_DOUBLE_QUOTE_UNSAFE.test(value)),\n )\n ) {\n return {\n kind: \"shared\",\n commandLine: argv.map((value) => (isPowerShellBare(value) ? value : `\"${value}\"`)).join(\" \"),\n };\n }\n const cmd = argv.map(quoteCmdArg).join(\" \");\n // Both PowerShell versions pass a `cmd` argument the same legacy way, so routing the cmd.exe\n // line through `cmd /c` keeps the arguments identical where their native quoting differs.\n const powershell = argv.some((value) => value === \"\" || POWERSHELL_VERSION_SENSITIVE.test(value))\n ? `cmd /d /s /c ${quotePowerShellArg(cmd)}`\n : argv.map(quotePowerShellArg).join(\" \");\n return { kind: \"perShell\", powershell, cmd };\n}\n\n/**\n * Render an argv array as command lines that the current platform's shells run with every\n * argument delivered unchanged\n * @param {readonly string[]} argv - Executable name followed by its arguments\n * @returns {ShellCommandLines} One shared command line, or one per Windows shell\n */\nexport function formatShellCommandLines(argv: readonly string[]): ShellCommandLines {\n if (process.platform === \"win32\") return formatWindowsCommandLines(argv);\n return { kind: \"shared\", commandLine: argv.map(quotePosixArg).join(\" \") };\n}\n","import { styles } from \"./logger\";\nimport { formatShellCommandLines } from \"./shell-quote\";\nimport type { Jsonifiable } from \"type-fest\";\n\n/**\n * Options for creating a CLI error\n */\ninterface CLIErrorOptions {\n /** Stable machine-readable failure code, such as `WORKSPACE_NOT_FOUND`. */\n code: string;\n message: string;\n details?: string;\n suggestion?: string;\n command?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, Jsonifiable | undefined>>;\n cause?: unknown;\n}\n\nexport type CLIErrorNextAction = {\n /** Executable name, such as `tailor`. */\n command: string;\n /** Arguments passed directly to the executable. */\n args: readonly string[];\n};\n\n/**\n * CLI error interface with formatted output\n */\nexport interface CLIError extends Error {\n readonly code: string;\n readonly details?: string;\n readonly suggestion?: string;\n readonly command?: string;\n readonly next?: CLIErrorNextAction;\n readonly context?: Readonly<Record<string, Jsonifiable | undefined>>;\n format(): string;\n}\n\ntype CLIErrorInternal = Error & {\n code: string;\n details?: string;\n suggestion?: string;\n command?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, Jsonifiable | undefined>>;\n format(): string;\n};\n\n/**\n * Render a command as a user-facing hint that can be copied into the current platform's shells:\n * one backticked command line, or on Windows, when no single quoting suits both shells, a\n * PowerShell and a cmd.exe line (`` `…` in PowerShell or `…` in cmd.exe ``)\n * @param {CLIErrorNextAction} action - Executable and arguments to suggest\n * @returns {string} The command part of the hint, to follow a verb such as \"Run\"\n */\nexport function formatCommandHint(action: CLIErrorNextAction): string {\n const commandLines = formatShellCommandLines([action.command, ...action.args]);\n if (commandLines.kind === \"shared\") {\n return `\\`${commandLines.commandLine}\\``;\n }\n return `\\`${commandLines.powershell}\\` in PowerShell or \\`${commandLines.cmd}\\` in cmd.exe`;\n}\n\n/**\n * Format CLI error for output\n * @param error - CLIError instance to format\n * @returns Formatted error message\n */\nfunction formatError(error: CLIError): string {\n const parts: string[] = [\n styles.error(`Error${error.code ? ` [${error.code}]` : \"\"}: ${error.message}`),\n ];\n\n if (error.details) {\n parts.push(`\\n ${styles.dim(\"Details:\")} ${error.details.split(\"\\n\").join(\"\\n \")}`);\n }\n\n if (error.suggestion) {\n parts.push(`\\n ${styles.info(\"Suggestion:\")} ${error.suggestion.split(\"\\n\").join(\"\\n \")}`);\n }\n\n if (error.command) {\n parts.push(\n `\\n ${styles.dim(\"Help:\")} Run \\`tailor ${error.command} --help\\` for usage information.`,\n );\n }\n\n if (error.next) {\n parts.push(`\\n ${styles.info(\"Next:\")} Run ${formatCommandHint(error.next)}.`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Create a CLI error with formatted output\n * @param options - Options to construct a CLIError\n * @returns Constructed CLIError instance\n */\nexport function CLIError(options: CLIErrorOptions): CLIError {\n const error = new Error(\n options.message,\n options.cause === undefined ? undefined : { cause: options.cause },\n ) as CLIErrorInternal;\n error.name = \"CLIError\";\n error.code = options.code;\n error.details = options.details;\n error.suggestion = options.suggestion;\n error.command = options.command;\n error.next = options.next;\n error.context = options.context;\n error.format = () => formatError(error);\n return error;\n}\n\n/**\n * Type guard to check if an error is a CLIError\n * @param error - Error to check\n * @returns True if the error is a CLIError\n */\nexport function isCLIError(error: unknown): error is CLIError {\n return error instanceof Error && error.name === \"CLIError\";\n}\n\n/**\n * Summarize a failure as plain text for reports that carry a single error string.\n * CLI errors contribute their details and suggestion so the remediation survives.\n * @param error - Failure to summarize\n * @returns Message followed by any details and suggestion, one per line\n */\nexport function errorSummary(error: unknown): string {\n if (isCLIError(error)) {\n return [error.message, error.details, error.suggestion].filter(Boolean).join(\"\\n\");\n }\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * Create an error for an SDK invariant violation that no user action can fix.\n * The result is a plain Error, so JSON output reports it as `UNEXPECTED_ERROR`\n * and crash reporting treats it like any other unexpected failure.\n * @param message - Description of the violated invariant\n * @param options - Standard Error options, such as `cause`\n * @returns Plain Error instance\n */\nexport function internalError(message: string, options?: ErrorOptions): Error {\n return new Error(message, options);\n}\n\n/**\n * Convert a caught value into an Error, keeping Error instances as-is\n * @param value - Caught value\n * @returns The value itself when it is an Error, otherwise an Error of its string form with the value as `cause`\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value), { cause: value });\n}\n\nconst MISSING_NAMED_EXPORT_PATTERN = /does not provide an export named '(?!default')([^']+)'/;\n\n/**\n * Suggest `import type` when importing user code fails on a missing named\n * export. The CLI strips types from each file in isolation, so a type-only\n * export does not exist at runtime and a plain import of it fails to link.\n * @param error - Error thrown while importing user modules\n * @returns Suggestion text, or undefined when the error is not that failure\n */\nexport function typeOnlyImportHint(error: unknown): string | undefined {\n if (!(error instanceof SyntaxError)) return undefined;\n const name = MISSING_NAMED_EXPORT_PATTERN.exec(error.message)?.[1];\n if (!name) return undefined;\n return (\n `If '${name}' is a type, import it with \\`import type\\` (or the inline \\`type\\` modifier). ` +\n \"The CLI runs TypeScript by stripping types from each file in isolation, so type-only exports do not exist at runtime. \" +\n 'Set \"verbatimModuleSyntax\": true in tsconfig.json to catch this at typecheck.'\n );\n}\n"],"mappings":"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"}
|