@ontrails/core 1.0.0-beta.15 → 1.0.0-beta.16
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 +63 -0
- package/README.md +61 -16
- package/package.json +9 -8
- 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/context.ts +4 -0
- package/src/{internal/cross-batch.ts → cross-batch.ts} +3 -11
- package/src/detours.ts +8 -0
- package/src/draft.ts +4 -0
- package/src/error-projection.ts +51 -0
- package/src/errors.ts +192 -39
- package/src/execute.ts +443 -44
- package/src/fire.ts +965 -91
- package/src/index.ts +285 -38
- package/src/layer-projection.ts +192 -0
- package/src/layer.ts +32 -1
- package/src/observe.ts +361 -0
- package/src/permits.ts +12 -0
- package/src/redaction/patterns.ts +6 -3
- package/src/resilience.ts +8 -8
- package/src/resource-config.ts +477 -44
- package/src/resource.ts +21 -1
- package/src/result.ts +18 -0
- package/src/run.ts +2 -2
- package/src/schedule-runtime.ts +689 -0
- package/src/schedule.ts +324 -0
- package/src/serialization.ts +90 -57
- package/src/signal-diagnostics.ts +633 -0
- package/src/{internal/signal-ref.ts → signal-ref.ts} +1 -1
- package/src/signal.ts +37 -0
- package/src/store/accessor-protocol.ts +5 -5
- package/src/structured-examples.ts +245 -0
- package/src/surface-derivation.ts +91 -0
- package/src/surface-filter.ts +7 -13
- package/src/topo.ts +386 -6
- package/src/tracing.ts +345 -0
- package/src/trail.ts +168 -22
- package/src/trails/derive-trail.ts +1 -1
- package/src/trails/ingest.ts +7 -15
- package/src/{internal/trails-db.ts → trails-db.ts} +42 -21
- package/src/transport-error-map.ts +146 -28
- package/src/types.ts +82 -28
- package/src/validate-established-topo.ts +6 -0
- package/src/validate-topo.ts +212 -5
- package/src/validation.ts +58 -1
- package/src/webhook.ts +276 -0
- package/src/{internal/zod-wrappers.ts → zod-wrappers.ts} +0 -6
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +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 -22
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -37
- package/dist/context.js.map +0 -1
- package/dist/contour.d.ts +0 -81
- package/dist/contour.d.ts.map +0 -1
- package/dist/contour.js +0 -176
- package/dist/contour.js.map +0 -1
- package/dist/cross-schema.d.ts +0 -18
- package/dist/cross-schema.d.ts.map +0 -1
- package/dist/cross-schema.js +0 -29
- package/dist/cross-schema.js.map +0 -1
- package/dist/derive.d.ts +0 -44
- package/dist/derive.d.ts.map +0 -1
- package/dist/derive.js +0 -149
- package/dist/derive.js.map +0 -1
- package/dist/draft.d.ts +0 -28
- package/dist/draft.d.ts.map +0 -1
- package/dist/draft.js +0 -157
- package/dist/draft.js.map +0 -1
- package/dist/errors.d.ts +0 -141
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -185
- package/dist/errors.js.map +0 -1
- package/dist/execute.d.ts +0 -52
- package/dist/execute.d.ts.map +0 -1
- package/dist/execute.js +0 -511
- package/dist/execute.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/fire.d.ts +0 -43
- package/dist/fire.d.ts.map +0 -1
- package/dist/fire.js +0 -185
- package/dist/fire.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/index.d.ts +0 -59
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -61
- package/dist/index.js.map +0 -1
- package/dist/internal/cross-batch.d.ts +0 -44
- package/dist/internal/cross-batch.d.ts.map +0 -1
- package/dist/internal/cross-batch.js +0 -58
- package/dist/internal/cross-batch.js.map +0 -1
- package/dist/internal/fork-ctx.d.ts +0 -41
- package/dist/internal/fork-ctx.d.ts.map +0 -1
- package/dist/internal/fork-ctx.js +0 -47
- package/dist/internal/fork-ctx.js.map +0 -1
- package/dist/internal/signal-ref.d.ts +0 -15
- package/dist/internal/signal-ref.d.ts.map +0 -1
- package/dist/internal/signal-ref.js +0 -41
- package/dist/internal/signal-ref.js.map +0 -1
- package/dist/internal/topo-saves.d.ts +0 -53
- package/dist/internal/topo-saves.d.ts.map +0 -1
- package/dist/internal/topo-saves.js +0 -449
- package/dist/internal/topo-saves.js.map +0 -1
- package/dist/internal/topo-snapshots.d.ts +0 -57
- package/dist/internal/topo-snapshots.d.ts.map +0 -1
- package/dist/internal/topo-snapshots.js +0 -372
- package/dist/internal/topo-snapshots.js.map +0 -1
- package/dist/internal/topo-store-read.d.ts +0 -70
- package/dist/internal/topo-store-read.d.ts.map +0 -1
- package/dist/internal/topo-store-read.js +0 -221
- package/dist/internal/topo-store-read.js.map +0 -1
- package/dist/internal/topo-store.d.ts +0 -26
- package/dist/internal/topo-store.d.ts.map +0 -1
- package/dist/internal/topo-store.js +0 -621
- package/dist/internal/topo-store.js.map +0 -1
- package/dist/internal/tracing.d.ts +0 -98
- package/dist/internal/tracing.d.ts.map +0 -1
- package/dist/internal/tracing.js +0 -102
- package/dist/internal/tracing.js.map +0 -1
- package/dist/internal/trails-db.d.ts +0 -16
- package/dist/internal/trails-db.d.ts.map +0 -1
- package/dist/internal/trails-db.js +0 -118
- package/dist/internal/trails-db.js.map +0 -1
- package/dist/internal/zod-wrappers.d.ts +0 -40
- package/dist/internal/zod-wrappers.d.ts.map +0 -1
- package/dist/internal/zod-wrappers.js +0 -59
- package/dist/internal/zod-wrappers.js.map +0 -1
- package/dist/layer.d.ts +0 -23
- 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/resource-config.d.ts +0 -22
- package/dist/resource-config.d.ts.map +0 -1
- package/dist/resource-config.js +0 -210
- package/dist/resource-config.js.map +0 -1
- package/dist/resource.d.ts +0 -74
- package/dist/resource.d.ts.map +0 -1
- package/dist/resource.js +0 -61
- package/dist/resource.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/run.d.ts +0 -28
- package/dist/run.d.ts.map +0 -1
- package/dist/run.js +0 -35
- package/dist/run.js.map +0 -1
- package/dist/serialization.d.ts +0 -27
- package/dist/serialization.d.ts.map +0 -1
- package/dist/serialization.js +0 -154
- package/dist/serialization.js.map +0 -1
- package/dist/signal.d.ts +0 -33
- package/dist/signal.d.ts.map +0 -1
- package/dist/signal.js +0 -17
- package/dist/signal.js.map +0 -1
- package/dist/store/accessor-protocol.d.ts +0 -51
- package/dist/store/accessor-protocol.d.ts.map +0 -1
- package/dist/store/accessor-protocol.js +0 -2
- package/dist/store/accessor-protocol.js.map +0 -1
- package/dist/store/index.d.ts +0 -2
- package/dist/store/index.d.ts.map +0 -1
- package/dist/store/index.js +0 -2
- package/dist/store/index.js.map +0 -1
- package/dist/surface-filter.d.ts +0 -13
- package/dist/surface-filter.d.ts.map +0 -1
- package/dist/surface-filter.js +0 -76
- package/dist/surface-filter.js.map +0 -1
- package/dist/topo-store.d.ts +0 -62
- package/dist/topo-store.d.ts.map +0 -1
- package/dist/topo-store.js +0 -319
- package/dist/topo-store.js.map +0 -1
- package/dist/topo.d.ts +0 -39
- package/dist/topo.d.ts.map +0 -1
- package/dist/topo.js +0 -241
- package/dist/topo.js.map +0 -1
- package/dist/trail.d.ts +0 -163
- package/dist/trail.d.ts.map +0 -1
- package/dist/trail.js +0 -75
- package/dist/trail.js.map +0 -1
- package/dist/trails/derive-trail.d.ts +0 -66
- package/dist/trails/derive-trail.d.ts.map +0 -1
- package/dist/trails/derive-trail.js +0 -378
- package/dist/trails/derive-trail.js.map +0 -1
- package/dist/trails/index.d.ts +0 -5
- package/dist/trails/index.d.ts.map +0 -1
- package/dist/trails/index.js +0 -3
- package/dist/trails/index.js.map +0 -1
- package/dist/trails/ingest.d.ts +0 -24
- package/dist/trails/ingest.d.ts.map +0 -1
- package/dist/trails/ingest.js +0 -63
- package/dist/trails/ingest.js.map +0 -1
- package/dist/transport-error-map.d.ts +0 -106
- package/dist/transport-error-map.d.ts.map +0 -1
- package/dist/transport-error-map.js +0 -24
- package/dist/transport-error-map.js.map +0 -1
- package/dist/type-checks.test-d.d.ts +0 -71
- package/dist/type-checks.test-d.d.ts.map +0 -1
- package/dist/type-checks.test-d.js +0 -12
- package/dist/type-checks.test-d.js.map +0 -1
- package/dist/type-utils.d.ts +0 -33
- package/dist/type-utils.d.ts.map +0 -1
- package/dist/type-utils.js +0 -12
- package/dist/type-utils.js.map +0 -1
- package/dist/types.d.ts +0 -177
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/dist/validate-established-topo.d.ts +0 -76
- package/dist/validate-established-topo.d.ts.map +0 -1
- package/dist/validate-established-topo.js +0 -43
- package/dist/validate-established-topo.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 -193
- 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 -165
- 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 -77
- package/src/__tests__/contour.test.ts +0 -263
- package/src/__tests__/derive-trail.test.ts +0 -668
- package/src/__tests__/derive.test.ts +0 -260
- package/src/__tests__/errors.test.ts +0 -427
- package/src/__tests__/execute.test.ts +0 -1563
- package/src/__tests__/fetch.test.ts +0 -217
- package/src/__tests__/fire.test.ts +0 -1056
- package/src/__tests__/fork-ctx.test.ts +0 -83
- package/src/__tests__/guards.test.ts +0 -102
- package/src/__tests__/ingest.test.ts +0 -191
- package/src/__tests__/layer.test.ts +0 -125
- 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__/resource.test.ts +0 -204
- package/src/__tests__/result.test.ts +0 -155
- package/src/__tests__/run.test.ts +0 -181
- package/src/__tests__/serialization.test.ts +0 -401
- package/src/__tests__/service-config.test.ts +0 -224
- package/src/__tests__/signal.test.ts +0 -82
- package/src/__tests__/surface-filter.test.ts +0 -196
- package/src/__tests__/topo-store-read.test.ts +0 -256
- package/src/__tests__/topo-store.test.ts +0 -869
- package/src/__tests__/topo.test.ts +0 -573
- package/src/__tests__/trail-permit.test.ts +0 -60
- package/src/__tests__/trail.test.ts +0 -426
- package/src/__tests__/trails-db.test.ts +0 -198
- package/src/__tests__/transport-error-map.test.ts +0 -82
- package/src/__tests__/type-utils.test.ts +0 -118
- package/src/__tests__/validate-topo.test.ts +0 -611
- package/src/__tests__/validation.test.ts +0 -336
- package/src/__tests__/workspace.test.ts +0 -183
- package/src/__tests__/zod-wrappers.test.ts +0 -62
- package/src/internal/topo-snapshots.ts +0 -517
- package/src/internal/topo-store-read.ts +0 -481
- package/src/internal/topo-store.ts +0 -1192
- package/src/internal/tracing.ts +0 -187
- package/src/topo-store.ts +0 -510
- package/src/type-checks.test-d.ts +0 -106
- package/tsconfig.json +0 -9
- package/tsconfig.tests.json +0 -10
- package/tsconfig.tsbuildinfo +0 -1
package/src/observe.ts
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { ValidationError } from './errors.js';
|
|
2
|
+
import type { TraceSink } from './tracing.js';
|
|
3
|
+
import type { Layer } from './layer.js';
|
|
4
|
+
import { safeStringify } from './serialization.js';
|
|
5
|
+
import type { Logger, LogLevel, LogRecord, LogSink } from './types.js';
|
|
6
|
+
|
|
7
|
+
export interface ObserveConfig {
|
|
8
|
+
readonly log?: Logger | LogSink | undefined;
|
|
9
|
+
readonly trace?: TraceSink | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ObserveCapabilities {
|
|
13
|
+
readonly log?: true | undefined;
|
|
14
|
+
readonly trace?: true | undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ObserveCapable {
|
|
18
|
+
readonly observes?: ObserveCapabilities | undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type ObserveInput = Logger | LogSink | TraceSink | ObserveConfig;
|
|
22
|
+
|
|
23
|
+
export interface TopoOptions {
|
|
24
|
+
readonly observe?: ObserveInput | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Typed layers attached at topo scope.
|
|
27
|
+
*
|
|
28
|
+
* Layers declared here wrap every trail invoked through this topo, on every
|
|
29
|
+
* surface. The execution pipeline composes topo-scope layers outermost —
|
|
30
|
+
* around surface-scope and trail-scope layers — so the final order is
|
|
31
|
+
* `topo → surface → trail → blaze` (outermost-first).
|
|
32
|
+
*/
|
|
33
|
+
readonly layers?: readonly Layer[] | undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const OBSERVE_CONFIG_KEYS = new Set(['log', 'trace']);
|
|
37
|
+
export const OBSERVE_LOGGER_CONTEXT_KEY = '__trails_observe_logger';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Context extension key that carries metadata accumulated on the observe
|
|
41
|
+
* logger across rebindings (e.g. signal fan-out metadata such as `consumerId`
|
|
42
|
+
* and `signalId`). When `applyTopoObserveContext` rebinds the logger for a
|
|
43
|
+
* new trail, it merges this metadata into the freshly built observe logger
|
|
44
|
+
* so consumer log records retain provenance back to the triggering signal.
|
|
45
|
+
*/
|
|
46
|
+
export const OBSERVE_LOGGER_METADATA_KEY = '__trails_observe_logger_metadata';
|
|
47
|
+
|
|
48
|
+
const isObject = (value: unknown): value is Record<string, unknown> =>
|
|
49
|
+
typeof value === 'object' && value !== null;
|
|
50
|
+
|
|
51
|
+
const hasFunction = (value: Record<string, unknown>, key: string): boolean =>
|
|
52
|
+
typeof value[key] === 'function';
|
|
53
|
+
|
|
54
|
+
export const isLogger = (value: unknown): value is Logger =>
|
|
55
|
+
isObject(value) &&
|
|
56
|
+
hasFunction(value, 'child') &&
|
|
57
|
+
hasFunction(value, 'debug') &&
|
|
58
|
+
hasFunction(value, 'error') &&
|
|
59
|
+
hasFunction(value, 'fatal') &&
|
|
60
|
+
hasFunction(value, 'info') &&
|
|
61
|
+
hasFunction(value, 'trace') &&
|
|
62
|
+
hasFunction(value, 'warn');
|
|
63
|
+
|
|
64
|
+
export const isLogSink = (value: unknown): value is LogSink =>
|
|
65
|
+
isObject(value) &&
|
|
66
|
+
typeof value['name'] === 'string' &&
|
|
67
|
+
hasFunction(value, 'write');
|
|
68
|
+
|
|
69
|
+
const readObserveCapabilities = (
|
|
70
|
+
value: unknown
|
|
71
|
+
): ObserveCapabilities | undefined => {
|
|
72
|
+
if (!isObject(value)) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
const capabilities = (value as ObserveCapable).observes;
|
|
76
|
+
if (!isObject(capabilities)) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
const log = capabilities['log'] === true;
|
|
80
|
+
const trace = capabilities['trace'] === true;
|
|
81
|
+
if (!log && !trace) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
return Object.freeze({
|
|
85
|
+
...(log ? { log: true as const } : {}),
|
|
86
|
+
...(trace ? { trace: true as const } : {}),
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const isTraceSink = (value: unknown): value is TraceSink =>
|
|
91
|
+
isObject(value) && hasFunction(value, 'write');
|
|
92
|
+
|
|
93
|
+
const isObserveConfigShape = (value: unknown): value is ObserveConfig => {
|
|
94
|
+
if (!isObject(value)) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const keys = Object.keys(value);
|
|
98
|
+
return (
|
|
99
|
+
keys.length > 0 &&
|
|
100
|
+
keys.every((key) => OBSERVE_CONFIG_KEYS.has(key)) &&
|
|
101
|
+
('log' in value || 'trace' in value)
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Type guard for the `ObserveInput` union. Returns `true` only for shapes
|
|
107
|
+
* that {@link normalizeObserve} will accept without throwing — i.e. a
|
|
108
|
+
* `Logger`, an explicit `ObserveConfig`, or a `TraceSink`. Capability-only
|
|
109
|
+
* payloads (`{ observes: { trace: true } }` without an accompanying
|
|
110
|
+
* `write` method) and bare `LogSink` shorthand are intentionally rejected:
|
|
111
|
+
* the former is metadata about a missing implementation, and the latter
|
|
112
|
+
* is ambiguous between a `LogSink` and a `TraceSink` and is rejected by
|
|
113
|
+
* `normalizeObserve` accordingly. Keeping the guard tighter than the
|
|
114
|
+
* runtime accepts would let callers narrow to `ObserveInput` and then
|
|
115
|
+
* see `normalizeObserve` throw at runtime.
|
|
116
|
+
*/
|
|
117
|
+
export const isObserveInput = (
|
|
118
|
+
value: unknown
|
|
119
|
+
): value is ObserveInput | undefined => {
|
|
120
|
+
if (value === undefined) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
if (isLogger(value)) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
if (isObserveConfigShape(value)) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
if (isTraceSink(value) && !isLogSink(value)) {
|
|
130
|
+
// A bare TraceSink (no `name`) is unambiguous — `normalizeObserve`
|
|
131
|
+
// routes it via the `isTraceSink` fallthrough. A LogSink shape is
|
|
132
|
+
// ambiguous (matches both guards) and would be rejected by
|
|
133
|
+
// `normalizeObserve`, so the guard rejects it here too.
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Returns true when `value` carries explicit observe capabilities via the
|
|
141
|
+
* `observes` discriminator. Used by the topo classifier to distinguish
|
|
142
|
+
* an `ObserveCapable` sink (clearly options) from a bare sink (ambiguous
|
|
143
|
+
* with a module export named `observe`).
|
|
144
|
+
*/
|
|
145
|
+
export const hasObserveCapabilities = (value: unknown): boolean =>
|
|
146
|
+
readObserveCapabilities(value) !== undefined;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Returns true when `value` is shaped like the explicit `{ log?, trace? }`
|
|
150
|
+
* `ObserveConfig` payload. Exposed for the topo classifier so a config-style
|
|
151
|
+
* trailing argument is unambiguously routed to options.
|
|
152
|
+
*/
|
|
153
|
+
export const isObserveConfig = (value: unknown): value is ObserveConfig =>
|
|
154
|
+
isObserveConfigShape(value);
|
|
155
|
+
|
|
156
|
+
const normalizeLogTarget = (
|
|
157
|
+
target: ObserveConfig['log']
|
|
158
|
+
): ObserveConfig['log'] => {
|
|
159
|
+
if (target === undefined || isLogger(target) || isLogSink(target)) {
|
|
160
|
+
return target;
|
|
161
|
+
}
|
|
162
|
+
throw new ValidationError('topo observe.log must be a Logger or LogSink');
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const normalizeTraceTarget = (
|
|
166
|
+
target: ObserveConfig['trace']
|
|
167
|
+
): ObserveConfig['trace'] => {
|
|
168
|
+
if (target === undefined || isTraceSink(target)) {
|
|
169
|
+
return target;
|
|
170
|
+
}
|
|
171
|
+
throw new ValidationError('topo observe.trace must be a TraceSink');
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Maps observe log levels to the `console` method that should receive the
|
|
176
|
+
* formatted record. `silent` is intentionally absent — records at that level
|
|
177
|
+
* are dropped before reaching `console`.
|
|
178
|
+
*/
|
|
179
|
+
const DEFAULT_SINK_CONSOLE_METHOD: Record<
|
|
180
|
+
LogLevel,
|
|
181
|
+
'debug' | 'error' | 'info' | 'warn' | undefined
|
|
182
|
+
> = {
|
|
183
|
+
debug: 'debug',
|
|
184
|
+
error: 'error',
|
|
185
|
+
fatal: 'error',
|
|
186
|
+
info: 'info',
|
|
187
|
+
silent: undefined,
|
|
188
|
+
trace: 'debug',
|
|
189
|
+
warn: 'warn',
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const stringifyDefaultConsoleRecord = (record: LogRecord): string => {
|
|
193
|
+
const serialized = safeStringify({
|
|
194
|
+
category: record.category,
|
|
195
|
+
level: record.level,
|
|
196
|
+
message: record.message,
|
|
197
|
+
metadata: record.metadata,
|
|
198
|
+
timestamp: record.timestamp.toISOString(),
|
|
199
|
+
});
|
|
200
|
+
if (serialized.isOk()) {
|
|
201
|
+
return serialized.value;
|
|
202
|
+
}
|
|
203
|
+
return JSON.stringify({
|
|
204
|
+
category: record.category,
|
|
205
|
+
level: record.level,
|
|
206
|
+
message: record.message,
|
|
207
|
+
metadata: '[unserializable]',
|
|
208
|
+
timestamp: record.timestamp.toISOString(),
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* In-core mirror of `@ontrails/observe`'s `createConsoleSink` shape, kept
|
|
214
|
+
* minimal and private to avoid a reverse dependency from `@ontrails/core`
|
|
215
|
+
* onto `@ontrails/observe`. It mirrors the console level routing in
|
|
216
|
+
* `packages/observe/src/sinks.ts:50` and emits each record as a single-line
|
|
217
|
+
* JSON object routed to the matching `console.{debug|info|warn|error}` method.
|
|
218
|
+
*
|
|
219
|
+
* @remarks
|
|
220
|
+
* Used as the default `observe.log` target when `topo()` is called without an
|
|
221
|
+
* explicit `observe` option, so every app gets a non-null `ctx.logger` with
|
|
222
|
+
* structured stdout output and zero configuration. Apps that want richer
|
|
223
|
+
* formatting, file destinations, or custom routing should pass an explicit
|
|
224
|
+
* `observe` option, which fully replaces this default.
|
|
225
|
+
*/
|
|
226
|
+
const createDefaultConsoleSink = (): LogSink => ({
|
|
227
|
+
name: 'console',
|
|
228
|
+
write(record): void {
|
|
229
|
+
const method = DEFAULT_SINK_CONSOLE_METHOD[record.level];
|
|
230
|
+
if (method === undefined) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const payload = stringifyDefaultConsoleRecord(record);
|
|
234
|
+
// oxlint-disable-next-line trails-local/no-console-in-packages -- ADR 0041 mandates a default console logger in core; this is the single sanctioned console boundary, mirroring `@ontrails/observe`'s `createConsoleSink`.
|
|
235
|
+
console[method](payload);
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The default observe configuration applied when `topo()` receives no
|
|
241
|
+
* `observe` option. Frozen so callers cannot mutate the shared default; the
|
|
242
|
+
* sink itself is shared across topos because it has no per-topo state.
|
|
243
|
+
*/
|
|
244
|
+
const DEFAULT_OBSERVE_CONFIG: ObserveConfig = Object.freeze({
|
|
245
|
+
log: createDefaultConsoleSink(),
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
export const normalizeObserve = (
|
|
249
|
+
observe: ObserveInput | undefined
|
|
250
|
+
): ObserveConfig | undefined => {
|
|
251
|
+
if (observe === undefined) {
|
|
252
|
+
// ADR 0041 promises a non-null `ctx.logger` with zero configuration.
|
|
253
|
+
// Returning the default config here lets the existing topo → adapter
|
|
254
|
+
// path project this log sink into `ctx.logger` without a second
|
|
255
|
+
// resolution point or a reverse dependency on `@ontrails/observe`.
|
|
256
|
+
return DEFAULT_OBSERVE_CONFIG;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const capabilities = readObserveCapabilities(observe);
|
|
260
|
+
if (capabilities !== undefined) {
|
|
261
|
+
const log =
|
|
262
|
+
capabilities.log === true
|
|
263
|
+
? normalizeLogTarget(observe as Logger | LogSink)
|
|
264
|
+
: undefined;
|
|
265
|
+
const trace =
|
|
266
|
+
capabilities.trace === true
|
|
267
|
+
? normalizeTraceTarget(observe as TraceSink)
|
|
268
|
+
: undefined;
|
|
269
|
+
return Object.freeze({
|
|
270
|
+
...(log === undefined ? {} : { log }),
|
|
271
|
+
...(trace === undefined ? {} : { trace }),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (isLogger(observe)) {
|
|
276
|
+
return Object.freeze({ log: observe });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (isObserveConfigShape(observe)) {
|
|
280
|
+
const log = normalizeLogTarget(observe.log);
|
|
281
|
+
const trace = normalizeTraceTarget(observe.trace);
|
|
282
|
+
if (log === undefined && trace === undefined) {
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
return Object.freeze({
|
|
286
|
+
...(log === undefined ? {} : { log }),
|
|
287
|
+
...(trace === undefined ? {} : { trace }),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (isLogSink(observe)) {
|
|
292
|
+
throw new ValidationError(
|
|
293
|
+
'topo observe shorthand is ambiguous for named sinks; use { log: sink } or { trace: sink }'
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (isTraceSink(observe)) {
|
|
298
|
+
return Object.freeze({ trace: observe });
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
throw new ValidationError(
|
|
302
|
+
'topo observe must be a Logger, LogSink, TraceSink, or { log, trace } object'
|
|
303
|
+
);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const createLogSinkLogger = (
|
|
307
|
+
sink: LogSink,
|
|
308
|
+
category: string,
|
|
309
|
+
baseMetadata: Record<string, unknown>
|
|
310
|
+
): Logger => {
|
|
311
|
+
const write = (
|
|
312
|
+
level: LogLevel,
|
|
313
|
+
message: string,
|
|
314
|
+
metadata?: Record<string, unknown>
|
|
315
|
+
): void => {
|
|
316
|
+
sink.write({
|
|
317
|
+
category,
|
|
318
|
+
level,
|
|
319
|
+
message,
|
|
320
|
+
metadata: { ...baseMetadata, ...metadata },
|
|
321
|
+
timestamp: new Date(),
|
|
322
|
+
});
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
child(metadata: Record<string, unknown>): Logger {
|
|
327
|
+
return createLogSinkLogger(sink, category, {
|
|
328
|
+
...baseMetadata,
|
|
329
|
+
...metadata,
|
|
330
|
+
});
|
|
331
|
+
},
|
|
332
|
+
debug(message, metadata): void {
|
|
333
|
+
write('debug', message, metadata);
|
|
334
|
+
},
|
|
335
|
+
error(message, metadata): void {
|
|
336
|
+
write('error', message, metadata);
|
|
337
|
+
},
|
|
338
|
+
fatal(message, metadata): void {
|
|
339
|
+
write('fatal', message, metadata);
|
|
340
|
+
},
|
|
341
|
+
info(message, metadata): void {
|
|
342
|
+
write('info', message, metadata);
|
|
343
|
+
},
|
|
344
|
+
name: category,
|
|
345
|
+
trace(message, metadata): void {
|
|
346
|
+
write('trace', message, metadata);
|
|
347
|
+
},
|
|
348
|
+
warn(message, metadata): void {
|
|
349
|
+
write('warn', message, metadata);
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
export const createObserveLogger = (
|
|
355
|
+
log: Logger | LogSink,
|
|
356
|
+
category: string,
|
|
357
|
+
metadata: Record<string, unknown>
|
|
358
|
+
): Logger =>
|
|
359
|
+
isLogger(log)
|
|
360
|
+
? log.child(metadata)
|
|
361
|
+
: createLogSinkLogger(log, category, metadata);
|
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
|
+
});
|
|
@@ -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
|
@@ -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
|
};
|