brickflow 0.0.1 → 0.0.2

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/dist/index.js ADDED
@@ -0,0 +1,755 @@
1
+ // src/index.ts
2
+ import { P } from "ts-pattern";
3
+
4
+ // src/worker/execution.ts
5
+ import { randomUUID } from "crypto";
6
+
7
+ // src/brick/failure.ts
8
+ var BrickFailure = class {
9
+ error;
10
+ constructor(error) {
11
+ this.error = error;
12
+ }
13
+ };
14
+ function failWith(error) {
15
+ throw new BrickFailure(error);
16
+ }
17
+ function readBrickFailure(value) {
18
+ return value instanceof BrickFailure ? value.error : void 0;
19
+ }
20
+ function isBrickFailure(value) {
21
+ return value instanceof BrickFailure;
22
+ }
23
+
24
+ // src/brick/implementation.ts
25
+ var implementationBrand = /* @__PURE__ */ Symbol("BrickImplementation");
26
+ function markBrickImplementation(target) {
27
+ Object.defineProperty(target, implementationBrand, {
28
+ configurable: false,
29
+ enumerable: false,
30
+ value: true,
31
+ writable: false
32
+ });
33
+ }
34
+ function isBrickImplementation(value) {
35
+ return typeof value === "object" && value !== null && Object.hasOwn(value, implementationBrand) && value[implementationBrand] === true;
36
+ }
37
+ async function executeBrickImplementation(implementation, params, requirements, dependencies, signals) {
38
+ try {
39
+ const value = await implementation.handler(params, requirements, dependencies, {
40
+ fail: failWith,
41
+ signals
42
+ });
43
+ return { ok: true, value };
44
+ } catch (error) {
45
+ if (!isBrickFailure(error)) throw error;
46
+ return { ok: false, error: readBrickFailure(error) };
47
+ }
48
+ }
49
+
50
+ // src/path-segment.ts
51
+ var PATH_DELIMITER = ".";
52
+ function assertValidPathSegment(segment) {
53
+ if (segment.length === 0) {
54
+ throw new Error("Invalid path segment: segment must not be empty");
55
+ }
56
+ if (segment.includes(PATH_DELIMITER)) {
57
+ throw new Error(
58
+ `Invalid path segment "${segment}": "${PATH_DELIMITER}" is a reserved delimiter`
59
+ );
60
+ }
61
+ }
62
+
63
+ // src/signal/namespace.ts
64
+ function isHandler(value) {
65
+ return typeof value === "function";
66
+ }
67
+ function flattenNamespacedSignalHandlers(handlers, path = []) {
68
+ const flattened = /* @__PURE__ */ Object.create(null);
69
+ for (const [name, value] of Object.entries(handlers)) {
70
+ assertValidPathSegment(name);
71
+ const durableName = durableSignalName(path, name);
72
+ if (isHandler(value)) {
73
+ flattened[durableName] = value;
74
+ continue;
75
+ }
76
+ if (typeof value === "object" && value !== null) {
77
+ Object.assign(
78
+ flattened,
79
+ flattenNamespacedSignalHandlers(value, [...path, name])
80
+ );
81
+ continue;
82
+ }
83
+ throw new Error(`Invalid signal handler at "${durableName}"`);
84
+ }
85
+ return Object.freeze(flattened);
86
+ }
87
+ function durableSignalName(path, signalName) {
88
+ for (const segment of path) assertValidPathSegment(segment);
89
+ assertValidPathSegment(signalName);
90
+ return [...path, signalName].join(".");
91
+ }
92
+ function signalCallMetadata(layerPath, brickPath, signalName, callId, occurrence) {
93
+ const stableLayerPath = Object.freeze([...layerPath]);
94
+ const stableBrickPath = Object.freeze([...brickPath]);
95
+ return Object.freeze({
96
+ durableName: durableSignalName([...stableLayerPath, ...stableBrickPath], signalName),
97
+ signalName,
98
+ layerPath: stableLayerPath,
99
+ brickPath: stableBrickPath,
100
+ callId,
101
+ occurrence
102
+ });
103
+ }
104
+
105
+ // src/engine/execution-context.ts
106
+ var ExecutionContext = class _ExecutionContext {
107
+ layerPath;
108
+ brickPath;
109
+ callId;
110
+ providers;
111
+ signalHandlers;
112
+ #signalOccurrences = /* @__PURE__ */ new Map();
113
+ constructor(options) {
114
+ const layerPath = [...options.layerPath ?? []];
115
+ const brickPath = [...options.brickPath ?? []];
116
+ for (const segment of layerPath) assertValidPathSegment(segment);
117
+ for (const segment of brickPath) assertValidPathSegment(segment);
118
+ this.layerPath = Object.freeze(layerPath);
119
+ this.brickPath = Object.freeze(brickPath);
120
+ this.callId = options.callId;
121
+ this.providers = options.providers ?? Object.freeze({});
122
+ this.signalHandlers = options.signalHandlers;
123
+ Object.freeze(this);
124
+ }
125
+ childLayer(segment) {
126
+ assertValidPathSegment(segment);
127
+ return new _ExecutionContext({
128
+ layerPath: [...this.layerPath, segment],
129
+ brickPath: this.brickPath,
130
+ callId: this.callId,
131
+ providers: this.providers,
132
+ ...this.signalHandlers ? { signalHandlers: this.signalHandlers } : {}
133
+ });
134
+ }
135
+ childBrick(segment, callId) {
136
+ assertValidPathSegment(segment);
137
+ return new _ExecutionContext({
138
+ layerPath: this.layerPath,
139
+ brickPath: [...this.brickPath, segment],
140
+ callId,
141
+ providers: this.providers,
142
+ ...this.signalHandlers ? { signalHandlers: this.signalHandlers } : {}
143
+ });
144
+ }
145
+ withSignalHandlers(signalHandlers) {
146
+ return new _ExecutionContext({
147
+ layerPath: this.layerPath,
148
+ brickPath: this.brickPath,
149
+ callId: this.callId,
150
+ providers: this.providers,
151
+ signalHandlers
152
+ });
153
+ }
154
+ nextSignalMetadata(signalName) {
155
+ const occurrence = (this.#signalOccurrences.get(signalName) ?? 0) + 1;
156
+ this.#signalOccurrences.set(signalName, occurrence);
157
+ return signalCallMetadata(this.layerPath, this.brickPath, signalName, this.callId, occurrence);
158
+ }
159
+ };
160
+
161
+ // src/signal/handler.ts
162
+ var NonBoundarySignalHandlerChainError = class extends Error {
163
+ signalName;
164
+ constructor(signalName) {
165
+ super(`Signal handler chain exhausted before reaching a boundary for "${signalName}"`);
166
+ this.name = "NonBoundarySignalHandlerChainError";
167
+ this.signalName = signalName;
168
+ }
169
+ };
170
+ var MissingSignalHandlerError = class extends Error {
171
+ signalName;
172
+ constructor(signalName) {
173
+ super(`No boundary handler resolved signal "${signalName}"`);
174
+ this.name = "MissingSignalHandlerError";
175
+ this.signalName = signalName;
176
+ }
177
+ };
178
+ function createSignalHandlerChain(handlers, parent, boundary = false) {
179
+ const chain = parent ? { handlers: Object.freeze({ ...handlers }), parent, boundary } : { handlers: Object.freeze({ ...handlers }), boundary };
180
+ return Object.freeze(chain);
181
+ }
182
+ async function resolveSignal(chain, signalName, request) {
183
+ const handler = Object.hasOwn(chain.handlers, signalName) ? chain.handlers[signalName] : void 0;
184
+ if (handler) {
185
+ const response = await handler(request);
186
+ if (response !== void 0) {
187
+ return response;
188
+ }
189
+ }
190
+ if (chain.parent) {
191
+ return resolveSignal(chain.parent, signalName, request);
192
+ }
193
+ if (chain.boundary) {
194
+ throw new MissingSignalHandlerError(signalName);
195
+ }
196
+ throw new NonBoundarySignalHandlerChainError(signalName);
197
+ }
198
+
199
+ // src/signal/functions.ts
200
+ function createSignalFunctions(context, dispatch) {
201
+ const functions = /* @__PURE__ */ new Map();
202
+ const target = Object.freeze({});
203
+ return new Proxy(target, {
204
+ get(_target, property) {
205
+ if (typeof property !== "string") return void 0;
206
+ assertValidPathSegment(property);
207
+ let signal = functions.get(property);
208
+ if (!signal) {
209
+ signal = async (request) => {
210
+ const metadata = context.nextSignalMetadata(property);
211
+ if (context.signalHandlers) {
212
+ return resolveSignal(context.signalHandlers, metadata.durableName, request);
213
+ }
214
+ if (dispatch) {
215
+ return dispatch({ name: metadata.durableName, request });
216
+ }
217
+ throw new Error(`No signal dispatch configured for "${metadata.durableName}"`);
218
+ };
219
+ functions.set(property, signal);
220
+ }
221
+ return signal;
222
+ }
223
+ });
224
+ }
225
+
226
+ // src/worker/local-worker.ts
227
+ var LocalRunCancelledError = class extends Error {
228
+ runId;
229
+ reason;
230
+ constructor(runId, reason) {
231
+ super(
232
+ reason === void 0 ? `Local run "${runId}" was cancelled` : `Local run "${runId}" was cancelled: ${reason}`
233
+ );
234
+ this.name = "LocalRunCancelledError";
235
+ this.runId = runId;
236
+ this.reason = reason;
237
+ }
238
+ };
239
+ var DuplicateLocalRunIdError = class extends Error {
240
+ runId;
241
+ constructor(runId) {
242
+ super(`Local run ID "${runId}" already exists`);
243
+ this.name = "DuplicateLocalRunIdError";
244
+ this.runId = runId;
245
+ }
246
+ };
247
+ var LocalRun = class {
248
+ constructor(request) {
249
+ this.request = request;
250
+ this.id = request.id;
251
+ this.result = new Promise((resolve, reject) => {
252
+ this.rejectResult = reject;
253
+ queueMicrotask(async () => {
254
+ if (this.cancellation !== void 0) return;
255
+ this.currentStatus = "running";
256
+ try {
257
+ const result = await this.request.execute();
258
+ if (this.cancellation !== void 0) return;
259
+ this.currentStatus = result.ok ? "completed" : "failed";
260
+ resolve(result);
261
+ } catch (error) {
262
+ if (this.cancellation !== void 0) return;
263
+ this.currentStatus = "failed";
264
+ reject(error);
265
+ }
266
+ });
267
+ });
268
+ }
269
+ request;
270
+ id;
271
+ result;
272
+ currentStatus = "queued";
273
+ cancellation;
274
+ rejectResult;
275
+ async status() {
276
+ return this.currentStatus;
277
+ }
278
+ async cancel(reason) {
279
+ if (this.cancellation !== void 0 || this.currentStatus === "completed" || this.currentStatus === "failed") {
280
+ return;
281
+ }
282
+ this.cancellation = new LocalRunCancelledError(this.id, reason);
283
+ this.currentStatus = "cancelled";
284
+ this.rejectResult(this.cancellation);
285
+ }
286
+ async signal(signal) {
287
+ if (this.cancellation !== void 0) throw this.cancellation;
288
+ return this.request.signal(signal);
289
+ }
290
+ };
291
+ var LocalWorker = class {
292
+ runIds = /* @__PURE__ */ new Set();
293
+ start(request) {
294
+ if (this.runIds.has(request.id)) throw new DuplicateLocalRunIdError(request.id);
295
+ this.runIds.add(request.id);
296
+ return new LocalRun(request);
297
+ }
298
+ };
299
+ var localWorker = new LocalWorker();
300
+
301
+ // src/worker/run.ts
302
+ import { isMatching } from "ts-pattern";
303
+ var UnhandledBrickFailureError = class extends Error {
304
+ failure;
305
+ constructor(failure) {
306
+ super(`Unhandled Brick failure: ${readableFailure(failure)}`);
307
+ this.name = "UnhandledBrickFailureError";
308
+ this.failure = failure;
309
+ }
310
+ };
311
+ function readableFailure(failure) {
312
+ if (typeof failure === "string") return failure;
313
+ try {
314
+ return JSON.stringify(failure);
315
+ } catch {
316
+ return String(failure);
317
+ }
318
+ }
319
+ var BrickRunImplementation = class _BrickRunImplementation {
320
+ id;
321
+ #handle;
322
+ #handlers;
323
+ #execution;
324
+ constructor(handle, handlers = []) {
325
+ this.id = handle.id;
326
+ this.#handle = handle;
327
+ this.#handlers = Object.freeze([...handlers]);
328
+ Object.freeze(this);
329
+ }
330
+ status() {
331
+ return this.#handle.status();
332
+ }
333
+ cancel(reason) {
334
+ return this.#handle.cancel(reason);
335
+ }
336
+ with(pattern, handler) {
337
+ return new _BrickRunImplementation(this.#handle, [
338
+ ...this.#handlers,
339
+ { pattern, handler }
340
+ ]);
341
+ }
342
+ // biome-ignore lint/suspicious/noThenProperty: exhaustive BrickRun values are intentionally PromiseLike
343
+ then(onfulfilled, onrejected) {
344
+ this.#execution ??= this.#execute();
345
+ return this.#execution.then(onfulfilled, onrejected);
346
+ }
347
+ async #execute() {
348
+ const outcome = await this.#handle.result;
349
+ if (outcome.ok) return outcome.value;
350
+ for (const registered of this.#handlers) {
351
+ if (!isMatching(registered.pattern)(outcome.error)) continue;
352
+ const recovered = await registered.handler(outcome.error);
353
+ if (recovered !== void 0) return recovered;
354
+ throw new UnhandledBrickFailureError(outcome.error);
355
+ }
356
+ throw new UnhandledBrickFailureError(outcome.error);
357
+ }
358
+ };
359
+ function createBrickRun(handle) {
360
+ return new BrickRunImplementation(handle);
361
+ }
362
+
363
+ // src/worker/execution.ts
364
+ function directSignalChain(handlers) {
365
+ return createSignalHandlerChain(flattenNamespacedSignalHandlers(handlers), void 0, true);
366
+ }
367
+ async function executeResolvedBrick(entry, params, options, context) {
368
+ const implementation = entry.implementation;
369
+ const dependencies = new Proxy(/* @__PURE__ */ Object.create(null), {
370
+ get(_target, property) {
371
+ if (typeof property !== "string") return void 0;
372
+ assertValidPathSegment(property);
373
+ const dependencyEntry = options.resolveDependency(entry, property);
374
+ return async (dependencyParams, callOptions) => {
375
+ const signalPath = dependencyEntry.signalPath ?? dependencyEntry.suppliedPath ?? dependencyEntry.id?.split(".") ?? [dependencyEntry.key];
376
+ const layerPath = signalPath.slice(0, -1);
377
+ const brickPath = [signalPath.at(-1) ?? dependencyEntry.key];
378
+ const parentSignalHandlers = context.signalHandlers;
379
+ const localHandlers = callOptions?.signals ? createSignalHandlerChain(
380
+ Object.fromEntries(
381
+ Object.entries(callOptions.signals).map(([name, handler]) => {
382
+ assertValidPathSegment(name);
383
+ return [[...signalPath, name].join("."), handler];
384
+ })
385
+ ),
386
+ parentSignalHandlers,
387
+ false
388
+ ) : parentSignalHandlers;
389
+ const dependencyContext = new ExecutionContext({
390
+ layerPath,
391
+ brickPath,
392
+ callId: context.callId,
393
+ providers: context.providers,
394
+ ...localHandlers ? { signalHandlers: localHandlers } : {}
395
+ });
396
+ const outcome = await executeResolvedBrick(
397
+ dependencyEntry,
398
+ dependencyParams,
399
+ options,
400
+ dependencyContext
401
+ );
402
+ if (outcome.ok) return outcome.value;
403
+ return failWith(outcome.error);
404
+ };
405
+ }
406
+ });
407
+ const signalContext = entry.ownSignalPath ? new ExecutionContext({
408
+ layerPath: entry.ownSignalPath.slice(0, -1),
409
+ brickPath: [entry.ownSignalPath.at(-1) ?? entry.key],
410
+ callId: context.callId,
411
+ providers: context.providers,
412
+ ...context.signalHandlers ? { signalHandlers: context.signalHandlers } : {}
413
+ }) : context;
414
+ const signals = createSignalFunctions(signalContext);
415
+ return executeBrickImplementation(
416
+ implementation,
417
+ params,
418
+ context.providers,
419
+ dependencies,
420
+ signals
421
+ );
422
+ }
423
+ function executeBrick(options) {
424
+ const id = options.id ?? randomUUID();
425
+ const handlers = options.signals ?? {};
426
+ const boundary = options.directSignals ? directSignalChain(handlers) : createSignalHandlerChain(flattenNamespacedSignalHandlers(handlers), void 0, true);
427
+ const rootId = options.root.id?.split(".") ?? [];
428
+ const context = new ExecutionContext({
429
+ layerPath: rootId.slice(0, -1),
430
+ brickPath: rootId.length > 0 ? [rootId.at(-1)] : [],
431
+ callId: id,
432
+ providers: options.providers,
433
+ signalHandlers: boundary
434
+ });
435
+ const request = {
436
+ id,
437
+ ...options.root.id ? { brickId: options.root.id } : {},
438
+ params: options.params,
439
+ ...options.metadata ? { metadata: options.metadata } : {},
440
+ context,
441
+ execute: () => executeResolvedBrick(options.root, options.params, options, context),
442
+ signal: ({ name, request: signalRequest }) => resolveSignal(boundary, name, signalRequest)
443
+ };
444
+ return createBrickRun((options.worker ?? localWorker).start(request));
445
+ }
446
+ function createSuppliedEntry(key, brick2, dependencies, path) {
447
+ const id = [...path, key].join(".");
448
+ return Object.freeze({
449
+ id,
450
+ key,
451
+ implementation: brick2,
452
+ suppliedPath: Object.freeze([...path, key]),
453
+ ...dependencies ? { suppliedDependencies: dependencies } : {}
454
+ });
455
+ }
456
+ function runDirectBrick(implementation, params, options) {
457
+ const root = Object.freeze({
458
+ key: "direct",
459
+ implementation,
460
+ ...options?.dependencies ? { suppliedDependencies: options.dependencies } : {}
461
+ });
462
+ return executeBrick({
463
+ root,
464
+ entries: [root],
465
+ params,
466
+ providers: Object.freeze({ ...options?.requirements ?? {} }),
467
+ resolveDependency: (caller, alias) => {
468
+ const node = caller.suppliedDependencies?.[alias];
469
+ if (!node?.brick) throw new Error(`Missing direct dependency Brick "${alias}"`);
470
+ return createSuppliedEntry(alias, node.brick, node.dependencies, caller.id?.split(".") ?? []);
471
+ },
472
+ directSignals: true,
473
+ ...options?.signals ? { signals: options.signals } : {},
474
+ ...options?.worker ? { worker: options.worker } : {},
475
+ ...typeof options?.id === "string" ? { id: options.id } : {},
476
+ ...options?.metadata ? { metadata: options.metadata } : {}
477
+ });
478
+ }
479
+
480
+ // src/brick/contract.ts
481
+ function brick(handler) {
482
+ const implementation = { handler };
483
+ Object.defineProperty(implementation, "run", {
484
+ configurable: false,
485
+ enumerable: false,
486
+ value: (params, ...options) => runDirectBrick(implementation, params, options[0]),
487
+ writable: false
488
+ });
489
+ markBrickImplementation(implementation);
490
+ return Object.freeze(implementation);
491
+ }
492
+
493
+ // src/layer/composition.ts
494
+ function isLayer(value) {
495
+ return value instanceof LayerRuntime;
496
+ }
497
+ function flattenLayer(layer) {
498
+ const flattened = [];
499
+ const durableIds = /* @__PURE__ */ new Set();
500
+ const active = /* @__PURE__ */ new Set();
501
+ const visit = (current, parentPath) => {
502
+ const path = [...parentPath, current.id];
503
+ if (active.has(current)) {
504
+ throw new Error(`Cyclic Layer nesting detected: ${[...path, current.id].join(" -> ")}`);
505
+ }
506
+ active.add(current);
507
+ for (const [key, entry] of Object.entries(current.entries)) {
508
+ if (isBrickImplementation(entry)) {
509
+ const id = [...path, key].join(".");
510
+ if (durableIds.has(id)) {
511
+ throw new Error(`Duplicate durable Brick ID: ${id}`);
512
+ }
513
+ durableIds.add(id);
514
+ flattened.push(
515
+ Object.freeze({
516
+ id,
517
+ key,
518
+ layerPath: Object.freeze([...path]),
519
+ implementation: entry
520
+ })
521
+ );
522
+ } else if (isLayer(entry)) {
523
+ visit(entry, path);
524
+ } else {
525
+ throw new Error(`Invalid Layer entry at ${[...path, key].join(".")}`);
526
+ }
527
+ }
528
+ active.delete(current);
529
+ };
530
+ visit(layer, []);
531
+ return Object.freeze(flattened);
532
+ }
533
+ function findLayerDependency(layer, caller, alias, fallback) {
534
+ const entries = flattenLayer(layer);
535
+ const inCallerScope = entries.filter(
536
+ (entry) => entry.key === alias && entry.layerPath.join(".") === caller.layerPath.join(".")
537
+ );
538
+ if (inCallerScope.length > 0) return inCallerScope[0];
539
+ const matches = entries.filter((entry) => entry.key === alias);
540
+ if (matches.length === 0) return fallback;
541
+ if (matches.length > 1 && fallback) return fallback;
542
+ if (matches.length > 1) {
543
+ throw new Error(
544
+ `Ambiguous dependency Brick entry "${alias}" resolved to multiple durable paths: ${matches.map(({ id }) => id).join(", ")}`
545
+ );
546
+ }
547
+ return matches[0];
548
+ }
549
+ function effectiveLayerProviders(layer) {
550
+ const providers = /* @__PURE__ */ Object.create(null);
551
+ const sources = /* @__PURE__ */ new Map();
552
+ const topLevelKeys = new Set(Object.keys(layer.providers));
553
+ const visit = (current, path) => {
554
+ const currentPath = [...path, current.id];
555
+ for (const entry of Object.values(current.entries)) {
556
+ if (isLayer(entry)) visit(entry, currentPath);
557
+ }
558
+ for (const [key, value] of Object.entries(current.providers)) {
559
+ if (path.length === 0) {
560
+ providers[key] = value;
561
+ sources.set(key, currentPath.join("."));
562
+ continue;
563
+ }
564
+ if (topLevelKeys.has(key)) continue;
565
+ if (Object.hasOwn(providers, key) && providers[key] !== value) {
566
+ throw new Error(
567
+ `Conflicting nested Layer provider "${key}" from ${sources.get(key)} and ${currentPath.join(".")}`
568
+ );
569
+ }
570
+ providers[key] = value;
571
+ sources.set(key, currentPath.join("."));
572
+ }
573
+ };
574
+ visit(layer, []);
575
+ return Object.freeze(providers);
576
+ }
577
+ var LayerRuntime = class {
578
+ };
579
+
580
+ // src/layer/provide.ts
581
+ function addProviders(current, values) {
582
+ for (const key of Object.keys(values)) {
583
+ if (Object.hasOwn(current, key)) {
584
+ throw new Error(`Provider key is already provided: ${key}`);
585
+ }
586
+ }
587
+ return Object.freeze({ ...current, ...values });
588
+ }
589
+
590
+ // src/layer/layer.ts
591
+ function hasProviderKey(layer, key) {
592
+ if (Object.hasOwn(layer.providers, key)) return true;
593
+ return Object.values(layer.entries).some((entry) => isLayer(entry) && hasProviderKey(entry, key));
594
+ }
595
+ var reservedNames = /* @__PURE__ */ new Set([
596
+ "id",
597
+ "entries",
598
+ "providers",
599
+ "provide",
600
+ "override"
601
+ ]);
602
+ var LayerImplementation = class _LayerImplementation extends LayerRuntime {
603
+ id;
604
+ entries;
605
+ providers;
606
+ constructor(id, entries, providers) {
607
+ super();
608
+ assertValidPathSegment(id);
609
+ for (const [key, entry] of Object.entries(entries)) {
610
+ assertValidPathSegment(key);
611
+ if (reservedNames.has(key)) {
612
+ throw new Error(`Reserved Layer entry name: ${key}`);
613
+ }
614
+ if (!isBrickImplementation(entry) && !isLayer(entry)) {
615
+ throw new Error(`Invalid Layer entry value for key: ${key}`);
616
+ }
617
+ }
618
+ this.id = id;
619
+ this.entries = Object.freeze({ ...entries });
620
+ this.providers = Object.freeze({ ...providers ?? {} });
621
+ for (const [key, entry] of Object.entries(this.entries)) {
622
+ Object.defineProperty(this, key, {
623
+ configurable: false,
624
+ enumerable: true,
625
+ value: this.bindEntry(entry, key),
626
+ writable: false
627
+ });
628
+ }
629
+ flattenLayer(this);
630
+ Object.freeze(this);
631
+ }
632
+ bindEntry(entry, key) {
633
+ if (isBrickImplementation(entry)) return this.bindBrick(entry, key, `${this.id}.${key}`);
634
+ if (isLayer(entry)) return this.bindNestedLayer(entry, `${this.id}.${entry.id}`);
635
+ return entry;
636
+ }
637
+ bindBrick(entry, key, targetId) {
638
+ const layer = this;
639
+ const bound = {
640
+ handler: entry.handler,
641
+ run(params, options) {
642
+ const entries = flattenLayer(layer);
643
+ const root = entries.find((candidate) => candidate.id === targetId);
644
+ if (!root) throw new Error(`Bound Brick entry not found: ${key}`);
645
+ const suppliedDependencies = options?.dependencies ?? {};
646
+ const executionRoot = Object.freeze({
647
+ ...root,
648
+ ...options?.dependencies ? { suppliedDependencies } : {}
649
+ });
650
+ return executeBrick({
651
+ root: executionRoot,
652
+ entries,
653
+ params,
654
+ providers: Object.freeze({
655
+ ...effectiveLayerProviders(layer),
656
+ ...options?.requirements ?? {}
657
+ }),
658
+ resolveDependency: (caller, alias) => {
659
+ const callerLayerPath = caller.layerPath ?? root.layerPath;
660
+ const suppliedNode = caller.suppliedDependencies?.[alias];
661
+ const supplied = suppliedNode?.brick ? suppliedNode : void 0;
662
+ const suppliedBranchPath = Object.freeze([...caller.suppliedPath ?? [], alias]);
663
+ const scopedSupplied = supplied ? Object.freeze({
664
+ id: [...caller.id?.split(".") ?? [], alias].join("."),
665
+ key: alias,
666
+ implementation: supplied.brick,
667
+ layerPath: Object.freeze([...callerLayerPath]),
668
+ suppliedPath: suppliedBranchPath,
669
+ ...supplied.dependencies ? { suppliedDependencies: supplied.dependencies } : {}
670
+ }) : void 0;
671
+ if (callerLayerPath.length > 0) {
672
+ const layerEntry = findLayerDependency(
673
+ layer,
674
+ caller,
675
+ alias,
676
+ scopedSupplied
677
+ );
678
+ if (layerEntry) {
679
+ const resolvedDependencies = suppliedNode?.dependencies ?? caller.suppliedDependencies?.[alias]?.dependencies;
680
+ return resolvedDependencies ? Object.freeze({
681
+ ...layerEntry,
682
+ suppliedDependencies: resolvedDependencies,
683
+ signalPath: Object.freeze(layerEntry.id.split(".")),
684
+ ...caller.suppliedPath || suppliedNode ? { suppliedPath: suppliedBranchPath } : {},
685
+ ...scopedSupplied ? { ownSignalPath: scopedSupplied.suppliedPath } : {}
686
+ }) : scopedSupplied ? Object.freeze({
687
+ ...layerEntry,
688
+ suppliedPath: scopedSupplied.suppliedPath,
689
+ signalPath: Object.freeze(layerEntry.id.split(".")),
690
+ ownSignalPath: scopedSupplied.suppliedPath
691
+ }) : caller.suppliedPath ? Object.freeze({
692
+ ...layerEntry,
693
+ suppliedPath: caller.suppliedPath,
694
+ signalPath: Object.freeze(layerEntry.id.split("."))
695
+ }) : layerEntry;
696
+ }
697
+ }
698
+ if (scopedSupplied) return scopedSupplied;
699
+ throw new Error(`Missing dependency Brick entry "${alias}" in the configured Layer`);
700
+ },
701
+ ...options?.signals ? { signals: options.signals } : {},
702
+ ...options?.worker ? { worker: options.worker } : {},
703
+ ...typeof options?.id === "string" ? { id: options.id } : {},
704
+ ...options?.metadata ? { metadata: options.metadata } : {}
705
+ });
706
+ }
707
+ };
708
+ markBrickImplementation(bound);
709
+ return Object.freeze(bound);
710
+ }
711
+ bindNestedLayer(nested, pathPrefix) {
712
+ const view = Object.create(Object.getPrototypeOf(nested));
713
+ Object.defineProperties(view, {
714
+ id: { enumerable: true, value: nested.id },
715
+ entries: { enumerable: false, value: nested.entries },
716
+ providers: { enumerable: false, value: nested.providers },
717
+ provide: { value: nested.provide?.bind(nested) },
718
+ override: { value: nested.override?.bind(nested) }
719
+ });
720
+ for (const [key, entry] of Object.entries(nested.entries)) {
721
+ Object.defineProperty(view, key, {
722
+ enumerable: true,
723
+ value: isBrickImplementation(entry) ? this.bindBrick(entry, key, `${pathPrefix}.${key}`) : isLayer(entry) ? this.bindNestedLayer(entry, `${pathPrefix}.${entry.id}`) : entry
724
+ });
725
+ }
726
+ return Object.freeze(view);
727
+ }
728
+ provide(values) {
729
+ addProviders(effectiveLayerProviders(this), values);
730
+ return new _LayerImplementation(this.id, this.entries, { ...this.providers, ...values });
731
+ }
732
+ override(values) {
733
+ for (const key of Object.keys(values)) {
734
+ if (!hasProviderKey(this, key)) {
735
+ throw new Error(`Cannot override absent provider key: ${key}`);
736
+ }
737
+ }
738
+ return new _LayerImplementation(this.id, this.entries, { ...this.providers, ...values });
739
+ }
740
+ };
741
+ var Layer = LayerImplementation;
742
+ export {
743
+ DuplicateLocalRunIdError,
744
+ ExecutionContext,
745
+ Layer,
746
+ LocalRunCancelledError,
747
+ LocalWorker,
748
+ MissingSignalHandlerError,
749
+ NonBoundarySignalHandlerChainError,
750
+ P,
751
+ UnhandledBrickFailureError,
752
+ brick,
753
+ localWorker
754
+ };
755
+ //# sourceMappingURL=index.js.map