@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
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* allowing the derivation helper to call accessors by convention.
|
|
11
11
|
*
|
|
12
12
|
* Required methods (`get`, `list`, `upsert`, `remove`) match the
|
|
13
|
-
*
|
|
14
|
-
* methods (`insert`, `update`) are declared by tabular
|
|
13
|
+
* backend-agnostic write contract every bound store must expose. Optional
|
|
14
|
+
* methods (`insert`, `update`) are declared by tabular adapters that
|
|
15
15
|
* distinguish create-only and patch-only operations from the generalized
|
|
16
16
|
* `upsert` contract.
|
|
17
17
|
*/
|
|
@@ -25,7 +25,7 @@ export interface StoreAccessorProtocol<
|
|
|
25
25
|
get(id: TId): Promise<TEntity | null>;
|
|
26
26
|
/** List entities, optionally filtered. Returns `[]` when no rows match. */
|
|
27
27
|
list(filters?: TFilters): Promise<readonly TEntity[]>;
|
|
28
|
-
/** Create-or-replace one entity using the
|
|
28
|
+
/** Create-or-replace one entity using the backend-agnostic contract. */
|
|
29
29
|
upsert(input: TInput): Promise<TEntity>;
|
|
30
30
|
/**
|
|
31
31
|
* Remove an entity by identity. Returns `{ deleted: true }` when the row
|
|
@@ -34,13 +34,13 @@ export interface StoreAccessorProtocol<
|
|
|
34
34
|
*/
|
|
35
35
|
remove(id: TId): Promise<{ readonly deleted: boolean }>;
|
|
36
36
|
/**
|
|
37
|
-
* Optional insert — available on tabular
|
|
37
|
+
* Optional insert — available on tabular adapters that distinguish
|
|
38
38
|
* create from update. When absent, synthesized blazes fall back to
|
|
39
39
|
* `upsert`.
|
|
40
40
|
*/
|
|
41
41
|
insert?(input: TInput): Promise<TEntity>;
|
|
42
42
|
/**
|
|
43
|
-
* Optional patch-by-identity — available on tabular
|
|
43
|
+
* Optional patch-by-identity — available on tabular adapters. Returns
|
|
44
44
|
* `null` when no row with the given identity exists. When absent,
|
|
45
45
|
* synthesized update blazes fall back to `get` + merge + `upsert`.
|
|
46
46
|
*/
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import type { TrailExample, TrailExampleSignalAssertion } from './trail.js';
|
|
2
|
+
|
|
3
|
+
export interface StructuredTrailExampleProvenance {
|
|
4
|
+
readonly source: 'trail.examples';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface StructuredSignalExampleProvenance {
|
|
8
|
+
readonly source: 'signal.examples';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type StructuredTrailExampleKind = 'success' | 'error';
|
|
12
|
+
|
|
13
|
+
export interface StructuredTrailExample {
|
|
14
|
+
readonly description?: string | undefined;
|
|
15
|
+
readonly error?: string | undefined;
|
|
16
|
+
readonly expected?: unknown | undefined;
|
|
17
|
+
readonly expectedMatch?: unknown | undefined;
|
|
18
|
+
readonly input: unknown;
|
|
19
|
+
readonly kind: StructuredTrailExampleKind;
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly provenance: StructuredTrailExampleProvenance;
|
|
22
|
+
readonly signals?:
|
|
23
|
+
| readonly StructuredTrailExampleSignalAssertion[]
|
|
24
|
+
| undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface StructuredTrailExampleSignalAssertion {
|
|
28
|
+
readonly payload?: unknown | undefined;
|
|
29
|
+
readonly payloadMatch?: unknown | undefined;
|
|
30
|
+
readonly signalId: string;
|
|
31
|
+
readonly times?: number | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface StructuredSignalExample {
|
|
35
|
+
readonly kind: 'payload';
|
|
36
|
+
readonly payload: unknown;
|
|
37
|
+
readonly provenance: StructuredSignalExampleProvenance;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// `Date`, `RegExp`, `Map`, and `Set` are objects (`typeof === 'object'`),
|
|
41
|
+
// so they would pass the structural walk and reach `JSON.stringify`, which
|
|
42
|
+
// silently coerces them: a `Date` becomes its ISO string, a `RegExp` and
|
|
43
|
+
// any `Map`/`Set` become `{}`. Either way the projected shape diverges
|
|
44
|
+
// from the example author's declared input. Treat them as non-serializable
|
|
45
|
+
// leaves so the example is dropped rather than misrepresented to MCP
|
|
46
|
+
// clients.
|
|
47
|
+
const isNonJsonLeaf = (value: unknown): boolean => {
|
|
48
|
+
const kind = typeof value;
|
|
49
|
+
if (kind === 'function' || kind === 'symbol' || kind === 'bigint') {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return (
|
|
53
|
+
value instanceof Date ||
|
|
54
|
+
value instanceof RegExp ||
|
|
55
|
+
value instanceof Map ||
|
|
56
|
+
value instanceof Set
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// `JSON.stringify` silently drops function / symbol property values rather than
|
|
61
|
+
// throwing, so we walk the value first and reject any example whose graph
|
|
62
|
+
// contains a non-serializable leaf. Without this, an MCP client consuming
|
|
63
|
+
// `ontrails/examples` would receive structurally incorrect inputs.
|
|
64
|
+
const containsNonSerializableLeaf = (
|
|
65
|
+
value: unknown,
|
|
66
|
+
seen: WeakSet<object>
|
|
67
|
+
): boolean => {
|
|
68
|
+
if (isNonJsonLeaf(value)) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (value === null || typeof value !== 'object') {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
const obj = value as object;
|
|
75
|
+
if (seen.has(obj)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
seen.add(obj);
|
|
79
|
+
if (Array.isArray(value)) {
|
|
80
|
+
return value.some((entry) => containsNonSerializableLeaf(entry, seen));
|
|
81
|
+
}
|
|
82
|
+
return Object.values(obj).some((entry) =>
|
|
83
|
+
containsNonSerializableLeaf(entry, seen)
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const toJsonSerializable = (value: unknown): unknown | undefined => {
|
|
88
|
+
if (containsNonSerializableLeaf(value, new WeakSet<object>())) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const encoded = JSON.stringify(value);
|
|
93
|
+
return encoded === undefined ? undefined : JSON.parse(encoded);
|
|
94
|
+
} catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const signalIdFromAssertion = (
|
|
100
|
+
assertion: TrailExampleSignalAssertion
|
|
101
|
+
): string | undefined => {
|
|
102
|
+
if (typeof assertion.signal === 'string') {
|
|
103
|
+
return assertion.signal;
|
|
104
|
+
}
|
|
105
|
+
return typeof assertion.signal.id === 'string'
|
|
106
|
+
? assertion.signal.id
|
|
107
|
+
: undefined;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const projectSignalAssertion = (
|
|
111
|
+
assertion: TrailExampleSignalAssertion
|
|
112
|
+
): StructuredTrailExampleSignalAssertion | undefined => {
|
|
113
|
+
const signalId = signalIdFromAssertion(assertion);
|
|
114
|
+
if (signalId === undefined) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const projected: Record<string, unknown> = { signalId };
|
|
119
|
+
if (assertion.payload !== undefined) {
|
|
120
|
+
const payload = toJsonSerializable(assertion.payload);
|
|
121
|
+
if (payload === undefined) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
projected['payload'] = payload;
|
|
125
|
+
}
|
|
126
|
+
if (assertion.payloadMatch !== undefined) {
|
|
127
|
+
const payloadMatch = toJsonSerializable(assertion.payloadMatch);
|
|
128
|
+
if (payloadMatch === undefined) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
projected['payloadMatch'] = payloadMatch;
|
|
132
|
+
}
|
|
133
|
+
if (assertion.times !== undefined) {
|
|
134
|
+
projected['times'] = assertion.times;
|
|
135
|
+
}
|
|
136
|
+
return Object.freeze(
|
|
137
|
+
projected
|
|
138
|
+
) as unknown as StructuredTrailExampleSignalAssertion;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const projectSignalAssertions = (
|
|
142
|
+
assertions: readonly TrailExampleSignalAssertion[] | undefined
|
|
143
|
+
): readonly StructuredTrailExampleSignalAssertion[] | undefined => {
|
|
144
|
+
if (assertions === undefined) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
const projected = assertions.map(projectSignalAssertion);
|
|
148
|
+
if (projected.some((assertion) => assertion === undefined)) {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
return Object.freeze(projected as StructuredTrailExampleSignalAssertion[]);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const projectExample = (
|
|
155
|
+
example: TrailExample<unknown, unknown>
|
|
156
|
+
): StructuredTrailExample | undefined => {
|
|
157
|
+
const input = toJsonSerializable(example.input);
|
|
158
|
+
if (input === undefined) {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const projected: Record<string, unknown> = {
|
|
163
|
+
input,
|
|
164
|
+
kind: example.error === undefined ? 'success' : 'error',
|
|
165
|
+
name: example.name,
|
|
166
|
+
provenance: { source: 'trail.examples' },
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
if (example.description !== undefined) {
|
|
170
|
+
projected['description'] = example.description;
|
|
171
|
+
}
|
|
172
|
+
if (example.expected !== undefined) {
|
|
173
|
+
const expected = toJsonSerializable(example.expected);
|
|
174
|
+
if (expected === undefined) {
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
projected['expected'] = expected;
|
|
178
|
+
}
|
|
179
|
+
if (example.expectedMatch !== undefined) {
|
|
180
|
+
const expectedMatch = toJsonSerializable(example.expectedMatch);
|
|
181
|
+
if (expectedMatch === undefined) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
projected['expectedMatch'] = expectedMatch;
|
|
185
|
+
}
|
|
186
|
+
if (example.error !== undefined) {
|
|
187
|
+
projected['error'] = example.error;
|
|
188
|
+
}
|
|
189
|
+
if (example.signals !== undefined) {
|
|
190
|
+
const signals = projectSignalAssertions(example.signals);
|
|
191
|
+
if (signals === undefined) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
projected['signals'] = signals;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return Object.freeze(projected) as unknown as StructuredTrailExample;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const projectSignalExample = (
|
|
201
|
+
payload: unknown
|
|
202
|
+
): StructuredSignalExample | undefined => {
|
|
203
|
+
const serializablePayload = toJsonSerializable(payload);
|
|
204
|
+
if (serializablePayload === undefined) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return Object.freeze({
|
|
209
|
+
kind: 'payload',
|
|
210
|
+
payload: serializablePayload,
|
|
211
|
+
provenance: { source: 'signal.examples' },
|
|
212
|
+
} satisfies StructuredSignalExample);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
export const deriveStructuredTrailExamples = (
|
|
216
|
+
examples: readonly TrailExample<unknown, unknown>[] | undefined
|
|
217
|
+
): readonly StructuredTrailExample[] | undefined => {
|
|
218
|
+
if (examples === undefined || examples.length === 0) {
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const projected = examples
|
|
223
|
+
.map(projectExample)
|
|
224
|
+
.filter(
|
|
225
|
+
(example): example is StructuredTrailExample => example !== undefined
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
return projected.length > 0 ? Object.freeze(projected) : undefined;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export const deriveStructuredSignalExamples = (
|
|
232
|
+
examples: readonly unknown[] | undefined
|
|
233
|
+
): readonly StructuredSignalExample[] | undefined => {
|
|
234
|
+
if (examples === undefined || examples.length === 0) {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const projected = examples
|
|
239
|
+
.map(projectSignalExample)
|
|
240
|
+
.filter(
|
|
241
|
+
(example): example is StructuredSignalExample => example !== undefined
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
return projected.length > 0 ? Object.freeze(projected) : undefined;
|
|
245
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Result } from './result.js';
|
|
2
|
+
import type { Layer } from './layer.js';
|
|
3
|
+
import type { Intent } from './trail.js';
|
|
4
|
+
import type { Topo } from './topo.js';
|
|
5
|
+
import type { SurfaceName } from './transport-error-map.js';
|
|
6
|
+
import type { TrailContextInit } from './types.js';
|
|
7
|
+
import { SURFACE_KEY, SURFACE_LAYER_NAMES_KEY } from './types.js';
|
|
8
|
+
import { validateEstablishedTopo } from './validate-established-topo.js';
|
|
9
|
+
|
|
10
|
+
export type SurfaceConfigValues = Readonly<
|
|
11
|
+
Record<string, Record<string, unknown>>
|
|
12
|
+
>;
|
|
13
|
+
|
|
14
|
+
export interface SurfaceSelectionOptions {
|
|
15
|
+
/** Glob patterns that remove matching trail IDs. */
|
|
16
|
+
readonly exclude?: readonly string[] | undefined;
|
|
17
|
+
/** Glob patterns that keep only matching trail IDs when provided. */
|
|
18
|
+
readonly include?: readonly string[] | undefined;
|
|
19
|
+
/** Allowed intents for exposed surfaces. Empty arrays act as no filter. */
|
|
20
|
+
readonly intent?: readonly Intent[] | undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SurfaceValidationOptions {
|
|
24
|
+
/** Set to `false` to skip established-topo validation during projection. */
|
|
25
|
+
readonly validate?: boolean | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface BaseSurfaceOptions
|
|
29
|
+
extends SurfaceSelectionOptions, SurfaceValidationOptions {
|
|
30
|
+
/** Config values for resources that declare a `config` schema, keyed by resource ID. */
|
|
31
|
+
readonly configValues?: SurfaceConfigValues | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const shouldValidateSurfaceTopo = (
|
|
35
|
+
options?: SurfaceValidationOptions
|
|
36
|
+
): boolean => options?.validate !== false;
|
|
37
|
+
|
|
38
|
+
export const validateSurfaceTopo = (
|
|
39
|
+
graph: Topo,
|
|
40
|
+
options?: SurfaceValidationOptions
|
|
41
|
+
): Result<void, Error> => {
|
|
42
|
+
if (!shouldValidateSurfaceTopo(options)) {
|
|
43
|
+
return Result.ok();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const validated = validateEstablishedTopo(graph);
|
|
47
|
+
return validated.isErr() ? Result.err(validated.error) : Result.ok();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type SurfaceMarkedContext = Partial<TrailContextInit> & {
|
|
51
|
+
readonly extensions: Readonly<Record<string, unknown>>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const withSurfaceMarker = (
|
|
55
|
+
surface: SurfaceName,
|
|
56
|
+
ctx: Partial<TrailContextInit> = {}
|
|
57
|
+
): SurfaceMarkedContext => ({
|
|
58
|
+
...ctx,
|
|
59
|
+
extensions: {
|
|
60
|
+
...ctx.extensions,
|
|
61
|
+
[SURFACE_KEY]: surface,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const readSurfaceLayerNameRecord = (
|
|
66
|
+
value: unknown
|
|
67
|
+
): Record<string, readonly string[]> =>
|
|
68
|
+
value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
69
|
+
? (value as Record<string, readonly string[]>)
|
|
70
|
+
: {};
|
|
71
|
+
|
|
72
|
+
export const withSurfaceLayerNames = (
|
|
73
|
+
surface: SurfaceName,
|
|
74
|
+
layers: readonly Layer[],
|
|
75
|
+
ctx: Partial<TrailContextInit> = {}
|
|
76
|
+
): SurfaceMarkedContext => {
|
|
77
|
+
const existing = readSurfaceLayerNameRecord(
|
|
78
|
+
ctx.extensions?.[SURFACE_LAYER_NAMES_KEY]
|
|
79
|
+
);
|
|
80
|
+
return {
|
|
81
|
+
...ctx,
|
|
82
|
+
extensions: {
|
|
83
|
+
...ctx.extensions,
|
|
84
|
+
[SURFACE_KEY]: surface,
|
|
85
|
+
[SURFACE_LAYER_NAMES_KEY]: {
|
|
86
|
+
...existing,
|
|
87
|
+
[surface]: layers.map((layer) => layer.name),
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
};
|
package/src/surface-filter.ts
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import type { Intent, Trail } from './trail.js';
|
|
2
|
+
import type { SurfaceSelectionOptions } from './surface-derivation.js';
|
|
2
3
|
|
|
3
4
|
// ---------------------------------------------------------------------------
|
|
4
5
|
// Public types
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
6
7
|
|
|
7
|
-
export
|
|
8
|
-
/** Glob patterns that keep only matching trail IDs when provided. */
|
|
9
|
-
readonly include?: readonly string[] | undefined;
|
|
10
|
-
/** Glob patterns that remove matching trail IDs. */
|
|
11
|
-
readonly exclude?: readonly string[] | undefined;
|
|
12
|
-
/** Allowed intents for exposed trailheads. Empty arrays act as no filter. */
|
|
13
|
-
readonly intent?: readonly Intent[] | undefined;
|
|
14
|
-
}
|
|
8
|
+
export type SurfaceFilterOptions = SurfaceSelectionOptions;
|
|
15
9
|
|
|
16
10
|
// ---------------------------------------------------------------------------
|
|
17
11
|
// Glob matching
|
|
@@ -93,7 +87,7 @@ export const matchesTrailPattern = (
|
|
|
93
87
|
};
|
|
94
88
|
|
|
95
89
|
// ---------------------------------------------------------------------------
|
|
96
|
-
//
|
|
90
|
+
// Surface filtering
|
|
97
91
|
// ---------------------------------------------------------------------------
|
|
98
92
|
|
|
99
93
|
const matchesAnyPattern = (
|
|
@@ -114,7 +108,7 @@ const isExplicitInternalInclude = (
|
|
|
114
108
|
* Returns `'internal'` when either the explicit `visibility` field is
|
|
115
109
|
* `'internal'` or the legacy `meta.internal === true` convention is set.
|
|
116
110
|
* Honoring the legacy flag keeps trails authored before the visibility
|
|
117
|
-
* field was introduced (e.g. `meta: { internal: true }`) off
|
|
111
|
+
* field was introduced (e.g. `meta: { internal: true }`) off surfaces.
|
|
118
112
|
*
|
|
119
113
|
* The runtime always fills in `visibility: 'public'` when the spec did not
|
|
120
114
|
* declare it, so we cannot distinguish explicit `'public'` from the default.
|
|
@@ -132,7 +126,7 @@ const effectiveVisibility = (
|
|
|
132
126
|
return trail.meta?.['internal'] === true ? 'internal' : 'public';
|
|
133
127
|
};
|
|
134
128
|
|
|
135
|
-
const
|
|
129
|
+
const isVisibleToSurfaces = (
|
|
136
130
|
trail: Trail<unknown, unknown, unknown>,
|
|
137
131
|
include: readonly string[] | undefined
|
|
138
132
|
): boolean =>
|
|
@@ -157,12 +151,12 @@ export const shouldIncludeTrailForSurface = (
|
|
|
157
151
|
trail: Trail<unknown, unknown, unknown>,
|
|
158
152
|
options: SurfaceFilterOptions = {}
|
|
159
153
|
): boolean => {
|
|
160
|
-
if (trail.
|
|
154
|
+
if (trail.activationSources.length > 0) {
|
|
161
155
|
return false;
|
|
162
156
|
}
|
|
163
157
|
|
|
164
158
|
const { exclude, include, intent } = options;
|
|
165
|
-
if (!
|
|
159
|
+
if (!isVisibleToSurfaces(trail, include)) {
|
|
166
160
|
return false;
|
|
167
161
|
}
|
|
168
162
|
|