@ontrails/core 1.0.0-beta.4 → 1.0.0-beta.41
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 +412 -12
- package/README.md +112 -28
- package/package.json +11 -1
- package/src/activation-provenance.ts +116 -0
- package/src/activation-source-compatibility.ts +430 -0
- package/src/activation-source-projection.ts +227 -0
- package/src/activation-source.ts +93 -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 +59 -11
- package/src/derive.ts +271 -19
- package/src/detours.ts +8 -0
- package/src/diagnostics.ts +21 -0
- package/src/draft.ts +350 -0
- package/src/entity.ts +346 -0
- package/src/error-projection.ts +51 -0
- package/src/errors.ts +335 -48
- package/src/execute.ts +1577 -0
- package/src/fire.ts +1172 -0
- package/src/glob.ts +81 -0
- package/src/index.ts +568 -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-scope.ts +66 -0
- package/src/path-security.ts +11 -3
- package/src/permits.ts +12 -0
- package/src/queue.ts +163 -0
- package/src/redaction/patterns.ts +6 -3
- package/src/resilience.ts +11 -11
- package/src/resource-config.ts +804 -0
- package/src/resource.ts +194 -0
- package/src/result.ts +18 -0
- package/src/run.ts +76 -0
- package/src/runtime-builtins.ts +69 -0
- package/src/schedule-runtime.ts +689 -0
- package/src/schedule.ts +326 -0
- package/src/serialization.ts +92 -55
- package/src/sha256.ts +136 -0
- package/src/signal-diagnostics.ts +633 -0
- package/src/signal-ref.ts +111 -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 +101 -0
- package/src/surface-overlay.ts +694 -0
- package/src/surface-versioning.ts +42 -0
- package/src/topo.ts +762 -38
- package/src/tracing.ts +346 -0
- package/src/trail-id-glob.ts +15 -0
- package/src/trail.ts +1244 -34
- package/src/trails/derive-trail.ts +835 -0
- package/src/trails/index.ts +9 -0
- package/src/trails/ingest.ts +152 -0
- package/src/trails-db.ts +212 -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 +73 -0
- package/src/validate-topo.ts +569 -55
- 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 +461 -0
- package/src/workspace.ts +183 -16
- 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 -125
- 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/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 -156
- 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 -153
- package/dist/serialization.js.map +0 -1
- package/dist/topo.d.ts +0 -16
- package/dist/topo.d.ts.map +0 -1
- package/dist/topo.js +0 -66
- package/dist/topo.js.map +0 -1
- package/dist/trail.d.ts +0 -89
- package/dist/trail.d.ts.map +0 -1
- package/dist/trail.js +0 -18
- 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 -158
- 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 -203
- 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__/job.test.ts +0 -98
- package/src/__tests__/layer.test.ts +0 -212
- 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 -401
- package/src/__tests__/topo.test.ts +0 -181
- package/src/__tests__/trail.test.ts +0 -217
- package/src/__tests__/validate-topo.test.ts +0 -278
- 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/job.ts +0 -20
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
import { matchesAnyGlob, matchesGlob } from './glob.js';
|
|
4
|
+
|
|
5
|
+
declare const pathGlobBrand: unique symbol;
|
|
6
|
+
|
|
7
|
+
export type PathGlob = string & {
|
|
8
|
+
readonly [pathGlobBrand]: 'PathGlob';
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const normalizePathScopePath = (value: string): string =>
|
|
12
|
+
value.replaceAll('\\', '/').replace(/^\.\//, '');
|
|
13
|
+
|
|
14
|
+
export const matchesPathGlob = (path: string, pattern: string): boolean =>
|
|
15
|
+
matchesGlob(normalizePathScopePath(path), normalizePathScopePath(pattern), {
|
|
16
|
+
separator: '/',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const matchesAnyPathGlob = (
|
|
20
|
+
path: string,
|
|
21
|
+
patterns: readonly string[] | undefined
|
|
22
|
+
): boolean =>
|
|
23
|
+
matchesAnyGlob(
|
|
24
|
+
normalizePathScopePath(path),
|
|
25
|
+
patterns?.map(normalizePathScopePath),
|
|
26
|
+
{ separator: '/' }
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const pathGlobArraySchema = z.array(z.string()).readonly();
|
|
30
|
+
|
|
31
|
+
export const pathScopeSchema = z
|
|
32
|
+
.object({
|
|
33
|
+
exclude: pathGlobArraySchema.optional(),
|
|
34
|
+
extensions: z.array(z.string()).readonly().optional(),
|
|
35
|
+
include: pathGlobArraySchema.optional(),
|
|
36
|
+
})
|
|
37
|
+
.strict();
|
|
38
|
+
|
|
39
|
+
export type PathScope = z.output<typeof pathScopeSchema>;
|
|
40
|
+
|
|
41
|
+
export type ScanTargets = Pick<PathScope, 'exclude' | 'extensions'>;
|
|
42
|
+
|
|
43
|
+
const extensionOf = (path: string): string => {
|
|
44
|
+
const normalized = normalizePathScopePath(path);
|
|
45
|
+
const name = normalized.slice(normalized.lastIndexOf('/') + 1);
|
|
46
|
+
const dot = name.lastIndexOf('.');
|
|
47
|
+
return dot <= 0 ? '' : name.slice(dot);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const normalizeExtension = (extension: string): string =>
|
|
51
|
+
extension === '' || extension.startsWith('.') ? extension : `.${extension}`;
|
|
52
|
+
|
|
53
|
+
const includedByExtension = (
|
|
54
|
+
path: string,
|
|
55
|
+
extensions: readonly string[] | undefined
|
|
56
|
+
): boolean =>
|
|
57
|
+
extensions === undefined ||
|
|
58
|
+
extensions.length === 0 ||
|
|
59
|
+
extensions.map(normalizeExtension).includes(extensionOf(path));
|
|
60
|
+
|
|
61
|
+
export const includedByPathScope = (path: string, scope?: PathScope): boolean =>
|
|
62
|
+
(scope?.include === undefined ||
|
|
63
|
+
scope.include.length === 0 ||
|
|
64
|
+
matchesAnyPathGlob(path, scope.include)) &&
|
|
65
|
+
!matchesAnyPathGlob(path, scope?.exclude) &&
|
|
66
|
+
includedByExtension(path, scope?.extensions);
|
package/src/path-security.ts
CHANGED
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
* All functions are runtime-agnostic (Node / Bun compatible).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { resolve, relative, normalize, isAbsolute } from 'node:path';
|
|
8
|
-
|
|
9
7
|
import { PermissionError } from './errors.js';
|
|
10
8
|
import { Result } from './result.js';
|
|
9
|
+
// Path security guards filesystem access on tooling paths: node:path
|
|
10
|
+
// loads lazily at first use so the core barrel's module graph stays
|
|
11
|
+
// execution-portable on runtimes without node: builtins (TRL-1198).
|
|
12
|
+
import { loadRuntimeBuiltin } from './runtime-builtins.js';
|
|
13
|
+
|
|
14
|
+
const nodePath = () => loadRuntimeBuiltin('node:path');
|
|
11
15
|
|
|
12
16
|
// ---------------------------------------------------------------------------
|
|
13
17
|
// Internal
|
|
@@ -15,6 +19,7 @@ import { Result } from './result.js';
|
|
|
15
19
|
|
|
16
20
|
/** Returns true when `target` is equal to or a descendant of `base`. */
|
|
17
21
|
const isWithin = (base: string, target: string): boolean => {
|
|
22
|
+
const { isAbsolute, relative } = nodePath();
|
|
18
23
|
const rel = relative(base, target);
|
|
19
24
|
// Empty string means they are the same directory.
|
|
20
25
|
// A relative path starting with ".." means it escapes.
|
|
@@ -39,6 +44,7 @@ export const securePath = (
|
|
|
39
44
|
basePath: string,
|
|
40
45
|
userPath: string
|
|
41
46
|
): Result<string, PermissionError> => {
|
|
47
|
+
const { resolve } = nodePath();
|
|
42
48
|
const base = resolve(basePath);
|
|
43
49
|
const resolved = resolve(base, userPath);
|
|
44
50
|
|
|
@@ -61,6 +67,7 @@ export const securePath = (
|
|
|
61
67
|
* `basePath`.
|
|
62
68
|
*/
|
|
63
69
|
export const isPathSafe = (basePath: string, userPath: string): boolean => {
|
|
70
|
+
const { resolve } = nodePath();
|
|
64
71
|
const base = resolve(basePath);
|
|
65
72
|
const resolved = resolve(base, userPath);
|
|
66
73
|
return isWithin(base, resolved);
|
|
@@ -70,10 +77,11 @@ export const isPathSafe = (basePath: string, userPath: string): boolean => {
|
|
|
70
77
|
* Joins multiple path segments, resolves them against `basePath`, and
|
|
71
78
|
* validates the result stays within the base directory.
|
|
72
79
|
*/
|
|
73
|
-
export const
|
|
80
|
+
export const deriveSafePath = (
|
|
74
81
|
basePath: string,
|
|
75
82
|
...segments: string[]
|
|
76
83
|
): Result<string, PermissionError> => {
|
|
84
|
+
const { normalize, resolve } = nodePath();
|
|
77
85
|
const base = resolve(basePath);
|
|
78
86
|
const joined = resolve(base, ...segments.map((s) => normalize(s)));
|
|
79
87
|
|
package/src/permits.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/** Minimal permit shape available on TrailContext. */
|
|
4
|
+
export interface BasePermit {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly scopes: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const basePermitSchema: z.ZodType<BasePermit> = z.object({
|
|
10
|
+
id: z.string(),
|
|
11
|
+
scopes: z.array(z.string()).readonly(),
|
|
12
|
+
});
|
package/src/queue.ts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ActivationSource,
|
|
3
|
+
ActivationSourceMeta,
|
|
4
|
+
ActivationSourceParse,
|
|
5
|
+
} from './activation-source.js';
|
|
6
|
+
import { ValidationError } from './errors.js';
|
|
7
|
+
|
|
8
|
+
export interface QueueSpec<TOutput = unknown> {
|
|
9
|
+
readonly meta?: ActivationSourceMeta | undefined;
|
|
10
|
+
readonly parse: ActivationSourceParse<TOutput>;
|
|
11
|
+
readonly payload?: ActivationSource['payload'] | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Runtime queue name. Defaults to the source id, so authored queue
|
|
14
|
+
* contracts can stay stable while host bindings choose their own names.
|
|
15
|
+
*/
|
|
16
|
+
readonly queue?: string | undefined;
|
|
17
|
+
/** Reserved for future queue-specific design; trail versioning is trail-only. */
|
|
18
|
+
readonly version?: never;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface QueueSource<TOutput = unknown> extends ActivationSource {
|
|
22
|
+
readonly kind: 'queue';
|
|
23
|
+
readonly meta?: ActivationSourceMeta | undefined;
|
|
24
|
+
readonly parse: ActivationSourceParse<TOutput>;
|
|
25
|
+
readonly payload?: ActivationSource['payload'] | undefined;
|
|
26
|
+
readonly queue: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface QueueValidationIssue {
|
|
30
|
+
readonly field: 'parse' | 'queue';
|
|
31
|
+
readonly message: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const normalizeQueueName = (queueName: string): string => queueName.trim();
|
|
35
|
+
|
|
36
|
+
const isObjectRecord = (value: unknown): value is Record<string, unknown> =>
|
|
37
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
38
|
+
|
|
39
|
+
const isZodSchema = (value: unknown): boolean =>
|
|
40
|
+
isObjectRecord(value) && typeof value['safeParse'] === 'function';
|
|
41
|
+
|
|
42
|
+
const validateQueueName = (queueName: unknown): QueueValidationIssue[] =>
|
|
43
|
+
typeof queueName === 'string' && queueName.trim().length > 0
|
|
44
|
+
? []
|
|
45
|
+
: [
|
|
46
|
+
{
|
|
47
|
+
field: 'queue',
|
|
48
|
+
message: 'Queue source must define a non-empty queue name',
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const validateRequiredParse = (parse: unknown): QueueValidationIssue[] =>
|
|
53
|
+
parse === undefined
|
|
54
|
+
? [
|
|
55
|
+
{
|
|
56
|
+
field: 'parse',
|
|
57
|
+
message: 'Queue sources must define parse',
|
|
58
|
+
},
|
|
59
|
+
]
|
|
60
|
+
: [];
|
|
61
|
+
|
|
62
|
+
const validateParseShape = (parse: unknown): QueueValidationIssue[] => {
|
|
63
|
+
if (parse === undefined || isZodSchema(parse)) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
if (isObjectRecord(parse) && isZodSchema(parse['output'])) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
return [
|
|
70
|
+
{
|
|
71
|
+
field: 'parse',
|
|
72
|
+
message: 'Queue parse must be a Zod schema or define parse.output',
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const queueIssuesMessage = (
|
|
78
|
+
id: string,
|
|
79
|
+
issues: readonly QueueValidationIssue[]
|
|
80
|
+
): string =>
|
|
81
|
+
`queue("${id}") is invalid: ${issues.map((issue) => `${issue.field}: ${issue.message}`).join('; ')}`;
|
|
82
|
+
|
|
83
|
+
const assertQueueSpec = <TOutput>(
|
|
84
|
+
id: string,
|
|
85
|
+
spec: QueueSpec<TOutput>
|
|
86
|
+
): {
|
|
87
|
+
readonly queue: string;
|
|
88
|
+
} => {
|
|
89
|
+
const queueName = spec.queue === undefined ? id : spec.queue;
|
|
90
|
+
const issues = [
|
|
91
|
+
...validateQueueName(queueName),
|
|
92
|
+
...validateRequiredParse(spec.parse),
|
|
93
|
+
...validateParseShape(spec.parse),
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
if (issues.length > 0) {
|
|
97
|
+
throw new ValidationError(queueIssuesMessage(id, issues), {
|
|
98
|
+
context: { issues },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { queue: normalizeQueueName(queueName) };
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const validateQueueSource = (
|
|
106
|
+
source: ActivationSource
|
|
107
|
+
): readonly QueueValidationIssue[] => {
|
|
108
|
+
if (source.kind !== 'queue') {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return [
|
|
113
|
+
...validateQueueName(source.queue),
|
|
114
|
+
...validateRequiredParse(source.parse),
|
|
115
|
+
...validateParseShape(source.parse),
|
|
116
|
+
];
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Define a queue activation source.
|
|
121
|
+
*
|
|
122
|
+
* Queue sources are inert contract data: they describe which runtime queue
|
|
123
|
+
* wakes a trail and how the message body becomes trail input. A host adapter
|
|
124
|
+
* such as `@ontrails/cloudflare/workers` owns delivery.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* import { queue } from '@ontrails/core';
|
|
129
|
+
* import { z } from 'zod';
|
|
130
|
+
*
|
|
131
|
+
* const source = queue('queue.email.outbox', {
|
|
132
|
+
* queue: 'email-outbox',
|
|
133
|
+
* parse: z.object({ messageId: z.string() }),
|
|
134
|
+
* });
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
export function queue<TOutput>(
|
|
138
|
+
id: string,
|
|
139
|
+
spec: QueueSpec<TOutput>
|
|
140
|
+
): QueueSource<TOutput>;
|
|
141
|
+
export function queue<TOutput>(
|
|
142
|
+
spec: QueueSpec<TOutput> & { readonly id: string }
|
|
143
|
+
): QueueSource<TOutput>;
|
|
144
|
+
export function queue<TOutput>(
|
|
145
|
+
idOrSpec: string | (QueueSpec<TOutput> & { readonly id: string }),
|
|
146
|
+
maybeSpec?: QueueSpec<TOutput>
|
|
147
|
+
): QueueSource<TOutput> {
|
|
148
|
+
const id = typeof idOrSpec === 'string' ? idOrSpec : idOrSpec.id;
|
|
149
|
+
// oxlint-disable-next-line no-non-null-assertion -- overload guarantees maybeSpec when idOrSpec is string
|
|
150
|
+
const spec = typeof idOrSpec === 'string' ? maybeSpec! : idOrSpec;
|
|
151
|
+
const normalized = assertQueueSpec(id, spec);
|
|
152
|
+
|
|
153
|
+
return Object.freeze({
|
|
154
|
+
id,
|
|
155
|
+
kind: 'queue' as const,
|
|
156
|
+
parse: spec.parse,
|
|
157
|
+
queue: normalized.queue,
|
|
158
|
+
...(spec.meta === undefined
|
|
159
|
+
? {}
|
|
160
|
+
: { meta: Object.freeze({ ...spec.meta }) }),
|
|
161
|
+
...(spec.payload === undefined ? {} : { payload: spec.payload }),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
@@ -16,11 +16,14 @@ export const DEFAULT_PATTERNS: RegExp[] = [
|
|
|
16
16
|
// SSN: XXX-XX-XXXX
|
|
17
17
|
/\b\d{3}-\d{2}-\d{4}\b/g,
|
|
18
18
|
|
|
19
|
-
// Bearer tokens
|
|
20
|
-
/Bearer\s+[A-Za-z0-9\-._
|
|
19
|
+
// Bearer tokens. Keep the threshold above short prose like "Bearer token".
|
|
20
|
+
/Bearer\s+[A-Za-z0-9\-._~+/=]{6,}/g,
|
|
21
21
|
|
|
22
22
|
// Basic auth
|
|
23
|
-
/Basic\s+[A-Za-z0-9
|
|
23
|
+
/Basic\s+[A-Za-z0-9+/=]{8,}/g,
|
|
24
|
+
|
|
25
|
+
// Common key/value secrets embedded in strings
|
|
26
|
+
/\b(?:password|secret|token|api[_-]?key|cookie)\s*[:=]\s*[^\s,;]+/gi,
|
|
24
27
|
|
|
25
28
|
// API keys: sk-*, pk_*, sk_* prefixed tokens
|
|
26
29
|
/\b(?:sk-|pk_|sk_)[A-Za-z0-9_-]{8,}\b/g,
|
package/src/resilience.ts
CHANGED
|
@@ -61,11 +61,11 @@ const sleep = (ms: number, signal?: AbortSignal): Promise<void> =>
|
|
|
61
61
|
export const shouldRetry = (error: Error): boolean => isRetryable(error);
|
|
62
62
|
|
|
63
63
|
// ---------------------------------------------------------------------------
|
|
64
|
-
//
|
|
64
|
+
// deriveBackoffDelay
|
|
65
65
|
// ---------------------------------------------------------------------------
|
|
66
66
|
|
|
67
67
|
/** Compute exponential backoff delay with full jitter. */
|
|
68
|
-
export const
|
|
68
|
+
export const deriveBackoffDelay = (
|
|
69
69
|
attempt: number,
|
|
70
70
|
options?: Pick<RetryOptions, 'baseDelay' | 'maxDelay' | 'backoffFactor'>
|
|
71
71
|
): number => {
|
|
@@ -108,7 +108,7 @@ const tryAttempt = async <T>(
|
|
|
108
108
|
if (isLast || !retryPredicate(result.error)) {
|
|
109
109
|
return { done: true, result };
|
|
110
110
|
}
|
|
111
|
-
const delay =
|
|
111
|
+
const delay = deriveBackoffDelay(attempt, options);
|
|
112
112
|
if (delay > 0) {
|
|
113
113
|
await sleep(delay, options?.signal);
|
|
114
114
|
}
|
|
@@ -203,10 +203,9 @@ export const withTimeout = <T>(
|
|
|
203
203
|
);
|
|
204
204
|
}, ms);
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
(result) => {
|
|
206
|
+
const run = async () => {
|
|
207
|
+
try {
|
|
208
|
+
const result = await fn();
|
|
210
209
|
if (settled) {
|
|
211
210
|
return;
|
|
212
211
|
}
|
|
@@ -215,9 +214,7 @@ export const withTimeout = <T>(
|
|
|
215
214
|
signal?.removeEventListener('abort', onAbort);
|
|
216
215
|
// oxlint-disable-next-line promise/no-multiple-resolved -- settled guard ensures single resolution
|
|
217
216
|
resolve(result);
|
|
218
|
-
}
|
|
219
|
-
// oxlint-disable-next-line prefer-await-to-callbacks -- rejection handler required inside .then()
|
|
220
|
-
(error: unknown) => {
|
|
217
|
+
} catch (error: unknown) {
|
|
221
218
|
if (settled) {
|
|
222
219
|
return;
|
|
223
220
|
}
|
|
@@ -229,6 +226,9 @@ export const withTimeout = <T>(
|
|
|
229
226
|
Result.err(error instanceof Error ? error : new Error(String(error)))
|
|
230
227
|
);
|
|
231
228
|
}
|
|
232
|
-
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// oxlint-disable-next-line no-void -- fire-and-settle inside Promise constructor
|
|
232
|
+
void run();
|
|
233
233
|
});
|
|
234
234
|
};
|