@ontrails/core 1.0.0-beta.11 → 1.0.0-beta.13
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/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +67 -30
- package/README.md +19 -19
- package/dist/context.d.ts +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +5 -4
- package/dist/context.js.map +1 -1
- package/dist/derive.d.ts +8 -3
- package/dist/derive.d.ts.map +1 -1
- package/dist/derive.js +7 -7
- package/dist/derive.js.map +1 -1
- package/dist/event.d.ts +4 -41
- package/dist/event.d.ts.map +1 -1
- package/dist/event.js +4 -14
- package/dist/event.js.map +1 -1
- package/dist/execute.d.ts +11 -9
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +34 -151
- package/dist/execute.js.map +1 -1
- package/dist/gate.d.ts +17 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +21 -0
- package/dist/gate.js.map +1 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/layer.d.ts +5 -16
- package/dist/layer.d.ts.map +1 -1
- package/dist/layer.js +3 -20
- package/dist/layer.js.map +1 -1
- package/dist/provision-config.d.ts +22 -0
- package/dist/provision-config.d.ts.map +1 -0
- package/dist/provision-config.js +210 -0
- package/dist/provision-config.js.map +1 -0
- package/dist/provision.d.ts +71 -0
- package/dist/provision.d.ts.map +1 -0
- package/dist/provision.js +56 -0
- package/dist/provision.js.map +1 -0
- package/dist/run.d.ts +27 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +34 -0
- package/dist/run.js.map +1 -0
- package/dist/service-config.d.ts +22 -0
- package/dist/service-config.d.ts.map +1 -0
- package/dist/service-config.js +210 -0
- package/dist/service-config.js.map +1 -0
- package/dist/service.d.ts +39 -37
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +21 -21
- package/dist/service.js.map +1 -1
- package/dist/signal.d.ts +33 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/signal.js +17 -0
- package/dist/signal.js.map +1 -0
- package/dist/topo.d.ts +10 -10
- package/dist/topo.d.ts.map +1 -1
- package/dist/topo.js +48 -35
- package/dist/topo.js.map +1 -1
- package/dist/trail.d.ts +19 -17
- package/dist/trail.d.ts.map +1 -1
- package/dist/trail.js +5 -4
- package/dist/trail.js.map +1 -1
- package/dist/types.d.ts +26 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -1
- package/dist/validate-topo.d.ts +2 -2
- package/dist/validate-topo.js +31 -31
- package/dist/validate-topo.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/context.test.ts +8 -8
- package/src/__tests__/execute.test.ts +124 -122
- package/src/__tests__/{layer.test.ts → gate.test.ts} +26 -26
- package/src/__tests__/{dispatch.test.ts → run.test.ts} +39 -39
- package/src/__tests__/service-config.test.ts +228 -0
- package/src/__tests__/service.test.ts +71 -71
- package/src/__tests__/{event.test.ts → signal.test.ts} +15 -15
- package/src/__tests__/topo.test.ts +54 -54
- package/src/__tests__/trail-permit.test.ts +60 -0
- package/src/__tests__/trail.test.ts +58 -58
- package/src/__tests__/type-utils.test.ts +3 -3
- package/src/__tests__/validate-topo.test.ts +38 -38
- package/src/context.ts +5 -4
- package/src/derive.ts +8 -8
- package/src/event.ts +11 -73
- package/src/execute.ts +59 -238
- package/src/{layer.ts → gate.ts} +13 -13
- package/src/index.ts +26 -21
- package/src/provision-config.ts +373 -0
- package/src/provision.ts +148 -0
- package/src/{dispatch.ts → run.ts} +8 -8
- package/src/signal.ts +65 -0
- package/src/topo.ts +74 -52
- package/src/trail.ts +30 -23
- package/src/types.ts +30 -10
- package/src/validate-topo.ts +33 -33
- package/tsconfig.tsbuildinfo +1 -1
- package/src/service.ts +0 -139
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provision resolution pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from execute.ts to keep both modules under the 400 LOC ceiling.
|
|
5
|
+
* Handles config validation, singleton caching, concurrent-creation dedup,
|
|
6
|
+
* and the full resolve-or-create flow for declared provisions.
|
|
7
|
+
*/
|
|
8
|
+
import { InternalError, ValidationError } from './errors.js';
|
|
9
|
+
import { Result } from './result.js';
|
|
10
|
+
import { createProvisionLookup } from './service.js';
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Singleton caches
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
const singletonProvisions = new WeakMap();
|
|
15
|
+
/** In-flight provision creation promises, keyed by provision x context. */
|
|
16
|
+
const pendingCreations = new WeakMap();
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Context helpers
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
const toProvisionContext = (ctx, config) => ({
|
|
21
|
+
config,
|
|
22
|
+
cwd: ctx.cwd,
|
|
23
|
+
env: ctx.env,
|
|
24
|
+
workspaceRoot: ctx.workspaceRoot,
|
|
25
|
+
});
|
|
26
|
+
const toProvisionContextKey = (ctx) => JSON.stringify({
|
|
27
|
+
config: ctx.config,
|
|
28
|
+
cwd: ctx.cwd,
|
|
29
|
+
env: Object.entries(ctx.env ?? {}).toSorted(([left], [right]) => left.localeCompare(right)),
|
|
30
|
+
workspaceRoot: ctx.workspaceRoot,
|
|
31
|
+
});
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Config validation
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
/** Validate and resolve a provision's config from the provided configValues map. */
|
|
36
|
+
const resolveProvisionConfig = (declaredProvision, configValues) => {
|
|
37
|
+
if (declaredProvision.config === undefined) {
|
|
38
|
+
return Result.ok();
|
|
39
|
+
}
|
|
40
|
+
const raw = configValues?.[declaredProvision.id];
|
|
41
|
+
if (raw === undefined) {
|
|
42
|
+
return Result.err(new ValidationError(`Provision "${declaredProvision.id}" declares a config schema but no config was provided`));
|
|
43
|
+
}
|
|
44
|
+
const parsed = declaredProvision.config.safeParse(raw);
|
|
45
|
+
if (!parsed.success) {
|
|
46
|
+
return Result.err(new ValidationError(`Provision "${declaredProvision.id}" config validation failed: ${parsed.error.message}`));
|
|
47
|
+
}
|
|
48
|
+
return Result.ok(parsed.data);
|
|
49
|
+
};
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Override / cache lookups
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
const hasOwnProvisionOverride = (overrides, id) => overrides !== undefined && Object.hasOwn(overrides, id);
|
|
54
|
+
const getCachedSingletonProvision = (declaredProvision, provisionContext) => {
|
|
55
|
+
const scopedCache = singletonProvisions.get(declaredProvision);
|
|
56
|
+
if (scopedCache === undefined) {
|
|
57
|
+
return { found: false, value: undefined };
|
|
58
|
+
}
|
|
59
|
+
const key = toProvisionContextKey(provisionContext);
|
|
60
|
+
if (!scopedCache.has(key)) {
|
|
61
|
+
return { found: false, value: undefined };
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
found: true,
|
|
65
|
+
value: scopedCache.get(key),
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
const getProvidedProvision = (ctx, overrides, declaredProvision, provisionContext) => {
|
|
69
|
+
const { id } = declaredProvision;
|
|
70
|
+
if (hasOwnProvisionOverride(overrides, id)) {
|
|
71
|
+
return Result.ok(overrides[id]);
|
|
72
|
+
}
|
|
73
|
+
if (Object.hasOwn(ctx.extensions ?? {}, id)) {
|
|
74
|
+
return Result.ok(ctx.extensions?.[id]);
|
|
75
|
+
}
|
|
76
|
+
const cached = getCachedSingletonProvision(declaredProvision, provisionContext);
|
|
77
|
+
if (cached.found) {
|
|
78
|
+
return Result.ok(cached.value);
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
};
|
|
82
|
+
const getOverrideOrExtension = (ctx, overrides, declaredProvision) => getProvidedProvision(ctx, overrides, declaredProvision, toProvisionContext(ctx));
|
|
83
|
+
const resolveConfigAwareProvidedProvision = (ctx, declaredProvision, configValues) => {
|
|
84
|
+
const configResult = resolveProvisionConfig(declaredProvision, configValues);
|
|
85
|
+
if (configResult.isErr()) {
|
|
86
|
+
return configResult;
|
|
87
|
+
}
|
|
88
|
+
const provisionContext = toProvisionContext(ctx, configResult.value);
|
|
89
|
+
const provided = getProvidedProvision(ctx, undefined, declaredProvision, provisionContext);
|
|
90
|
+
return provided
|
|
91
|
+
? Result.ok({ kind: 'provided', value: provided.unwrap() })
|
|
92
|
+
: Result.ok({ kind: 'context', provisionContext });
|
|
93
|
+
};
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Instance creation
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
const toInternalProvisionError = (id, error) => {
|
|
98
|
+
const cause = error instanceof Error ? error : undefined;
|
|
99
|
+
const message = cause?.message ?? String(error);
|
|
100
|
+
return new InternalError(`Provision "${id}" failed to resolve: ${message}`, {
|
|
101
|
+
...(cause ? { cause } : {}),
|
|
102
|
+
context: { provisionId: id },
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
const getSingletonProvisionCache = (declaredProvision) => {
|
|
106
|
+
const existing = singletonProvisions.get(declaredProvision);
|
|
107
|
+
if (existing !== undefined) {
|
|
108
|
+
return existing;
|
|
109
|
+
}
|
|
110
|
+
const created = new Map();
|
|
111
|
+
singletonProvisions.set(declaredProvision, created);
|
|
112
|
+
return created;
|
|
113
|
+
};
|
|
114
|
+
const doCreateProvisionInstance = async (declaredProvision, provisionContext) => {
|
|
115
|
+
try {
|
|
116
|
+
const created = await declaredProvision.create(provisionContext);
|
|
117
|
+
if (created.isErr()) {
|
|
118
|
+
return Result.err(created.error);
|
|
119
|
+
}
|
|
120
|
+
const instance = created.unwrap();
|
|
121
|
+
getSingletonProvisionCache(declaredProvision).set(toProvisionContextKey(provisionContext), instance);
|
|
122
|
+
return Result.ok(instance);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
return Result.err(toInternalProvisionError(declaredProvision.id, error));
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const trackPendingCreation = (declaredProvision, key, promise) => {
|
|
129
|
+
const pending = pendingCreations.get(declaredProvision);
|
|
130
|
+
if (pending) {
|
|
131
|
+
pending.set(key, promise);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
pendingCreations.set(declaredProvision, new Map([[key, promise]]));
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Deduplicates concurrent creation of the same provision singleton.
|
|
139
|
+
* If a creation is already in flight for this provision x context key,
|
|
140
|
+
* returns the existing promise instead of spawning a second factory call.
|
|
141
|
+
*/
|
|
142
|
+
const createProvisionInstance = async (declaredProvision, provisionContext) => {
|
|
143
|
+
const key = toProvisionContextKey(provisionContext);
|
|
144
|
+
const inflight = pendingCreations.get(declaredProvision)?.get(key);
|
|
145
|
+
if (inflight) {
|
|
146
|
+
return inflight;
|
|
147
|
+
}
|
|
148
|
+
const promise = doCreateProvisionInstance(declaredProvision, provisionContext);
|
|
149
|
+
trackPendingCreation(declaredProvision, key, promise);
|
|
150
|
+
try {
|
|
151
|
+
return await promise;
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
pendingCreations.get(declaredProvision)?.delete(key);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
/** Validate config and resolve a single declared provision. */
|
|
158
|
+
const resolveDeclaredProvision = async (declaredProvision, ctx, overrides, configValues) => {
|
|
159
|
+
// Check overrides/extensions first — skip config validation entirely when
|
|
160
|
+
// a provision instance is already provided.
|
|
161
|
+
const overrideOrExtension = getOverrideOrExtension(ctx, overrides, declaredProvision);
|
|
162
|
+
if (overrideOrExtension !== undefined) {
|
|
163
|
+
return overrideOrExtension;
|
|
164
|
+
}
|
|
165
|
+
// Resolve config before consulting the singleton cache so config-aware
|
|
166
|
+
// provisions use the same canonical context for cache reads and writes.
|
|
167
|
+
const configAwareProvision = resolveConfigAwareProvidedProvision(ctx, declaredProvision, configValues);
|
|
168
|
+
if (configAwareProvision.isErr()) {
|
|
169
|
+
return configAwareProvision;
|
|
170
|
+
}
|
|
171
|
+
// No provided instance — create via factory.
|
|
172
|
+
const resolved = configAwareProvision.unwrap();
|
|
173
|
+
if (resolved.kind === 'provided') {
|
|
174
|
+
return Result.ok(resolved.value);
|
|
175
|
+
}
|
|
176
|
+
return await createProvisionInstance(declaredProvision, resolved.provisionContext);
|
|
177
|
+
};
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
// Full trail provision resolution
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
const withResolvedProvisions = (ctx, resolvedProvisions) => {
|
|
182
|
+
const extensions = { ...ctx.extensions, ...resolvedProvisions };
|
|
183
|
+
const resolvedCtx = { ...ctx, extensions };
|
|
184
|
+
const lookup = createProvisionLookup(() => resolvedCtx);
|
|
185
|
+
resolvedCtx.provision = lookup;
|
|
186
|
+
return resolvedCtx;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Resolve all declared provisions for a trail.
|
|
190
|
+
*
|
|
191
|
+
* Validates per-provision config, checks overrides and caches, and creates
|
|
192
|
+
* new instances as needed. Returns an enriched context with all provision
|
|
193
|
+
* instances injected into extensions.
|
|
194
|
+
*/
|
|
195
|
+
export const resolveProvisions = async (trail, ctx, overrides, configValues) => {
|
|
196
|
+
const provisions = trail.provisions;
|
|
197
|
+
if (provisions.length === 0) {
|
|
198
|
+
return Result.ok(ctx);
|
|
199
|
+
}
|
|
200
|
+
const resolvedProvisions = {};
|
|
201
|
+
for (const declaredProvision of provisions) {
|
|
202
|
+
const resolved = await resolveDeclaredProvision(declaredProvision, ctx, overrides, configValues);
|
|
203
|
+
if (resolved.isErr()) {
|
|
204
|
+
return resolved;
|
|
205
|
+
}
|
|
206
|
+
resolvedProvisions[declaredProvision.id] = resolved.unwrap();
|
|
207
|
+
}
|
|
208
|
+
return Result.ok(withResolvedProvisions(ctx, resolvedProvisions));
|
|
209
|
+
};
|
|
210
|
+
//# sourceMappingURL=service-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-config.js","sourceRoot":"","sources":["../src/service-config.ts"],"names":[],"mappings":"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"}
|
package/dist/service.d.ts
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
import type { Result } from './result.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ProvisionLookup, TrailContext } from './types.js';
|
|
3
3
|
import type { z } from 'zod';
|
|
4
4
|
/**
|
|
5
|
-
* Stable process-scoped fields available when constructing a
|
|
5
|
+
* Stable process-scoped fields available when constructing a provision.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* full per-request TrailContext.
|
|
7
|
+
* Provisions are app-level singletons, so they intentionally do not receive
|
|
8
|
+
* the full per-request TrailContext. When a provision declares a `config` schema,
|
|
9
|
+
* the validated config is passed as `svc.config`.
|
|
9
10
|
*/
|
|
10
|
-
export type
|
|
11
|
+
export type ProvisionContext<C = unknown> = Pick<TrailContext, 'cwd' | 'env' | 'workspaceRoot'> & {
|
|
12
|
+
readonly config: C;
|
|
13
|
+
};
|
|
11
14
|
/**
|
|
12
|
-
* Everything needed to describe a
|
|
15
|
+
* Everything needed to describe a provision before a factory is introduced.
|
|
16
|
+
*
|
|
17
|
+
* When `config` is a Zod schema, the `create` callback receives
|
|
18
|
+
* `ProvisionContext<C>` with the validated config value.
|
|
13
19
|
*/
|
|
14
|
-
export interface
|
|
15
|
-
/** Create the
|
|
16
|
-
readonly create: (svc:
|
|
17
|
-
/**
|
|
18
|
-
readonly config?: z.ZodType | undefined;
|
|
20
|
+
export interface ProvisionSpec<T, C = unknown> {
|
|
21
|
+
/** Create the provision instance from stable process-scoped context. */
|
|
22
|
+
readonly create: (svc: ProvisionContext<C>) => Result<T, Error> | Promise<Result<T, Error>>;
|
|
23
|
+
/** Config schema — when present, config is validated and passed to `create`. */
|
|
24
|
+
readonly config?: z.ZodType<C> | undefined;
|
|
19
25
|
/** Optional cleanup performed when the hosting surface shuts down. */
|
|
20
|
-
readonly dispose?: ((
|
|
26
|
+
readonly dispose?: ((provision: T) => void | Promise<void>) | undefined;
|
|
21
27
|
/** Optional operational readiness probe for introspection tooling. */
|
|
22
|
-
readonly health?: ((
|
|
28
|
+
readonly health?: ((provision: T) => Result<unknown, Error> | Promise<Result<unknown, Error>>) | undefined;
|
|
23
29
|
/** Optional test factory used by higher-level helpers. */
|
|
24
30
|
readonly mock?: (() => T | Promise<T>) | undefined;
|
|
25
31
|
/** Human-readable description. */
|
|
@@ -27,43 +33,39 @@ export interface ServiceSpec<T> {
|
|
|
27
33
|
/** Arbitrary metadata for tooling and filtering. */
|
|
28
34
|
readonly metadata?: Readonly<Record<string, unknown>> | undefined;
|
|
29
35
|
}
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* TRL-73 introduces the structural contract only. The `service()` factory and
|
|
34
|
-
* runtime helpers land in follow-up branches.
|
|
35
|
-
*/
|
|
36
|
-
export interface Service<T> extends ServiceSpec<T> {
|
|
37
|
-
readonly kind: 'service';
|
|
36
|
+
/** A typed provision definition. */
|
|
37
|
+
export interface Provision<T> extends ProvisionSpec<T> {
|
|
38
|
+
readonly kind: 'provision';
|
|
38
39
|
readonly id: string;
|
|
39
|
-
/** Read the resolved
|
|
40
|
+
/** Read the resolved provision instance from a trail context. */
|
|
40
41
|
from(ctx: TrailContext): T;
|
|
41
42
|
}
|
|
42
43
|
/**
|
|
43
|
-
* Existential type for heterogeneous
|
|
44
|
+
* Existential type for heterogeneous provision collections.
|
|
44
45
|
*
|
|
45
|
-
* `
|
|
46
|
-
* is too narrow for mixed
|
|
46
|
+
* `Provision<T>` includes function parameters in `dispose`/`health`, so
|
|
47
|
+
* `unknown` is too narrow for mixed provision arrays. `any` is the correct
|
|
48
|
+
* existential here.
|
|
47
49
|
*/
|
|
48
|
-
export type
|
|
49
|
-
/** Explicit runtime overrides keyed by
|
|
50
|
-
export type
|
|
51
|
-
/** Create a `ctx.
|
|
52
|
-
export declare const
|
|
50
|
+
export type AnyProvision = Provision<any>;
|
|
51
|
+
/** Explicit runtime overrides keyed by provision ID. */
|
|
52
|
+
export type ProvisionOverrideMap = Readonly<Record<string, unknown>>;
|
|
53
|
+
/** Create a `ctx.provision(...)` accessor bound to a concrete context snapshot. */
|
|
54
|
+
export declare const createProvisionLookup: (getContext: () => Pick<TrailContext, "extensions">) => ProvisionLookup;
|
|
53
55
|
/**
|
|
54
|
-
* Create a typed
|
|
56
|
+
* Create a typed provision definition.
|
|
55
57
|
*
|
|
56
|
-
* The
|
|
58
|
+
* The provision object is inert until a later execution branch resolves concrete
|
|
57
59
|
* instances into TrailContext extensions.
|
|
58
60
|
*/
|
|
59
|
-
export declare const
|
|
60
|
-
/** Narrow unknown values to
|
|
61
|
-
export declare const
|
|
61
|
+
export declare const provision: <T>(id: string, spec: ProvisionSpec<T>) => Provision<T>;
|
|
62
|
+
/** Narrow unknown values to provision definitions during topo discovery. */
|
|
63
|
+
export declare const isProvision: (value: unknown) => value is AnyProvision;
|
|
62
64
|
/**
|
|
63
|
-
* Return the first duplicate
|
|
65
|
+
* Return the first duplicate provision ID in a collection, if any.
|
|
64
66
|
*
|
|
65
67
|
* This supports later topo registration without each caller duplicating the
|
|
66
68
|
* same scan logic.
|
|
67
69
|
*/
|
|
68
|
-
export declare const
|
|
70
|
+
export declare const findDuplicateProvisionId: (provisions: readonly Pick<AnyProvision, "id">[]) => string | undefined;
|
|
69
71
|
//# sourceMappingURL=service.d.ts.map
|
package/dist/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,CAC9C,YAAY,EACZ,KAAK,GAAG,KAAK,GAAG,eAAe,CAChC,GAAG;IACF,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;CACpB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO;IAC3C,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,CACf,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC,KACrB,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IAClD,gFAAgF;IAChF,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAC3C,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;IACxE,sEAAsE;IACtE,QAAQ,CAAC,MAAM,CAAC,EACZ,CAAC,CACC,SAAS,EAAE,CAAC,KACT,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,GAC9D,SAAS,CAAC;IACd,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACnD,kCAAkC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CACnE;AAED,oCAAoC;AACpC,MAAM,WAAW,SAAS,CAAC,CAAC,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;IACpD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,CAAC,CAAC;CAC5B;AAED;;;;;;GAMG;AAEH,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;AAE1C,wDAAwD;AACxD,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAqBrE,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,GAChC,YAAY,MAAM,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,KACjD,eAQoB,CAAC;AAExB;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,EACzB,IAAI,MAAM,EACV,MAAM,aAAa,CAAC,CAAC,CAAC,KACrB,SAAS,CAAC,CAAC,CASV,CAAC;AAEL,4EAA4E;AAC5E,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,YAMrD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACnC,YAAY,SAAS,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,KAC9C,MAAM,GAAG,SASX,CAAC"}
|
package/dist/service.js
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
1
|
import { NotFoundError } from './errors.js';
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const id =
|
|
2
|
+
const getProvisionId = (provisionOrId) => typeof provisionOrId === 'string' ? provisionOrId : provisionOrId.id;
|
|
3
|
+
const getProvisionInstance = (ctx, provisionOrId) => {
|
|
4
|
+
const id = getProvisionId(provisionOrId);
|
|
5
5
|
return ctx.extensions?.[id];
|
|
6
6
|
};
|
|
7
|
-
const
|
|
8
|
-
/** Create a `ctx.
|
|
9
|
-
export const
|
|
10
|
-
const id =
|
|
7
|
+
const hasProvisionInstance = (ctx, provisionOrId) => Object.hasOwn(ctx.extensions ?? {}, getProvisionId(provisionOrId));
|
|
8
|
+
/** Create a `ctx.provision(...)` accessor bound to a concrete context snapshot. */
|
|
9
|
+
export const createProvisionLookup = (getContext) => ((provisionOrId) => {
|
|
10
|
+
const id = getProvisionId(provisionOrId);
|
|
11
11
|
const ctx = getContext();
|
|
12
|
-
if (!
|
|
13
|
-
throw new NotFoundError(`
|
|
12
|
+
if (!hasProvisionInstance(ctx, id)) {
|
|
13
|
+
throw new NotFoundError(`Provision "${id}" not found in trail context`);
|
|
14
14
|
}
|
|
15
|
-
return
|
|
15
|
+
return getProvisionInstance(ctx, id);
|
|
16
16
|
});
|
|
17
17
|
/**
|
|
18
|
-
* Create a typed
|
|
18
|
+
* Create a typed provision definition.
|
|
19
19
|
*
|
|
20
|
-
* The
|
|
20
|
+
* The provision object is inert until a later execution branch resolves concrete
|
|
21
21
|
* instances into TrailContext extensions.
|
|
22
22
|
*/
|
|
23
|
-
export const
|
|
23
|
+
export const provision = (id, spec) => Object.freeze({
|
|
24
24
|
...spec,
|
|
25
25
|
from(ctx) {
|
|
26
|
-
const lookup = ctx.
|
|
26
|
+
const lookup = ctx.provision ?? createProvisionLookup(() => ctx);
|
|
27
27
|
return lookup(this);
|
|
28
28
|
},
|
|
29
29
|
id,
|
|
30
|
-
kind: '
|
|
30
|
+
kind: 'provision',
|
|
31
31
|
});
|
|
32
|
-
/** Narrow unknown values to
|
|
33
|
-
export const
|
|
32
|
+
/** Narrow unknown values to provision definitions during topo discovery. */
|
|
33
|
+
export const isProvision = (value) => {
|
|
34
34
|
if (typeof value !== 'object' || value === null) {
|
|
35
35
|
return false;
|
|
36
36
|
}
|
|
37
37
|
const v = value;
|
|
38
|
-
return v.kind === '
|
|
38
|
+
return v.kind === 'provision' && typeof v.id === 'string';
|
|
39
39
|
};
|
|
40
40
|
/**
|
|
41
|
-
* Return the first duplicate
|
|
41
|
+
* Return the first duplicate provision ID in a collection, if any.
|
|
42
42
|
*
|
|
43
43
|
* This supports later topo registration without each caller duplicating the
|
|
44
44
|
* same scan logic.
|
|
45
45
|
*/
|
|
46
|
-
export const
|
|
46
|
+
export const findDuplicateProvisionId = (provisions) => {
|
|
47
47
|
const seen = new Set();
|
|
48
|
-
for (const candidate of
|
|
48
|
+
for (const candidate of provisions) {
|
|
49
49
|
if (seen.has(candidate.id)) {
|
|
50
50
|
return candidate.id;
|
|
51
51
|
}
|
package/dist/service.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAqE5C,MAAM,cAAc,GAAG,CACrB,aAAgD,EACxC,EAAE,CACV,OAAO,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC;AAEvE,MAAM,oBAAoB,GAAG,CAC3B,GAAqC,EACrC,aAAgD,EAC7C,EAAE;IACL,MAAM,EAAE,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;IACzC,OAAO,GAAG,CAAC,UAAU,EAAE,CAAC,EAAE,CAAM,CAAC;AACnC,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,CAC3B,GAAqC,EACrC,aAAgD,EACvC,EAAE,CACX,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;AAErE,mFAAmF;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,UAAkD,EACjC,EAAE,CACnB,CAAC,CAAC,aAAgD,EAAE,EAAE;IACpD,MAAM,EAAE,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,aAAa,CAAC,cAAc,EAAE,8BAA8B,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,oBAAoB,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC,CAAoB,CAAC;AAExB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,EAAU,EACV,IAAsB,EACR,EAAE,CAChB,MAAM,CAAC,MAAM,CAAC;IACZ,GAAG,IAAI;IACP,IAAI,CAAC,GAAiB;QACpB,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,IAAI,qBAAqB,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IACD,EAAE;IACF,IAAI,EAAE,WAAoB;CAC3B,CAAC,CAAC;AAEL,4EAA4E;AAC5E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAc,EAAyB,EAAE;IACnE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,CAAC,GAAG,KAAyC,CAAC;IACpD,OAAO,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC;AAC5D,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,UAA+C,EAC3B,EAAE;IACtB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YAC3B,OAAO,SAAS,CAAC,EAAE,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC"}
|
package/dist/signal.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signal — a named payload schema with optional provenance meta.
|
|
3
|
+
*/
|
|
4
|
+
import type { z } from 'zod';
|
|
5
|
+
export interface SignalSpec<T> {
|
|
6
|
+
readonly payload: z.ZodType<T>;
|
|
7
|
+
readonly description?: string | undefined;
|
|
8
|
+
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
9
|
+
/** Trail IDs that produce this signal (e.g. the trails it originates from). */
|
|
10
|
+
readonly from?: readonly string[] | undefined;
|
|
11
|
+
}
|
|
12
|
+
export interface Signal<T> {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly kind: 'signal';
|
|
15
|
+
readonly payload: z.ZodType<T>;
|
|
16
|
+
readonly description?: string | undefined;
|
|
17
|
+
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
18
|
+
/** Trail IDs that produce this signal (e.g. the trails it originates from). */
|
|
19
|
+
readonly from?: readonly string[] | undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a signal definition.
|
|
23
|
+
*
|
|
24
|
+
* A signal is a named payload schema describing something that happened.
|
|
25
|
+
* Returns a frozen object with `kind: "signal"` and all spec fields.
|
|
26
|
+
*/
|
|
27
|
+
export declare function signal<T>(id: string, spec: SignalSpec<T>): Signal<T>;
|
|
28
|
+
export declare function signal<T>(spec: SignalSpec<T> & {
|
|
29
|
+
readonly id: string;
|
|
30
|
+
}): Signal<T>;
|
|
31
|
+
/** Existential type for heterogeneous signal collections */
|
|
32
|
+
export type AnySignal = Signal<unknown>;
|
|
33
|
+
//# sourceMappingURL=signal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../src/signal.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAM7B,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAC9D,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAC/C;AAMD,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAC9D,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAC/C;AAMD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;AACtE,wBAAgB,MAAM,CAAC,CAAC,EACtB,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC5C,MAAM,CAAC,CAAC,CAAC,CAAC;AAkBb,4DAA4D;AAC5D,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC"}
|
package/dist/signal.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signal — a named payload schema with optional provenance meta.
|
|
3
|
+
*/
|
|
4
|
+
export function signal(idOrSpec, maybeSpec) {
|
|
5
|
+
const resolvedId = typeof idOrSpec === 'string' ? idOrSpec : idOrSpec.id;
|
|
6
|
+
// oxlint-disable-next-line no-non-null-assertion -- overload guarantees maybeSpec when idOrSpec is string
|
|
7
|
+
const resolvedSpec = typeof idOrSpec === 'string' ? maybeSpec : idOrSpec;
|
|
8
|
+
return Object.freeze({
|
|
9
|
+
description: resolvedSpec.description,
|
|
10
|
+
from: resolvedSpec.from ? Object.freeze([...resolvedSpec.from]) : undefined,
|
|
11
|
+
id: resolvedId,
|
|
12
|
+
kind: 'signal',
|
|
13
|
+
meta: resolvedSpec.meta,
|
|
14
|
+
payload: resolvedSpec.payload,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=signal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal.js","sourceRoot":"","sources":["../src/signal.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4CH,MAAM,UAAU,MAAM,CACpB,QAA4D,EAC5D,SAAyB;IAEzB,MAAM,UAAU,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;IACzE,0GAA0G;IAC1G,MAAM,YAAY,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAU,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC1E,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,WAAW,EAAE,YAAY,CAAC,WAAW;QACrC,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;QAC3E,EAAE,EAAE,UAAU;QACd,IAAI,EAAE,QAAiB;QACvB,IAAI,EAAE,YAAY,CAAC,IAAI;QACvB,OAAO,EAAE,YAAY,CAAC,OAAO;KAC9B,CAAC,CAAC;AACL,CAAC"}
|
package/dist/topo.d.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Application entry point — scans module exports to build a topology graph.
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
4
|
+
import type { AnySignal } from './event.js';
|
|
5
|
+
import type { AnyProvision } from './provision.js';
|
|
6
6
|
import type { AnyTrail } from './trail.js';
|
|
7
7
|
export interface Topo {
|
|
8
8
|
readonly name: string;
|
|
9
9
|
readonly trails: ReadonlyMap<string, AnyTrail>;
|
|
10
|
-
readonly
|
|
11
|
-
readonly
|
|
10
|
+
readonly signals: ReadonlyMap<string, AnySignal>;
|
|
11
|
+
readonly provisions: ReadonlyMap<string, AnyProvision>;
|
|
12
12
|
readonly count: number;
|
|
13
|
-
readonly
|
|
13
|
+
readonly provisionCount: number;
|
|
14
14
|
get(id: string): AnyTrail | undefined;
|
|
15
|
-
|
|
15
|
+
getProvision(id: string): AnyProvision | undefined;
|
|
16
16
|
has(id: string): boolean;
|
|
17
|
-
|
|
17
|
+
hasProvision(id: string): boolean;
|
|
18
18
|
ids(): string[];
|
|
19
|
-
|
|
19
|
+
provisionIds(): string[];
|
|
20
20
|
list(): AnyTrail[];
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
listSignals(): AnySignal[];
|
|
22
|
+
listProvisions(): AnyProvision[];
|
|
23
23
|
}
|
|
24
24
|
export declare const topo: (name: string, ...modules: Record<string, unknown>[]) => Topo;
|
|
25
25
|
//# sourceMappingURL=topo.d.ts.map
|
package/dist/topo.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"topo.d.ts","sourceRoot":"","sources":["../src/topo.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"topo.d.ts","sourceRoot":"","sources":["../src/topo.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAM3C,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/C,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjD,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IACtC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IACnD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,GAAG,IAAI,MAAM,EAAE,CAAC;IAChB,YAAY,IAAI,MAAM,EAAE,CAAC;IACzB,IAAI,IAAI,QAAQ,EAAE,CAAC;IACnB,WAAW,IAAI,SAAS,EAAE,CAAC;IAC3B,cAAc,IAAI,YAAY,EAAE,CAAC;CAClC;AA4ID,eAAO,MAAM,IAAI,GACf,MAAM,MAAM,EACZ,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KACpC,IAUF,CAAC"}
|