@ontrails/core 1.0.0-beta.2 → 1.0.0-beta.21
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 +283 -5
- package/README.md +113 -33
- package/package.json +11 -1
- package/src/activation-provenance.ts +111 -0
- package/src/activation-source-compatibility.ts +430 -0
- package/src/activation-source-projection.ts +221 -0
- package/src/activation-source.ts +91 -0
- package/src/blob-ref.ts +51 -0
- package/src/branded.ts +1 -1
- package/src/compose-batch.ts +69 -0
- package/src/compose-schema.ts +36 -0
- package/src/context.ts +44 -10
- package/src/contour.ts +346 -0
- package/src/derive.ts +59 -18
- package/src/detours.ts +8 -0
- package/src/draft.ts +342 -0
- package/src/error-projection.ts +51 -0
- package/src/errors.ts +309 -48
- package/src/execute.ts +1577 -0
- package/src/fire.ts +1169 -0
- package/src/index.ts +477 -32
- package/src/internal/fork-ctx.ts +69 -0
- package/src/layer-projection.ts +193 -0
- package/src/layer.ts +43 -6
- package/src/observe.ts +361 -0
- package/src/path-security.ts +1 -1
- package/src/permits.ts +12 -0
- package/src/redaction/patterns.ts +6 -3
- package/src/resilience.ts +11 -11
- package/src/resource-config.ts +792 -0
- package/src/resource.ts +194 -0
- package/src/result.ts +36 -4
- package/src/run.ts +76 -0
- package/src/schedule-runtime.ts +689 -0
- package/src/schedule.ts +326 -0
- package/src/serialization.ts +119 -39
- package/src/signal-diagnostics.ts +633 -0
- package/src/signal-ref.ts +87 -0
- package/src/signal.ts +104 -0
- package/src/store/accessor-protocol.ts +56 -0
- package/src/store/index.ts +4 -0
- package/src/structured-examples.ts +248 -0
- package/src/surface-derivation.ts +91 -0
- package/src/surface-filter.ts +176 -0
- package/src/surface-versioning.ts +42 -0
- package/src/topo.ts +769 -57
- package/src/tracing.ts +345 -0
- package/src/trail.ts +1161 -29
- package/src/trails/derive-trail.ts +842 -0
- package/src/trails/index.ts +9 -0
- package/src/trails/ingest.ts +139 -0
- package/src/trails-db.ts +208 -0
- package/src/transport-error-map.ts +174 -0
- package/src/type-utils.ts +87 -0
- package/src/types.ts +254 -12
- package/src/validate-established-topo.ts +69 -0
- package/src/validate-topo.ts +442 -35
- package/src/validation.ts +152 -4
- package/src/version-marker.ts +716 -0
- package/src/version-resolution.ts +308 -0
- package/src/version-runtime.ts +120 -0
- package/src/webhook.ts +278 -0
- package/src/workspace.ts +1 -1
- package/src/zod-wrappers.ts +72 -0
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/dist/adapters.d.ts +0 -39
- package/dist/adapters.d.ts.map +0 -1
- package/dist/adapters.js +0 -2
- package/dist/adapters.js.map +0 -1
- package/dist/blob-ref.d.ts +0 -20
- package/dist/blob-ref.d.ts.map +0 -1
- package/dist/blob-ref.js +0 -22
- package/dist/blob-ref.js.map +0 -1
- package/dist/branded.d.ts +0 -36
- package/dist/branded.d.ts.map +0 -1
- package/dist/branded.js +0 -89
- package/dist/branded.js.map +0 -1
- package/dist/collections.d.ts +0 -31
- package/dist/collections.d.ts.map +0 -1
- package/dist/collections.js +0 -60
- package/dist/collections.js.map +0 -1
- package/dist/context.d.ts +0 -10
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -15
- package/dist/context.js.map +0 -1
- package/dist/derive.d.ts +0 -33
- package/dist/derive.d.ts.map +0 -1
- package/dist/derive.js +0 -122
- package/dist/derive.js.map +0 -1
- package/dist/errors.d.ts +0 -83
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -142
- package/dist/errors.js.map +0 -1
- package/dist/event.d.ts +0 -45
- package/dist/event.d.ts.map +0 -1
- package/dist/event.js +0 -17
- package/dist/event.js.map +0 -1
- package/dist/fetch.d.ts +0 -15
- package/dist/fetch.d.ts.map +0 -1
- package/dist/fetch.js +0 -102
- package/dist/fetch.js.map +0 -1
- package/dist/guards.d.ts +0 -17
- package/dist/guards.d.ts.map +0 -1
- package/dist/guards.js +0 -25
- package/dist/guards.js.map +0 -1
- package/dist/health.d.ts +0 -18
- package/dist/health.d.ts.map +0 -1
- package/dist/health.js +0 -5
- package/dist/health.js.map +0 -1
- package/dist/hike.d.ts +0 -36
- package/dist/hike.d.ts.map +0 -1
- package/dist/hike.js +0 -20
- package/dist/hike.js.map +0 -1
- package/dist/index.d.ts +0 -34
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -38
- package/dist/index.js.map +0 -1
- package/dist/job.d.ts +0 -24
- package/dist/job.d.ts.map +0 -1
- package/dist/job.js +0 -17
- package/dist/job.js.map +0 -1
- package/dist/layer.d.ts +0 -17
- package/dist/layer.d.ts.map +0 -1
- package/dist/layer.js +0 -21
- package/dist/layer.js.map +0 -1
- package/dist/path-security.d.ts +0 -28
- package/dist/path-security.d.ts.map +0 -1
- package/dist/path-security.js +0 -63
- package/dist/path-security.js.map +0 -1
- package/dist/patterns/bulk.d.ts +0 -15
- package/dist/patterns/bulk.d.ts.map +0 -1
- package/dist/patterns/bulk.js +0 -14
- package/dist/patterns/bulk.js.map +0 -1
- package/dist/patterns/change.d.ts +0 -10
- package/dist/patterns/change.d.ts.map +0 -1
- package/dist/patterns/change.js +0 -10
- package/dist/patterns/change.js.map +0 -1
- package/dist/patterns/date-range.d.ts +0 -10
- package/dist/patterns/date-range.d.ts.map +0 -1
- package/dist/patterns/date-range.js +0 -10
- package/dist/patterns/date-range.js.map +0 -1
- package/dist/patterns/index.d.ts +0 -9
- package/dist/patterns/index.d.ts.map +0 -1
- package/dist/patterns/index.js +0 -9
- package/dist/patterns/index.js.map +0 -1
- package/dist/patterns/pagination.d.ts +0 -18
- package/dist/patterns/pagination.d.ts.map +0 -1
- package/dist/patterns/pagination.js +0 -18
- package/dist/patterns/pagination.js.map +0 -1
- package/dist/patterns/progress.d.ts +0 -11
- package/dist/patterns/progress.d.ts.map +0 -1
- package/dist/patterns/progress.js +0 -11
- package/dist/patterns/progress.js.map +0 -1
- package/dist/patterns/sorting.d.ts +0 -13
- package/dist/patterns/sorting.d.ts.map +0 -1
- package/dist/patterns/sorting.js +0 -10
- package/dist/patterns/sorting.js.map +0 -1
- package/dist/patterns/status.d.ts +0 -15
- package/dist/patterns/status.d.ts.map +0 -1
- package/dist/patterns/status.js +0 -9
- package/dist/patterns/status.js.map +0 -1
- package/dist/patterns/timestamps.d.ts +0 -10
- package/dist/patterns/timestamps.d.ts.map +0 -1
- package/dist/patterns/timestamps.js +0 -10
- package/dist/patterns/timestamps.js.map +0 -1
- package/dist/redaction/index.d.ts +0 -4
- package/dist/redaction/index.d.ts.map +0 -1
- package/dist/redaction/index.js +0 -3
- package/dist/redaction/index.js.map +0 -1
- package/dist/redaction/patterns.d.ts +0 -9
- package/dist/redaction/patterns.d.ts.map +0 -1
- package/dist/redaction/patterns.js +0 -39
- package/dist/redaction/patterns.js.map +0 -1
- package/dist/redaction/redactor.d.ts +0 -27
- package/dist/redaction/redactor.d.ts.map +0 -1
- package/dist/redaction/redactor.js +0 -89
- package/dist/redaction/redactor.js.map +0 -1
- package/dist/resilience.d.ts +0 -34
- package/dist/resilience.d.ts.map +0 -1
- package/dist/resilience.js +0 -164
- package/dist/resilience.js.map +0 -1
- package/dist/result.d.ts +0 -57
- package/dist/result.d.ts.map +0 -1
- package/dist/result.js +0 -145
- package/dist/result.js.map +0 -1
- package/dist/serialization.d.ts +0 -27
- package/dist/serialization.d.ts.map +0 -1
- package/dist/serialization.js +0 -115
- package/dist/serialization.js.map +0 -1
- package/dist/topo.d.ts +0 -18
- package/dist/topo.d.ts.map +0 -1
- package/dist/topo.js +0 -74
- package/dist/topo.js.map +0 -1
- package/dist/trail.d.ts +0 -83
- package/dist/trail.d.ts.map +0 -1
- package/dist/trail.js +0 -16
- package/dist/trail.js.map +0 -1
- package/dist/types.d.ts +0 -46
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/validate-topo.d.ts +0 -24
- package/dist/validate-topo.d.ts.map +0 -1
- package/dist/validate-topo.js +0 -108
- package/dist/validate-topo.js.map +0 -1
- package/dist/validation.d.ts +0 -27
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js +0 -134
- package/dist/validation.js.map +0 -1
- package/dist/workspace.d.ts +0 -25
- package/dist/workspace.d.ts.map +0 -1
- package/dist/workspace.js +0 -57
- package/dist/workspace.js.map +0 -1
- package/src/__tests__/blob-ref.test.ts +0 -103
- package/src/__tests__/branded.test.ts +0 -148
- package/src/__tests__/collections.test.ts +0 -126
- package/src/__tests__/context.test.ts +0 -66
- package/src/__tests__/derive.test.ts +0 -159
- package/src/__tests__/errors.test.ts +0 -309
- package/src/__tests__/event.test.ts +0 -82
- package/src/__tests__/fetch.test.ts +0 -217
- package/src/__tests__/guards.test.ts +0 -102
- package/src/__tests__/hike.test.ts +0 -117
- package/src/__tests__/job.test.ts +0 -98
- package/src/__tests__/layer.test.ts +0 -224
- package/src/__tests__/path-security.test.ts +0 -114
- package/src/__tests__/patterns.test.ts +0 -273
- package/src/__tests__/redaction.test.ts +0 -244
- package/src/__tests__/resilience.test.ts +0 -246
- package/src/__tests__/result.test.ts +0 -155
- package/src/__tests__/serialization.test.ts +0 -236
- package/src/__tests__/topo.test.ts +0 -184
- package/src/__tests__/trail.test.ts +0 -179
- package/src/__tests__/validate-topo.test.ts +0 -201
- package/src/__tests__/validation.test.ts +0 -283
- package/src/__tests__/workspace.test.ts +0 -183
- package/src/adapters.ts +0 -68
- package/src/event.ts +0 -77
- package/src/health.ts +0 -23
- package/src/hike.ts +0 -77
- package/src/job.ts +0 -20
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
package/dist/validation.js
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validation utilities for @ontrails/core
|
|
3
|
-
*
|
|
4
|
-
* Wraps Zod parsing into Result types and provides JSON Schema conversion
|
|
5
|
-
* for trail input schemas.
|
|
6
|
-
*/
|
|
7
|
-
import { ValidationError } from './errors.js';
|
|
8
|
-
import { Result } from './result.js';
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
// Internal helpers (defined before usage)
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
const isOptionalLike = (s) => {
|
|
13
|
-
const defType = s._zod.def['type'];
|
|
14
|
-
return defType === 'optional' || defType === 'default';
|
|
15
|
-
};
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
// Issue formatting
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
/** Format each ZodIssue as "path: message" (or just "message" for root). */
|
|
20
|
-
export const formatZodIssues = (issues) => issues.map((issue) => {
|
|
21
|
-
const path = issue.path.join('.');
|
|
22
|
-
return path ? `${path}: ${issue.message}` : issue.message;
|
|
23
|
-
});
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Input validation
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
/** Parse unknown data against a Zod schema, returning a Result. */
|
|
28
|
-
export const validateInput = (schema, data) => {
|
|
29
|
-
const parsed = schema.safeParse(data);
|
|
30
|
-
if (parsed.success) {
|
|
31
|
-
return Result.ok(parsed.data);
|
|
32
|
-
}
|
|
33
|
-
const messages = formatZodIssues(parsed.error.issues);
|
|
34
|
-
return Result.err(new ValidationError(messages.join('; '), {
|
|
35
|
-
cause: parsed.error,
|
|
36
|
-
context: { issues: parsed.error.issues },
|
|
37
|
-
}));
|
|
38
|
-
};
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
// Output validation
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
/** Parse unknown data against a Zod schema, returning a Result suitable for output validation. */
|
|
43
|
-
export const validateOutput = (schema, data) => {
|
|
44
|
-
const parsed = schema.safeParse(data);
|
|
45
|
-
if (parsed.success) {
|
|
46
|
-
return Result.ok(parsed.data);
|
|
47
|
-
}
|
|
48
|
-
const messages = formatZodIssues(parsed.error.issues);
|
|
49
|
-
return Result.err(new ValidationError(`Output validation failed: ${messages.join('; ')}`, {
|
|
50
|
-
cause: parsed.error,
|
|
51
|
-
context: { issues: parsed.error.issues },
|
|
52
|
-
}));
|
|
53
|
-
};
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
// Zod → JSON Schema (public API)
|
|
56
|
-
// ---------------------------------------------------------------------------
|
|
57
|
-
/**
|
|
58
|
-
* Convert common Zod types to a JSON Schema object.
|
|
59
|
-
*
|
|
60
|
-
* Uses Zod v4's `_zod.def` and `_zod.traits` for introspection.
|
|
61
|
-
* Covers: string, number, boolean, object, array, enum, optional,
|
|
62
|
-
* default, union, literal, nullable, and describe.
|
|
63
|
-
*/
|
|
64
|
-
export const zodToJsonSchema = (schema) => {
|
|
65
|
-
const s = schema;
|
|
66
|
-
const collectObjectFields = (shape) => {
|
|
67
|
-
const properties = {};
|
|
68
|
-
const required = [];
|
|
69
|
-
for (const [key, value] of Object.entries(shape)) {
|
|
70
|
-
properties[key] = zodToJsonSchema(value);
|
|
71
|
-
if (!isOptionalLike(value)) {
|
|
72
|
-
required.push(key);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return { properties, required };
|
|
76
|
-
};
|
|
77
|
-
const convertObject = (value) => {
|
|
78
|
-
const shape = value._zod.def['shape'];
|
|
79
|
-
if (!shape) {
|
|
80
|
-
return { type: 'object' };
|
|
81
|
-
}
|
|
82
|
-
const { properties, required } = collectObjectFields(shape);
|
|
83
|
-
const result = { properties, type: 'object' };
|
|
84
|
-
if (required.length > 0) {
|
|
85
|
-
result['required'] = required;
|
|
86
|
-
}
|
|
87
|
-
return result;
|
|
88
|
-
};
|
|
89
|
-
const zodConverters = {
|
|
90
|
-
array: (value) => {
|
|
91
|
-
const element = value._zod.def['element'];
|
|
92
|
-
return { items: zodToJsonSchema(element), type: 'array' };
|
|
93
|
-
},
|
|
94
|
-
boolean: () => ({ type: 'boolean' }),
|
|
95
|
-
default: (value) => {
|
|
96
|
-
const inner = value._zod.def['innerType'];
|
|
97
|
-
const innerSchema = zodToJsonSchema(inner);
|
|
98
|
-
const rawDefault = value._zod.def['defaultValue'];
|
|
99
|
-
innerSchema['default'] =
|
|
100
|
-
typeof rawDefault === 'function' ? rawDefault() : rawDefault;
|
|
101
|
-
return innerSchema;
|
|
102
|
-
},
|
|
103
|
-
enum: (value) => {
|
|
104
|
-
const entries = value._zod.def['entries'];
|
|
105
|
-
return { enum: Object.values(entries), type: 'string' };
|
|
106
|
-
},
|
|
107
|
-
literal: (value) => {
|
|
108
|
-
const values = value._zod.def['values'];
|
|
109
|
-
return { const: values[0] };
|
|
110
|
-
},
|
|
111
|
-
nullable: (value) => {
|
|
112
|
-
const inner = value._zod.def['innerType'];
|
|
113
|
-
return { anyOf: [zodToJsonSchema(inner), { type: 'null' }] };
|
|
114
|
-
},
|
|
115
|
-
number: () => ({ type: 'number' }),
|
|
116
|
-
object: convertObject,
|
|
117
|
-
optional: (value) => {
|
|
118
|
-
const inner = value._zod.def['innerType'];
|
|
119
|
-
return zodToJsonSchema(inner);
|
|
120
|
-
},
|
|
121
|
-
string: () => ({ type: 'string' }),
|
|
122
|
-
union: (value) => {
|
|
123
|
-
const options = value._zod.def['options'];
|
|
124
|
-
return { anyOf: options.map((option) => zodToJsonSchema(option)) };
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
const converter = zodConverters[s._zod.def['type']];
|
|
128
|
-
const base = converter ? converter(s) : {};
|
|
129
|
-
if (s.description) {
|
|
130
|
-
base['description'] = s.description;
|
|
131
|
-
}
|
|
132
|
-
return base;
|
|
133
|
-
};
|
|
134
|
-
//# sourceMappingURL=validation.js.map
|
package/dist/validation.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
|
package/dist/workspace.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workspace detection utilities.
|
|
3
|
-
*
|
|
4
|
-
* Walks the filesystem to find monorepo workspace roots and provides
|
|
5
|
-
* helpers for working with paths relative to a workspace.
|
|
6
|
-
*/
|
|
7
|
-
import { NotFoundError } from './errors.js';
|
|
8
|
-
import { Result } from './result.js';
|
|
9
|
-
/**
|
|
10
|
-
* Walks up from `startDir` (defaults to `process.cwd()`) looking for a
|
|
11
|
-
* `package.json` that contains a `"workspaces"` field.
|
|
12
|
-
*
|
|
13
|
-
* Returns the directory path of the workspace root on success, or a
|
|
14
|
-
* `NotFoundError` if no workspace root is found.
|
|
15
|
-
*/
|
|
16
|
-
export declare const findWorkspaceRoot: (startDir?: string) => Promise<Result<string, NotFoundError>>;
|
|
17
|
-
/**
|
|
18
|
-
* Returns `true` if `filePath` is inside `workspaceRoot`.
|
|
19
|
-
*/
|
|
20
|
-
export declare const isInsideWorkspace: (filePath: string, workspaceRoot: string) => boolean;
|
|
21
|
-
/**
|
|
22
|
-
* Returns the relative path from `workspaceRoot` to `filePath`.
|
|
23
|
-
*/
|
|
24
|
-
export declare const getRelativePath: (filePath: string, workspaceRoot: string) => string;
|
|
25
|
-
//# sourceMappingURL=workspace.d.ts.map
|
package/dist/workspace.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAiBrC;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAC5B,WAAW,MAAM,KAChB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAqBvC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAC5B,UAAU,MAAM,EAChB,eAAe,MAAM,KACpB,OAGF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAC1B,UAAU,MAAM,EAChB,eAAe,MAAM,KACpB,MAA6D,CAAC"}
|
package/dist/workspace.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workspace detection utilities.
|
|
3
|
-
*
|
|
4
|
-
* Walks the filesystem to find monorepo workspace roots and provides
|
|
5
|
-
* helpers for working with paths relative to a workspace.
|
|
6
|
-
*/
|
|
7
|
-
import { resolve, relative, dirname, join, isAbsolute } from 'node:path';
|
|
8
|
-
import { NotFoundError } from './errors.js';
|
|
9
|
-
import { Result } from './result.js';
|
|
10
|
-
/** Check if a directory has a package.json with a `workspaces` field. */
|
|
11
|
-
const hasWorkspacesField = async (dir) => {
|
|
12
|
-
const pkgPath = join(dir, 'package.json');
|
|
13
|
-
const file = Bun.file(pkgPath);
|
|
14
|
-
if (!(await file.exists())) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
try {
|
|
18
|
-
const pkg = await file.json();
|
|
19
|
-
return typeof pkg === 'object' && pkg !== null && 'workspaces' in pkg;
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Walks up from `startDir` (defaults to `process.cwd()`) looking for a
|
|
27
|
-
* `package.json` that contains a `"workspaces"` field.
|
|
28
|
-
*
|
|
29
|
-
* Returns the directory path of the workspace root on success, or a
|
|
30
|
-
* `NotFoundError` if no workspace root is found.
|
|
31
|
-
*/
|
|
32
|
-
export const findWorkspaceRoot = async (startDir) => {
|
|
33
|
-
let current = resolve(startDir ?? process.cwd());
|
|
34
|
-
// eslint-disable-next-line no-constant-condition
|
|
35
|
-
while (true) {
|
|
36
|
-
if (await hasWorkspacesField(current)) {
|
|
37
|
-
return Result.ok(current);
|
|
38
|
-
}
|
|
39
|
-
const parent = dirname(current);
|
|
40
|
-
if (parent === current) {
|
|
41
|
-
return Result.err(new NotFoundError(`No workspace root found from "${startDir ?? process.cwd()}"`));
|
|
42
|
-
}
|
|
43
|
-
current = parent;
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
/**
|
|
47
|
-
* Returns `true` if `filePath` is inside `workspaceRoot`.
|
|
48
|
-
*/
|
|
49
|
-
export const isInsideWorkspace = (filePath, workspaceRoot) => {
|
|
50
|
-
const rel = relative(resolve(workspaceRoot), resolve(filePath));
|
|
51
|
-
return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel);
|
|
52
|
-
};
|
|
53
|
-
/**
|
|
54
|
-
* Returns the relative path from `workspaceRoot` to `filePath`.
|
|
55
|
-
*/
|
|
56
|
-
export const getRelativePath = (filePath, workspaceRoot) => relative(resolve(workspaceRoot), resolve(filePath));
|
|
57
|
-
//# sourceMappingURL=workspace.js.map
|
package/dist/workspace.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,yEAAyE;AACzE,MAAM,kBAAkB,GAAG,KAAK,EAAE,GAAW,EAAoB,EAAE;IACjE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAY,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACvC,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,YAAY,IAAI,GAAG,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,EACpC,QAAiB,EACuB,EAAE;IAC1C,IAAI,OAAO,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAEjD,iDAAiD;IACjD,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,MAAM,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;YACtC,OAAO,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAEhC,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YACvB,OAAO,MAAM,CAAC,GAAG,CACf,IAAI,aAAa,CACf,iCAAiC,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,GAAG,CAC9D,CACF,CAAC;QACJ,CAAC;QAED,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;AACH,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,QAAgB,EAChB,aAAqB,EACZ,EAAE;IACX,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAChE,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;AACjE,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,QAAgB,EAChB,aAAqB,EACb,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC"}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from 'bun:test';
|
|
2
|
-
|
|
3
|
-
import { createBlobRef, isBlobRef } from '../blob-ref';
|
|
4
|
-
|
|
5
|
-
describe('BlobRef', () => {
|
|
6
|
-
const sampleData = new Uint8Array([137, 80, 78, 71]);
|
|
7
|
-
|
|
8
|
-
describe('createBlobRef', () => {
|
|
9
|
-
test('returns a frozen object with the provided fields', () => {
|
|
10
|
-
const ref = createBlobRef({
|
|
11
|
-
data: sampleData,
|
|
12
|
-
mimeType: 'image/png',
|
|
13
|
-
name: 'image.png',
|
|
14
|
-
size: 4,
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
expect(ref.name).toBe('image.png');
|
|
18
|
-
expect(ref.mimeType).toBe('image/png');
|
|
19
|
-
expect(ref.size).toBe(4);
|
|
20
|
-
expect(ref.data).toBe(sampleData);
|
|
21
|
-
expect(Object.isFrozen(ref)).toBe(true);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('works with ReadableStream data', () => {
|
|
25
|
-
const stream = new ReadableStream<Uint8Array>({
|
|
26
|
-
start(controller) {
|
|
27
|
-
controller.enqueue(sampleData);
|
|
28
|
-
controller.close();
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const ref = createBlobRef({
|
|
33
|
-
data: stream,
|
|
34
|
-
mimeType: 'video/mp4',
|
|
35
|
-
name: 'video.mp4',
|
|
36
|
-
size: 1024,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
expect(ref.data).toBeInstanceOf(ReadableStream);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('isBlobRef', () => {
|
|
44
|
-
test('returns true for a valid BlobRef with Uint8Array data', () => {
|
|
45
|
-
const ref = createBlobRef({
|
|
46
|
-
data: sampleData,
|
|
47
|
-
mimeType: 'application/octet-stream',
|
|
48
|
-
name: 'file.bin',
|
|
49
|
-
size: 4,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
expect(isBlobRef(ref)).toBe(true);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('returns true for a valid BlobRef with ReadableStream data', () => {
|
|
56
|
-
const stream = new ReadableStream<Uint8Array>();
|
|
57
|
-
const ref = createBlobRef({
|
|
58
|
-
data: stream,
|
|
59
|
-
mimeType: 'application/octet-stream',
|
|
60
|
-
name: 'file.bin',
|
|
61
|
-
size: 0,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
expect(isBlobRef(ref)).toBe(true);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test('returns true for a plain object matching the shape', () => {
|
|
68
|
-
expect(
|
|
69
|
-
isBlobRef({
|
|
70
|
-
data: new Uint8Array(),
|
|
71
|
-
mimeType: 'application/pdf',
|
|
72
|
-
name: 'doc.pdf',
|
|
73
|
-
size: 999,
|
|
74
|
-
})
|
|
75
|
-
).toBe(true);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test('returns false for null', () => {
|
|
79
|
-
expect(isBlobRef(null)).toBe(false);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test('returns false for a string', () => {
|
|
83
|
-
expect(isBlobRef('not a blob')).toBe(false);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test('returns false when name is missing', () => {
|
|
87
|
-
expect(
|
|
88
|
-
isBlobRef({ data: new Uint8Array(), mimeType: 'text/plain', size: 0 })
|
|
89
|
-
).toBe(false);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test('returns false when data is a plain object', () => {
|
|
93
|
-
expect(
|
|
94
|
-
isBlobRef({
|
|
95
|
-
data: {},
|
|
96
|
-
mimeType: 'text/plain',
|
|
97
|
-
name: 'x',
|
|
98
|
-
size: 0,
|
|
99
|
-
})
|
|
100
|
-
).toBe(false);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
});
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from 'bun:test';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
brand,
|
|
5
|
-
unbrand,
|
|
6
|
-
uuid,
|
|
7
|
-
email,
|
|
8
|
-
nonEmptyString,
|
|
9
|
-
positiveInt,
|
|
10
|
-
shortId,
|
|
11
|
-
hashId,
|
|
12
|
-
} from '../branded';
|
|
13
|
-
|
|
14
|
-
describe('branded', () => {
|
|
15
|
-
describe('brand / unbrand', () => {
|
|
16
|
-
test('brand wraps and unbrand unwraps', () => {
|
|
17
|
-
const branded = brand('Tag', 'hello');
|
|
18
|
-
expect(unbrand(branded)).toBe('hello');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test('branded value is still usable as base type at runtime', () => {
|
|
22
|
-
const branded = brand('PositiveInt', 42);
|
|
23
|
-
expect(branded + 1).toBe(43);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('uuid()', () => {
|
|
28
|
-
test('accepts a valid v4 UUID', () => {
|
|
29
|
-
const result = uuid('550e8400-e29b-41d4-a716-446655440000');
|
|
30
|
-
expect(result.isOk()).toBe(true);
|
|
31
|
-
const value: string = unbrand(result.unwrap());
|
|
32
|
-
expect(value).toBe('550e8400-e29b-41d4-a716-446655440000');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('rejects an empty string', () => {
|
|
36
|
-
const result = uuid('');
|
|
37
|
-
expect(result.isErr()).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('rejects a malformed UUID', () => {
|
|
41
|
-
const result = uuid('not-a-uuid');
|
|
42
|
-
expect(result.isErr()).toBe(true);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test('rejects UUID missing hyphens', () => {
|
|
46
|
-
const result = uuid('550e8400e29b41d4a716446655440000');
|
|
47
|
-
expect(result.isErr()).toBe(true);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('email()', () => {
|
|
52
|
-
test('accepts a simple email', () => {
|
|
53
|
-
const result = email('test@example.com');
|
|
54
|
-
expect(result.isOk()).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('rejects missing @', () => {
|
|
58
|
-
const result = email('testexample.com');
|
|
59
|
-
expect(result.isErr()).toBe(true);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test('rejects missing domain', () => {
|
|
63
|
-
const result = email('test@');
|
|
64
|
-
expect(result.isErr()).toBe(true);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test('rejects empty string', () => {
|
|
68
|
-
const result = email('');
|
|
69
|
-
expect(result.isErr()).toBe(true);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
describe('nonEmptyString()', () => {
|
|
74
|
-
test('accepts a non-empty string', () => {
|
|
75
|
-
const result = nonEmptyString('hello');
|
|
76
|
-
expect(result.isOk()).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('rejects an empty string', () => {
|
|
80
|
-
const result = nonEmptyString('');
|
|
81
|
-
expect(result.isErr()).toBe(true);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
describe('positiveInt()', () => {
|
|
86
|
-
test('accepts 1', () => {
|
|
87
|
-
const result = positiveInt(1);
|
|
88
|
-
expect(result.isOk()).toBe(true);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('accepts a large integer', () => {
|
|
92
|
-
const result = positiveInt(1_000_000);
|
|
93
|
-
expect(result.isOk()).toBe(true);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('rejects 0', () => {
|
|
97
|
-
const result = positiveInt(0);
|
|
98
|
-
expect(result.isErr()).toBe(true);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test('rejects negative numbers', () => {
|
|
102
|
-
const result = positiveInt(-5);
|
|
103
|
-
expect(result.isErr()).toBe(true);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test('rejects non-integers', () => {
|
|
107
|
-
const result = positiveInt(1.5);
|
|
108
|
-
expect(result.isErr()).toBe(true);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe('shortId()', () => {
|
|
113
|
-
test('returns a string of default length 8', () => {
|
|
114
|
-
const id = shortId();
|
|
115
|
-
expect(id).toHaveLength(8);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test('respects custom length', () => {
|
|
119
|
-
const id = shortId(16);
|
|
120
|
-
expect(id).toHaveLength(16);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test('contains only alphanumeric characters', () => {
|
|
124
|
-
const id = shortId(100);
|
|
125
|
-
expect(id).toMatch(/^[A-Za-z0-9]+$/);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test('produces unique values', () => {
|
|
129
|
-
const ids = new Set(Array.from({ length: 50 }, () => shortId()));
|
|
130
|
-
expect(ids.size).toBe(50);
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
describe('hashId()', () => {
|
|
135
|
-
test('returns a hex string', () => {
|
|
136
|
-
const id = hashId('test');
|
|
137
|
-
expect(id).toMatch(/^[0-9a-f]{8}$/);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test('is deterministic', () => {
|
|
141
|
-
expect(hashId('hello')).toBe(hashId('hello'));
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test('different inputs produce different outputs', () => {
|
|
145
|
-
expect(hashId('a')).not.toBe(hashId('b'));
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
});
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from 'bun:test';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
chunk,
|
|
5
|
-
dedupe,
|
|
6
|
-
groupBy,
|
|
7
|
-
sortBy,
|
|
8
|
-
isNonEmptyArray,
|
|
9
|
-
} from '../collections';
|
|
10
|
-
|
|
11
|
-
describe('collections', () => {
|
|
12
|
-
describe('chunk()', () => {
|
|
13
|
-
test('splits into even chunks', () => {
|
|
14
|
-
expect(chunk([1, 2, 3, 4], 2)).toEqual([
|
|
15
|
-
[1, 2],
|
|
16
|
-
[3, 4],
|
|
17
|
-
]);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test('handles remainder', () => {
|
|
21
|
-
expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('returns empty array for empty input', () => {
|
|
25
|
-
expect(chunk([], 3)).toEqual([]);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('single-element chunks', () => {
|
|
29
|
-
expect(chunk([1, 2, 3], 1)).toEqual([[1], [2], [3]]);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('chunk size larger than array', () => {
|
|
33
|
-
expect(chunk([1, 2], 10)).toEqual([[1, 2]]);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('throws on size < 1', () => {
|
|
37
|
-
expect(() => chunk([1], 0)).toThrow(RangeError);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('dedupe()', () => {
|
|
42
|
-
test('removes primitive duplicates', () => {
|
|
43
|
-
expect(dedupe([1, 2, 2, 3, 1])).toEqual([1, 2, 3]);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('dedupes by key function', () => {
|
|
47
|
-
const items = [
|
|
48
|
-
{ id: 1, name: 'a' },
|
|
49
|
-
{ id: 2, name: 'b' },
|
|
50
|
-
{ id: 1, name: 'c' },
|
|
51
|
-
];
|
|
52
|
-
const result = dedupe(items, (i) => i.id);
|
|
53
|
-
expect(result).toEqual([
|
|
54
|
-
{ id: 1, name: 'a' },
|
|
55
|
-
{ id: 2, name: 'b' },
|
|
56
|
-
]);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('preserves order', () => {
|
|
60
|
-
expect(dedupe([3, 1, 2, 1, 3])).toEqual([3, 1, 2]);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('handles empty array', () => {
|
|
64
|
-
expect(dedupe([])).toEqual([]);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
describe('groupBy()', () => {
|
|
69
|
-
test('groups by key', () => {
|
|
70
|
-
const items = [
|
|
71
|
-
{ type: 'a', value: 1 },
|
|
72
|
-
{ type: 'b', value: 2 },
|
|
73
|
-
{ type: 'a', value: 3 },
|
|
74
|
-
];
|
|
75
|
-
const groups = groupBy(items, (i) => i.type);
|
|
76
|
-
expect(groups).toEqual({
|
|
77
|
-
a: [
|
|
78
|
-
{ type: 'a', value: 1 },
|
|
79
|
-
{ type: 'a', value: 3 },
|
|
80
|
-
],
|
|
81
|
-
b: [{ type: 'b', value: 2 }],
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test('returns empty object for empty array', () => {
|
|
86
|
-
expect(groupBy([], () => 'key')).toEqual({});
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe('sortBy()', () => {
|
|
91
|
-
test('sorts by numeric key ascending', () => {
|
|
92
|
-
const items = [{ n: 3 }, { n: 1 }, { n: 2 }];
|
|
93
|
-
expect(sortBy(items, (i) => i.n)).toEqual([{ n: 1 }, { n: 2 }, { n: 3 }]);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('sorts by string key alphabetically', () => {
|
|
97
|
-
const items = [{ name: 'charlie' }, { name: 'alice' }, { name: 'bob' }];
|
|
98
|
-
expect(sortBy(items, (i) => i.name)).toEqual([
|
|
99
|
-
{ name: 'alice' },
|
|
100
|
-
{ name: 'bob' },
|
|
101
|
-
{ name: 'charlie' },
|
|
102
|
-
]);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('does not mutate the original array', () => {
|
|
106
|
-
const original = [{ n: 3 }, { n: 1 }];
|
|
107
|
-
sortBy(original, (i) => i.n);
|
|
108
|
-
expect(original[0]?.n).toBe(3);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test('returns empty array for empty input', () => {
|
|
112
|
-
expect(sortBy([], () => 0)).toEqual([]);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe('isNonEmptyArray()', () => {
|
|
117
|
-
test('returns true for arrays with elements', () => {
|
|
118
|
-
expect(isNonEmptyArray([1])).toBe(true);
|
|
119
|
-
expect(isNonEmptyArray([1, 2, 3])).toBe(true);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test('returns false for empty array', () => {
|
|
123
|
-
expect(isNonEmptyArray([])).toBe(false);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
});
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from 'bun:test';
|
|
2
|
-
|
|
3
|
-
import { createTrailContext } from '../context.js';
|
|
4
|
-
import type { TrailContext } from '../types.js';
|
|
5
|
-
|
|
6
|
-
const UUID_RE =
|
|
7
|
-
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8
|
-
|
|
9
|
-
describe('createTrailContext', () => {
|
|
10
|
-
test('generates a requestId in UUID format', () => {
|
|
11
|
-
const ctx = createTrailContext();
|
|
12
|
-
expect(ctx.requestId).toMatch(UUID_RE);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test('defaults cwd to process.cwd()', () => {
|
|
16
|
-
const ctx = createTrailContext();
|
|
17
|
-
expect(ctx.cwd).toBe(process.cwd());
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test('defaults env to process.env', () => {
|
|
21
|
-
const ctx = createTrailContext();
|
|
22
|
-
expect(ctx.env).toBeTruthy();
|
|
23
|
-
expect(ctx.env).toBe(process.env);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test('provides a non-aborted signal by default', () => {
|
|
27
|
-
const ctx = createTrailContext();
|
|
28
|
-
expect(ctx.signal).toBeInstanceOf(AbortSignal);
|
|
29
|
-
expect(ctx.signal.aborted).toBe(false);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('override values take precedence', () => {
|
|
33
|
-
const ac = new AbortController();
|
|
34
|
-
ac.abort();
|
|
35
|
-
|
|
36
|
-
const ctx = createTrailContext({
|
|
37
|
-
cwd: '/custom/dir',
|
|
38
|
-
env: { CUSTOM: 'yes' },
|
|
39
|
-
requestId: 'custom-id',
|
|
40
|
-
signal: ac.signal,
|
|
41
|
-
workspaceRoot: '/tmp',
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
expect(ctx.cwd).toBe('/custom/dir');
|
|
45
|
-
expect(ctx.env).toEqual({ CUSTOM: 'yes' });
|
|
46
|
-
expect(ctx.requestId).toBe('custom-id');
|
|
47
|
-
expect(ctx.signal.aborted).toBe(true);
|
|
48
|
-
expect(ctx.workspaceRoot).toBe('/tmp');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('TrailContext is extensible with custom fields', () => {
|
|
52
|
-
const ctx: TrailContext = createTrailContext({
|
|
53
|
-
customField: 42,
|
|
54
|
-
nested: { key: 'value' },
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
expect(ctx['customField']).toBe(42);
|
|
58
|
-
expect(ctx['nested']).toEqual({ key: 'value' });
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('each call generates a unique requestId', () => {
|
|
62
|
-
const a = createTrailContext();
|
|
63
|
-
const b = createTrailContext();
|
|
64
|
-
expect(a.requestId).not.toBe(b.requestId);
|
|
65
|
-
});
|
|
66
|
-
});
|