@nimbus-sh/platform 0.1.0 → 0.3.0
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/README.md +157 -23
- package/dist/composition.d.ts +50 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +43 -0
- package/package.json +1 -1
- package/src/composition.ts +87 -0
package/README.md
CHANGED
|
@@ -4,32 +4,166 @@
|
|
|
4
4
|
> cloud OS. This README is edited and maintained with Claude (AI) and
|
|
5
5
|
> presented as-is.
|
|
6
6
|
|
|
7
|
-
The
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
The measured limits of the Cloudflare Workers platform, and a classifier for
|
|
8
|
+
the failures workerd reports only as prose.
|
|
9
|
+
|
|
10
|
+
Some Workers failures give you nothing to branch on. A Durable Object call
|
|
11
|
+
comes back with the words `internal error`. An isolate dies without throwing.
|
|
12
|
+
A write past a cap resets the object instead of failing. This package holds
|
|
13
|
+
the numbers those failures happen at, and the matchers that name a caught
|
|
14
|
+
error. No call site has to string-match its own.
|
|
15
|
+
|
|
16
|
+
Use it if you write Workers or Durable Object code. It answers whether to
|
|
17
|
+
retry a failed call, and what the limit is before you write past it. It has
|
|
18
|
+
no runtime dependencies and imports nothing from `cloudflare:`, so it loads
|
|
19
|
+
in workerd,
|
|
20
|
+
bun, and node.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @nimbus-sh/platform
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Retry or fail
|
|
29
|
+
|
|
30
|
+
`classifyDoCall` reads a failed Durable Object call and returns one class.
|
|
31
|
+
It walks the cause chain, because a wrapper error drops the runtime's
|
|
32
|
+
`.retryable` property and only the joined prose survives. `isRetryableDoCall`
|
|
33
|
+
says which classes a retry may act on.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { classifyDoCall, isRetryableDoCall, describeError } from '@nimbus-sh/platform';
|
|
37
|
+
|
|
38
|
+
export async function callWithRetry<T>(call: () => Promise<T>, attempts = 3): Promise<T> {
|
|
39
|
+
for (let attempt = 1; ; attempt++) {
|
|
40
|
+
try {
|
|
41
|
+
// Mint a fresh stub inside `call`: a stub that failed once is spent.
|
|
42
|
+
return await call();
|
|
43
|
+
} catch (error) {
|
|
44
|
+
const outcome = classifyDoCall(error);
|
|
45
|
+
if (attempt >= attempts || !isRetryableDoCall(outcome)) {
|
|
46
|
+
throw new Error(`${outcome}: ${describeError(error)}`, { cause: error });
|
|
47
|
+
}
|
|
48
|
+
await new Promise((resolve) => setTimeout(resolve, 100 * 2 ** attempt));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| Class | What happened | Retry |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `superseded_isolate` | a deploy replaced the isolate mid-call | yes |
|
|
57
|
+
| `connection_lost` | the stub or storage connection dropped | yes |
|
|
58
|
+
| `storage_reset` | storage reset the object: cold start, live write, or timeout | yes |
|
|
59
|
+
| `retryable_flag` | the runtime flagged the error `retryable` itself | yes |
|
|
60
|
+
| `overloaded` | the object shed the call under queue pressure | never |
|
|
61
|
+
| `permanent` | the call ran and the answer is the error | no |
|
|
62
|
+
|
|
63
|
+
`overloaded` is checked first and vetoes the rest, because retrying an
|
|
64
|
+
overloaded object is what overloaded it. Cloudflare's own error-handling
|
|
65
|
+
guidance forbids it.
|
|
66
|
+
|
|
67
|
+
`describeError` gives one line you can log: the error's class, whether the
|
|
68
|
+
text came from a remote isolate, and the condition it classifies as. Reach
|
|
69
|
+
for it wherever you would otherwise write `e?.message ?? String(e)` and get
|
|
70
|
+
back the word `internal error`.
|
|
71
|
+
|
|
72
|
+
## Naming what died
|
|
73
|
+
|
|
74
|
+
`classifyError` maps an error, or a bare stderr line, to one cause:
|
|
75
|
+
`sqlite_nomem`, `sqlite_full`, `oom`, `cpu_exceeded`, `clone_refused`,
|
|
76
|
+
`rpc_timeout`, `subrequest_cap`, `dynamic_worker_cap`, `condemnation`,
|
|
77
|
+
`hard_evict`, or `unknown`.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { classifyError } from '@nimbus-sh/platform';
|
|
81
|
+
|
|
82
|
+
export function remedy(error: unknown): string {
|
|
83
|
+
switch (classifyError(error)) {
|
|
84
|
+
case 'sqlite_full': return 'drain data; reads and DELETEs still work at the wall';
|
|
85
|
+
case 'sqlite_nomem': return 'retry with a smaller transaction';
|
|
86
|
+
case 'clone_refused': return 'ship the payload in slices under the RPC envelope';
|
|
87
|
+
case 'cpu_exceeded': return 're-enter the object; yielding inside the turn buys nothing';
|
|
88
|
+
default: return 'unclassified: keep the message and widen the rules';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The split between `sqlite_full` and `sqlite_nomem` carries the remedy. At the
|
|
94
|
+
storage wall, reads and deletes keep working and you drain data; on NOMEM you
|
|
95
|
+
ask for a smaller transaction.
|
|
96
|
+
|
|
97
|
+
Both `exceededMemory` and `exceededCpu` are uncatchable inside the isolate
|
|
98
|
+
that dies. Only a caller across an RPC boundary sees the message, and
|
|
99
|
+
isolates also vanish with no message at all. A low `cpu_exceeded` count is
|
|
100
|
+
therefore not evidence that CPU was not the cause.
|
|
101
|
+
|
|
102
|
+
`isTransientDoReset` and `isDoOverloaded` answer those two narrower questions
|
|
103
|
+
on their own.
|
|
104
|
+
|
|
105
|
+
## Limits
|
|
106
|
+
|
|
107
|
+
Every constant is a fact about the platform: a hard ceiling, or a measured
|
|
108
|
+
envelope proven safe against one.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { MAX_RPC_SAFE_PAYLOAD_BYTES } from '@nimbus-sh/platform';
|
|
112
|
+
|
|
113
|
+
export function* slices(bytes: Uint8Array): Generator<Uint8Array> {
|
|
114
|
+
for (let at = 0; at < bytes.length; at += MAX_RPC_SAFE_PAYLOAD_BYTES) {
|
|
115
|
+
yield bytes.subarray(at, at + MAX_RPC_SAFE_PAYLOAD_BYTES);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
| Constant | Value | What it bounds |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `CHUNK_SIZE` | 65,536 | the storage and wire quantum every other bound is counted in |
|
|
123
|
+
| `MAX_TX_BLOB_BYTES` | 1 MiB | bytes one storage transaction may hold outstanding; the platform resets the object over the turn, not the total |
|
|
124
|
+
| `MAX_TX_LOGICAL_ROWS` / `MAX_TX_SQL_EXECS` | 256 / 64 | rows and execs in that same transaction |
|
|
125
|
+
| `SQLITE_MAX_ROW_BYTES` | 2,000,000 | bytes per ROW, key length included; budget values against the row bound |
|
|
126
|
+
| `SQLITE_MAX_STATEMENT_BYTES` | 100 KiB | SQL text per exec |
|
|
127
|
+
| `SQLITE_MAX_BOUND_PARAMETERS` | 100 | bound parameters per query; a batched insert breaches this first |
|
|
128
|
+
| `DO_STORAGE_LIMIT_BYTES` | 10,000,000,000 | storage per Durable Object, shared by every facet and clone under it, with no copy-on-write credit |
|
|
129
|
+
| `BLOCK_CONCURRENCY_CANCEL_MS` | 30,000 | how long a `blockConcurrencyWhile()` callback may run before the object is reset |
|
|
130
|
+
| `WS_ATTACHMENT_LIMIT_BYTES` | 16,384 | serialized bytes per hibernatable WebSocket attachment |
|
|
131
|
+
| `MAX_RPC_SAFE_PAYLOAD_BYTES` | 28 MiB | bytes to send in one RPC value, under the platform's 32 MiB |
|
|
132
|
+
|
|
133
|
+
Each constant's doc comment carries the measurement that set it, and says
|
|
134
|
+
whether the value is documented by Cloudflare or probed. `SQLITE_MAX_ROW_BYTES`
|
|
135
|
+
is 2,000,000 against a measured single-value ceiling of 2,199,981 bytes,
|
|
136
|
+
because budgeting a row against the value ceiling would be wrong.
|
|
137
|
+
|
|
138
|
+
The supervisor budget constants (`SUPERVISOR_HEAP_CEILING_BYTES` and the
|
|
139
|
+
allocation budgets beside it) are Nimbus policy derived from the 128 MiB
|
|
140
|
+
isolate ceiling. They are soft admission targets, and the doc comments say so.
|
|
141
|
+
|
|
142
|
+
## The rest of the surface
|
|
143
|
+
|
|
19
144
|
- `heap-estimate.js` / `heavy-alloc-coord.js` / `weighted-credit-pool.js` —
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
145
|
+
an instrumented heap model for a supervisor Durable Object, and the
|
|
146
|
+
weighted admission budget that holds allocations under it.
|
|
147
|
+
`estimateSupervisorHeap` returns a lower bound, and `HEAP_BLIND_SPOTS`
|
|
148
|
+
names the allocation sites it cannot see.
|
|
149
|
+
- `diag-counters.js` / `install-phase.js` — deterministic allocation-site
|
|
150
|
+
counters, because `process.memoryUsage()` returns 0 in a Durable Object,
|
|
151
|
+
plus the phase taxonomy they report against.
|
|
152
|
+
- `oom-discriminator.js` — the failure ring that attributes a death after the
|
|
153
|
+
fact: `recordFailure`, `getFailures`, and a snapshot pair that carries the
|
|
154
|
+
ring across a reset.
|
|
155
|
+
- `rpc-dispose.js` — `disposeRpcResource` and `disposeRpcResources`, explicit
|
|
156
|
+
`Symbol.dispose` for RPC values, including on error paths.
|
|
27
157
|
- `w7-frame.js` — the incremental typed record format for streamed bulk
|
|
28
|
-
filesystem writes, and the batch payload
|
|
158
|
+
filesystem writes, with `encodeWriteBatchStream` and the batch payload
|
|
159
|
+
types it encodes.
|
|
160
|
+
|
|
161
|
+
## Related packages
|
|
29
162
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
163
|
+
[`@nimbus-sh/core`](https://www.npmjs.com/package/@nimbus-sh/core) is the
|
|
164
|
+
backend-agnostic OS, and
|
|
165
|
+
[`@nimbus-sh/fabric`](https://www.npmjs.com/package/@nimbus-sh/fabric) is the
|
|
166
|
+
Durable Object machinery. Both are built on the limits and the taxonomy here.
|
|
33
167
|
|
|
34
168
|
## License
|
|
35
169
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* composition.ts — the ONE seam an embedder wires the fabric through.
|
|
3
|
+
*
|
|
4
|
+
* The fabric mints supervisor bindings and assembles staged boots for the
|
|
5
|
+
* programs it hosts, but the entrypoint class that answers those bindings
|
|
6
|
+
* and the artifact sources a stage names both belong to the embedder. The
|
|
7
|
+
* embedder states them once, in its composition root, with one call:
|
|
8
|
+
*
|
|
9
|
+
* composeFabric({
|
|
10
|
+
* supervisorEntrypoint: 'SupervisorRPC',
|
|
11
|
+
* stagedBootAssembler: (env, stage) => assembleConfig(env, stage),
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* First-write-wins, like every holder in this module: the composition
|
|
15
|
+
* root's module scope runs once per isolate, before any request.
|
|
16
|
+
*
|
|
17
|
+
* `ctx.exports` is runtime state, not composition: workerd mints it per
|
|
18
|
+
* instance, so the embedder captures it where the platform hands it over —
|
|
19
|
+
* the first fetch, or the DO constructor — with {@link adoptCtxExports}.
|
|
20
|
+
*
|
|
21
|
+
* This module stays a leaf (no fabric imports) so helpers (notably
|
|
22
|
+
* isolate-pool.ts) can read `ctx.exports` without transitively importing
|
|
23
|
+
* the Durable Object classes, which is what lets the pool be unit-tested
|
|
24
|
+
* in a plain Node/Bun process.
|
|
25
|
+
*/
|
|
26
|
+
export type EntrypointLoopbackFactory = <Stub = unknown>(options: {
|
|
27
|
+
props: object;
|
|
28
|
+
}) => Stub;
|
|
29
|
+
export type CtxExports = Record<string, EntrypointLoopbackFactory | undefined>;
|
|
30
|
+
export type StagedBootAssembler = (env: unknown, stage: unknown) => Promise<object>;
|
|
31
|
+
export interface FabricComposition {
|
|
32
|
+
supervisorEntrypoint: string;
|
|
33
|
+
stagedBootAssembler?: StagedBootAssembler;
|
|
34
|
+
/** Durable Object namespace binding that owns the workspace. */
|
|
35
|
+
hostNamespace?: string;
|
|
36
|
+
/** RPC method the host forwards to workspace.supervisorOp. */
|
|
37
|
+
hostDispatchMethod?: string;
|
|
38
|
+
}
|
|
39
|
+
export declare const DEFAULT_HOST_NAMESPACE = "NIMBUS_SESSION";
|
|
40
|
+
export declare const DEFAULT_HOST_DISPATCH_METHOD = "supervisorOp";
|
|
41
|
+
/** Composition and loopback exports are first-write-wins for this isolate. */
|
|
42
|
+
export declare function composeFabric(value: FabricComposition): void;
|
|
43
|
+
export declare function adoptCtxExports(value: CtxExports): void;
|
|
44
|
+
export declare function getCtxExports(): CtxExports | null;
|
|
45
|
+
export declare function supervisorEntrypoint(exportsObj?: unknown): EntrypointLoopbackFactory | null;
|
|
46
|
+
export declare function supervisorEntrypointName(): string | null;
|
|
47
|
+
export declare function hostNamespace(): string;
|
|
48
|
+
export declare function hostDispatchMethod(): string;
|
|
49
|
+
export declare function stagedBootAssembler(): StagedBootAssembler;
|
|
50
|
+
//# sourceMappingURL=composition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../src/composition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,CAAC;AAC7F,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,SAAS,CAAC,CAAC;AAC/E,MAAM,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAEpF,MAAM,WAAW,iBAAiB;IAChC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,eAAO,MAAM,sBAAsB,mBAAmB,CAAC;AACvD,eAAO,MAAM,4BAA4B,iBAAiB,CAAC;AAI3D,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAE5D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAEvD;AAED,wBAAgB,aAAa,IAAI,UAAU,GAAG,IAAI,CAEjD;AAED,wBAAgB,oBAAoB,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,yBAAyB,GAAG,IAAI,CAO3F;AAED,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,IAAI,CAExD;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,mBAAmB,IAAI,mBAAmB,CASzD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const DEFAULT_HOST_NAMESPACE = 'NIMBUS_SESSION';
|
|
2
|
+
export const DEFAULT_HOST_DISPATCH_METHOD = 'supervisorOp';
|
|
3
|
+
let composition = null;
|
|
4
|
+
let ctxExports = null;
|
|
5
|
+
/** Composition and loopback exports are first-write-wins for this isolate. */
|
|
6
|
+
export function composeFabric(value) {
|
|
7
|
+
if (!composition)
|
|
8
|
+
composition = value;
|
|
9
|
+
}
|
|
10
|
+
export function adoptCtxExports(value) {
|
|
11
|
+
if (!ctxExports)
|
|
12
|
+
ctxExports = value;
|
|
13
|
+
}
|
|
14
|
+
export function getCtxExports() {
|
|
15
|
+
return ctxExports;
|
|
16
|
+
}
|
|
17
|
+
export function supervisorEntrypoint(exportsObj) {
|
|
18
|
+
const exports = exportsObj ?? ctxExports;
|
|
19
|
+
const name = composition?.supervisorEntrypoint;
|
|
20
|
+
if (!name)
|
|
21
|
+
return null;
|
|
22
|
+
if ((typeof exports !== 'object' && typeof exports !== 'function') || exports === null)
|
|
23
|
+
return null;
|
|
24
|
+
const factory = exports[name];
|
|
25
|
+
return typeof factory === 'function' ? factory : null;
|
|
26
|
+
}
|
|
27
|
+
export function supervisorEntrypointName() {
|
|
28
|
+
return composition?.supervisorEntrypoint ?? null;
|
|
29
|
+
}
|
|
30
|
+
export function hostNamespace() {
|
|
31
|
+
return composition?.hostNamespace ?? DEFAULT_HOST_NAMESPACE;
|
|
32
|
+
}
|
|
33
|
+
export function hostDispatchMethod() {
|
|
34
|
+
return composition?.hostDispatchMethod ?? DEFAULT_HOST_DISPATCH_METHOD;
|
|
35
|
+
}
|
|
36
|
+
export function stagedBootAssembler() {
|
|
37
|
+
const assembler = composition?.stagedBootAssembler;
|
|
38
|
+
if (!assembler) {
|
|
39
|
+
throw new Error('fabric: no staged-boot assembler composed; a \'staged\' boot spec '
|
|
40
|
+
+ 'cannot be assembled without one (composeFabric)');
|
|
41
|
+
}
|
|
42
|
+
return assembler;
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nimbus-sh/platform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "The measured Cloudflare platform truths Nimbus is built on — error taxonomy, limits tables, budget coordination, and wire-format primitives, with zero runtime dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* composition.ts — the ONE seam an embedder wires the fabric through.
|
|
3
|
+
*
|
|
4
|
+
* The fabric mints supervisor bindings and assembles staged boots for the
|
|
5
|
+
* programs it hosts, but the entrypoint class that answers those bindings
|
|
6
|
+
* and the artifact sources a stage names both belong to the embedder. The
|
|
7
|
+
* embedder states them once, in its composition root, with one call:
|
|
8
|
+
*
|
|
9
|
+
* composeFabric({
|
|
10
|
+
* supervisorEntrypoint: 'SupervisorRPC',
|
|
11
|
+
* stagedBootAssembler: (env, stage) => assembleConfig(env, stage),
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* First-write-wins, like every holder in this module: the composition
|
|
15
|
+
* root's module scope runs once per isolate, before any request.
|
|
16
|
+
*
|
|
17
|
+
* `ctx.exports` is runtime state, not composition: workerd mints it per
|
|
18
|
+
* instance, so the embedder captures it where the platform hands it over —
|
|
19
|
+
* the first fetch, or the DO constructor — with {@link adoptCtxExports}.
|
|
20
|
+
*
|
|
21
|
+
* This module stays a leaf (no fabric imports) so helpers (notably
|
|
22
|
+
* isolate-pool.ts) can read `ctx.exports` without transitively importing
|
|
23
|
+
* the Durable Object classes, which is what lets the pool be unit-tested
|
|
24
|
+
* in a plain Node/Bun process.
|
|
25
|
+
*/
|
|
26
|
+
export type EntrypointLoopbackFactory = <Stub = unknown>(options: { props: object }) => Stub;
|
|
27
|
+
export type CtxExports = Record<string, EntrypointLoopbackFactory | undefined>;
|
|
28
|
+
export type StagedBootAssembler = (env: unknown, stage: unknown) => Promise<object>;
|
|
29
|
+
|
|
30
|
+
export interface FabricComposition {
|
|
31
|
+
supervisorEntrypoint: string;
|
|
32
|
+
stagedBootAssembler?: StagedBootAssembler;
|
|
33
|
+
/** Durable Object namespace binding that owns the workspace. */
|
|
34
|
+
hostNamespace?: string;
|
|
35
|
+
/** RPC method the host forwards to workspace.supervisorOp. */
|
|
36
|
+
hostDispatchMethod?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const DEFAULT_HOST_NAMESPACE = 'NIMBUS_SESSION';
|
|
40
|
+
export const DEFAULT_HOST_DISPATCH_METHOD = 'supervisorOp';
|
|
41
|
+
let composition: FabricComposition | null = null;
|
|
42
|
+
let ctxExports: CtxExports | null = null;
|
|
43
|
+
|
|
44
|
+
/** Composition and loopback exports are first-write-wins for this isolate. */
|
|
45
|
+
export function composeFabric(value: FabricComposition): void {
|
|
46
|
+
if (!composition) composition = value;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function adoptCtxExports(value: CtxExports): void {
|
|
50
|
+
if (!ctxExports) ctxExports = value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function getCtxExports(): CtxExports | null {
|
|
54
|
+
return ctxExports;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function supervisorEntrypoint(exportsObj?: unknown): EntrypointLoopbackFactory | null {
|
|
58
|
+
const exports = exportsObj ?? ctxExports;
|
|
59
|
+
const name = composition?.supervisorEntrypoint;
|
|
60
|
+
if (!name) return null;
|
|
61
|
+
if ((typeof exports !== 'object' && typeof exports !== 'function') || exports === null) return null;
|
|
62
|
+
const factory = (exports as Record<string, unknown>)[name];
|
|
63
|
+
return typeof factory === 'function' ? factory as EntrypointLoopbackFactory : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function supervisorEntrypointName(): string | null {
|
|
67
|
+
return composition?.supervisorEntrypoint ?? null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function hostNamespace(): string {
|
|
71
|
+
return composition?.hostNamespace ?? DEFAULT_HOST_NAMESPACE;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function hostDispatchMethod(): string {
|
|
75
|
+
return composition?.hostDispatchMethod ?? DEFAULT_HOST_DISPATCH_METHOD;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function stagedBootAssembler(): StagedBootAssembler {
|
|
79
|
+
const assembler = composition?.stagedBootAssembler;
|
|
80
|
+
if (!assembler) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
'fabric: no staged-boot assembler composed; a \'staged\' boot spec '
|
|
83
|
+
+ 'cannot be assembled without one (composeFabric)',
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return assembler;
|
|
87
|
+
}
|