@mysten-incubation/devstack 0.3.0 → 0.5.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 +10 -11
- package/dashboard-ui/assets/{grpc-Clz0oOtc.js → grpc-CpkDu4SA.js} +1 -1
- package/dashboard-ui/assets/{index-Cmqv9kiU.js → index-jLPRmjst.js} +2 -2
- package/dashboard-ui/index.html +1 -1
- package/dist/api/inference-network.d.mts +5 -5
- package/dist/api/inference-network.mjs +5 -5
- package/dist/api/inference-network.mjs.map +1 -1
- package/dist/api/run-stack-internal.mjs +1 -3
- package/dist/api/run-stack-internal.mjs.map +1 -1
- package/dist/build-integrations/dapp-kit/index.d.mts +12 -0
- package/dist/build-integrations/dapp-kit/index.mjs +14 -1
- package/dist/build-integrations/dapp-kit/index.mjs.map +1 -1
- package/dist/build-integrations/playwright/errors.d.mts +1 -1
- package/dist/build-integrations/playwright/errors.mjs.map +1 -1
- package/dist/build-integrations/playwright/index.d.mts +2 -2
- package/dist/build-integrations/playwright/index.mjs +2 -2
- package/dist/build-integrations/playwright/wallet-context.d.mts +2 -1
- package/dist/build-integrations/playwright/wallet-context.mjs +141 -41
- package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
- package/dist/build-integrations/runtime/dapp-kit-slot.mjs +1 -1
- package/dist/build-integrations/runtime/dapp-kit-slot.mjs.map +1 -1
- package/dist/build-integrations/vite/index.d.mts +54 -42
- package/dist/build-integrations/vite/index.mjs +239 -106
- package/dist/build-integrations/vite/index.mjs.map +1 -1
- package/dist/build-integrations/vitest/global-setup.mjs +8 -8
- package/dist/build-integrations/vitest/global-setup.mjs.map +1 -1
- package/dist/cli/main.mjs +3 -2
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/wirings/apply.mjs +0 -1
- package/dist/cli/wirings/apply.mjs.map +1 -1
- package/dist/cli/wirings/codegen.mjs +2 -3
- package/dist/cli/wirings/codegen.mjs.map +1 -1
- package/dist/cli/wirings/dump-deployment.mjs +141 -0
- package/dist/cli/wirings/dump-deployment.mjs.map +1 -0
- package/dist/cli/wirings/snapshot.mjs +0 -1
- package/dist/cli/wirings/snapshot.mjs.map +1 -1
- package/dist/contracts/codegenable.d.mts +22 -25
- package/dist/contracts/codegenable.mjs +35 -4
- package/dist/contracts/codegenable.mjs.map +1 -1
- package/dist/contracts/config-bindings.mjs +43 -18
- package/dist/contracts/config-bindings.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/orchestrators/boot.d.mts +0 -10
- package/dist/orchestrators/boot.mjs +19 -59
- package/dist/orchestrators/boot.mjs.map +1 -1
- package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
- package/dist/orchestrators/codegen/config-runtime.mjs +137 -112
- package/dist/orchestrators/codegen/config-runtime.mjs.map +1 -1
- package/dist/orchestrators/codegen/deployment-network-file.mjs +130 -0
- package/dist/orchestrators/codegen/deployment-network-file.mjs.map +1 -0
- package/dist/orchestrators/codegen/deployment-strict.mjs +77 -0
- package/dist/orchestrators/codegen/deployment-strict.mjs.map +1 -0
- package/dist/orchestrators/codegen/deployment.d.mts +63 -0
- package/dist/orchestrators/codegen/deployment.mjs +153 -0
- package/dist/orchestrators/codegen/deployment.mjs.map +1 -0
- package/dist/orchestrators/codegen/errors.mjs +7 -9
- package/dist/orchestrators/codegen/errors.mjs.map +1 -1
- package/dist/orchestrators/codegen/format.mjs +20 -1
- package/dist/orchestrators/codegen/format.mjs.map +1 -1
- package/dist/orchestrators/codegen/paths.mjs +1 -8
- package/dist/orchestrators/codegen/paths.mjs.map +1 -1
- package/dist/orchestrators/codegen/service.mjs +354 -141
- package/dist/orchestrators/codegen/service.mjs.map +1 -1
- package/dist/orchestrators/layers.mjs +0 -1
- package/dist/orchestrators/layers.mjs.map +1 -1
- package/dist/orchestrators/network-options.d.mts +37 -35
- package/dist/orchestrators/network-options.mjs +13 -10
- package/dist/orchestrators/network-options.mjs.map +1 -1
- package/dist/plugins/account/codegen.d.mts +3 -4
- package/dist/plugins/account/codegen.mjs +5 -7
- package/dist/plugins/account/codegen.mjs.map +1 -1
- package/dist/plugins/account/index.mjs +1 -3
- package/dist/plugins/account/index.mjs.map +1 -1
- package/dist/plugins/coin/codegen.mjs +5 -5
- package/dist/plugins/coin/codegen.mjs.map +1 -1
- package/dist/plugins/coin/index.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/root.mjs +3 -1
- package/dist/plugins/dashboard/schema/root.mjs.map +1 -1
- package/dist/plugins/deepbook/codegen.mjs +4 -4
- package/dist/plugins/deepbook/codegen.mjs.map +1 -1
- package/dist/plugins/deepbook/index.d.mts +36 -6
- package/dist/plugins/deepbook/index.mjs +52 -16
- package/dist/plugins/deepbook/index.mjs.map +1 -1
- package/dist/plugins/internal/codegen-helpers.mjs +0 -1
- package/dist/plugins/internal/codegen-helpers.mjs.map +1 -1
- package/dist/plugins/package/codegen.d.mts +1 -12
- package/dist/plugins/package/codegen.mjs +43 -25
- package/dist/plugins/package/codegen.mjs.map +1 -1
- package/dist/plugins/package/dep-resolution.mjs +1 -1
- package/dist/plugins/package/dep-resolution.mjs.map +1 -1
- package/dist/plugins/package/index.d.mts +14 -11
- package/dist/plugins/package/index.mjs +6 -7
- package/dist/plugins/package/index.mjs.map +1 -1
- package/dist/plugins/seal/codegen.d.mts +5 -0
- package/dist/plugins/seal/codegen.mjs +55 -12
- package/dist/plugins/seal/codegen.mjs.map +1 -1
- package/dist/plugins/seal/index.d.mts +64 -19
- package/dist/plugins/seal/index.mjs +26 -36
- package/dist/plugins/seal/index.mjs.map +1 -1
- package/dist/plugins/seal/mode/fork-known.d.mts +0 -2
- package/dist/plugins/seal/mode/fork-known.mjs +11 -22
- package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
- package/dist/plugins/seal/mode/live.d.mts +36 -13
- package/dist/plugins/seal/mode/live.mjs +104 -43
- package/dist/plugins/seal/mode/live.mjs.map +1 -1
- package/dist/plugins/seal/mode/local-keygen.mjs +2 -1
- package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
- package/dist/plugins/seal/registry-publish.d.mts +15 -3
- package/dist/plugins/seal/registry-publish.mjs.map +1 -1
- package/dist/plugins/sui/codegen.mjs +5 -4
- package/dist/plugins/sui/codegen.mjs.map +1 -1
- package/dist/plugins/sui/index.mjs +1 -2
- package/dist/plugins/sui/index.mjs.map +1 -1
- package/dist/plugins/wallet/codegen.d.mts +8 -12
- package/dist/plugins/wallet/codegen.mjs +22 -15
- package/dist/plugins/wallet/codegen.mjs.map +1 -1
- package/dist/plugins/wallet/index.d.mts +4 -3
- package/dist/plugins/wallet/index.mjs +1 -1
- package/dist/plugins/wallet/index.mjs.map +1 -1
- package/dist/plugins/wallet/service.d.mts +2 -2
- package/dist/plugins/wallet/service.mjs +2 -3
- package/dist/plugins/wallet/service.mjs.map +1 -1
- package/dist/plugins/walrus/codegen.mjs +4 -4
- package/dist/plugins/walrus/codegen.mjs.map +1 -1
- package/dist/plugins/walrus/index.d.mts +137 -8
- package/dist/plugins/walrus/index.mjs +46 -9
- package/dist/plugins/walrus/index.mjs.map +1 -1
- package/dist/plugins/walrus/mode/known-deploy.d.mts +5 -1
- package/dist/plugins/walrus/mode/known-deploy.mjs +13 -10
- package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
- package/dist/substrate/manifest.d.mts +17 -22
- package/dist/substrate/manifest.mjs +4 -6
- package/dist/substrate/manifest.mjs.map +1 -1
- package/dist/substrate/plugin-ctx.d.mts +6 -6
- package/dist/substrate/plugin-ctx.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
- package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
- package/dist/substrate/runtime/paths.mjs +1 -1
- package/dist/surfaces/cli/command-tree.mjs +6 -6
- package/dist/surfaces/cli/command-tree.mjs.map +1 -1
- package/dist/surfaces/cli/index.mjs +6 -4
- package/dist/surfaces/cli/index.mjs.map +1 -1
- package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/cli/wirings/dump-ids.mjs +0 -86
- package/dist/cli/wirings/dump-ids.mjs.map +0 -1
- package/dist/orchestrators/codegen/id-config.d.mts +0 -1
- package/dist/orchestrators/codegen/id-config.mjs +0 -107
- package/dist/orchestrators/codegen/id-config.mjs.map +0 -1
- package/dist/orchestrators/codegen/output-location.mjs +0 -32
- package/dist/orchestrators/codegen/output-location.mjs.map +0 -1
- package/dist/plugins/seal/service.mjs +0 -17
- package/dist/plugins/seal/service.mjs.map +0 -1
package/dashboard-ui/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>devstack dashboard</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-jLPRmjst.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-B82Bh84P.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
@@ -31,11 +31,11 @@ type ForkDevstackNetworkName = (typeof FORK_DEVSTACK_NETWORK_NAMES)[number];
|
|
|
31
31
|
type DevstackNetworkName = (typeof DEVSTACK_NETWORK_NAMES)[number];
|
|
32
32
|
type LiveDevstackNetworkName = 'testnet' | 'mainnet' | 'devnet';
|
|
33
33
|
/** The network every local stack actually runs, and the active key in the
|
|
34
|
-
* generated `config.ts` (`config.network`, `networks.<key
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
34
|
+
* generated `config.ts` (`config.network`, `networks.<key>`). A fork runs on
|
|
35
|
+
* a local node too, so this is `localnet` for every mode — the fork's
|
|
36
|
+
* upstream identity lives separately in the network entry's `forkUpstream`.
|
|
37
|
+
* The sui and package codegen contributions share this one literal so their
|
|
38
|
+
* active-network keys can never drift. */
|
|
39
39
|
declare const LOCAL_NETWORK_NAME = "localnet";
|
|
40
40
|
type ParsedDevstackNetwork = {
|
|
41
41
|
readonly mode: 'local';
|
|
@@ -82,11 +82,11 @@ const FORK_DEVSTACK_NETWORK_NAMES = [
|
|
|
82
82
|
];
|
|
83
83
|
const DEVSTACK_NETWORK_NAMES = [...ACTIVE_DEVSTACK_NETWORK_NAMES, ...FORK_DEVSTACK_NETWORK_NAMES];
|
|
84
84
|
/** The network every local stack actually runs, and the active key in the
|
|
85
|
-
* generated `config.ts` (`config.network`, `networks.<key
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
85
|
+
* generated `config.ts` (`config.network`, `networks.<key>`). A fork runs on
|
|
86
|
+
* a local node too, so this is `localnet` for every mode — the fork's
|
|
87
|
+
* upstream identity lives separately in the network entry's `forkUpstream`.
|
|
88
|
+
* The sui and package codegen contributions share this one literal so their
|
|
89
|
+
* active-network keys can never drift. */
|
|
90
90
|
const LOCAL_NETWORK_NAME = "localnet";
|
|
91
91
|
/** Canonical network names accepted as input. There is no alias table:
|
|
92
92
|
* a network has ONE spelling (`localnet`, `testnet`, …). The chain id is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inference-network.mjs","names":[],"sources":["../../src/api/inference-network.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\n\nimport { Effect } from 'effect';\n\n/** Canonical default stack name (`'main'`). The literal's single source\n * of truth lives HERE: the discovery ladder\n * (`build-integrations/runtime/resolve-discovery-env.ts`) imports\n * `inferPackageNameFromCwd` from this module for its package-name\n * rung, so this module must not import the ladder's consts back — a\n * two-way top-level-const read would TDZ-crash under one import\n * order. The ladder re-exports this value as `DEFAULT_DISCOVERY_STACK`\n * (and the vitest env module as its own `DEFAULT_STACK_NAME`) so all\n * entry points still agree on one literal. */\nexport const DEFAULT_STACK_NAME = 'main';\n\nexport interface StackNameResolutionOptions {\n\treadonly explicit?: string;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly cwd?: string;\n\treadonly defaultName?: string;\n}\n\nexport interface AppNameResolutionOptions {\n\treadonly explicit?: string;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly cwd?: string;\n\treadonly defaultName?: string;\n}\n\n/** Normalize a candidate name: trim, strip an `@scope/` prefix, then\n * strip any leading non-alphanumeric run. Returns `undefined` for empty\n * or whitespace-only input, and for inputs that normalize to an empty\n * string. Shared by every inferred-name resolver so explicit / env /\n * package paths all reject the same junk. */\nconst usefulName = (value: string | undefined): string | undefined => {\n\tconst trimmed = value?.trim();\n\tif (trimmed === undefined || trimmed.length === 0) return undefined;\n\tconst unscoped = trimmed.replace(/^@[^/]+\\//, '').replace(/^[^a-zA-Z0-9]+/, '');\n\treturn unscoped.length > 0 ? unscoped : undefined;\n};\n\nexport const readPackageName = (dir: string): string | undefined => {\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(resolve(dir, 'package.json'), 'utf8')) as {\n\t\t\treadonly name?: string;\n\t\t};\n\t\treturn typeof pkg.name === 'string' ? usefulName(pkg.name) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n};\n\nexport const inferPackageNameFromCwd = (cwd: string = process.cwd()): string | undefined => {\n\tlet dir = resolve(cwd);\n\tfor (let i = 0; i < 32; i += 1) {\n\t\tconst name = readPackageName(dir);\n\t\tif (name !== undefined) return name;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return undefined;\n\t\tdir = parent;\n\t}\n\treturn undefined;\n};\n\n/** Shared `explicit > env > package > default` precedence ladder for the\n * app/stack name resolvers. Each input is fed through `usefulName` so a\n * whitespace-only or junk-prefixed value falls through to the next rung\n * rather than poisoning identity. `envKey` is the environment variable\n * whose value (under `env`) supplies the env rung. */\nconst resolveInferredName = (params: {\n\treadonly explicit: string | undefined;\n\treadonly envKey: string;\n\treadonly env: Readonly<Record<string, string | undefined>> | undefined;\n\treadonly cwd: string | undefined;\n\treadonly defaultName: string;\n}): string => {\n\tconst explicit = usefulName(params.explicit);\n\tif (explicit !== undefined) return explicit;\n\tconst env = params.env ?? process.env;\n\tconst fromEnv = usefulName(env[params.envKey]);\n\tif (fromEnv !== undefined) return fromEnv;\n\tconst fromPackage = inferPackageNameFromCwd(params.cwd);\n\tif (fromPackage !== undefined) return fromPackage;\n\treturn params.defaultName;\n};\n\nexport const resolveStackName = (options: StackNameResolutionOptions = {}): string =>\n\tresolveInferredName({\n\t\texplicit: options.explicit,\n\t\tenvKey: 'DEVSTACK_STACK',\n\t\tenv: options.env,\n\t\tcwd: options.cwd,\n\t\tdefaultName: options.defaultName ?? DEFAULT_STACK_NAME,\n\t});\n\nexport const resolveAppName = (options: AppNameResolutionOptions = {}): string =>\n\tresolveInferredName({\n\t\texplicit: options.explicit,\n\t\tenvKey: 'DEVSTACK_APP',\n\t\tenv: options.env,\n\t\tcwd: options.cwd,\n\t\tdefaultName: options.defaultName ?? 'devstack',\n\t});\n\nexport const ACTIVE_DEVSTACK_NETWORK_NAMES = ['localnet', 'testnet', 'mainnet', 'devnet'] as const;\n\nexport const FORK_DEVSTACK_NETWORK_NAMES = ['testnet-fork', 'mainnet-fork', 'devnet-fork'] as const;\n\nexport const DEVSTACK_NETWORK_NAMES = [\n\t...ACTIVE_DEVSTACK_NETWORK_NAMES,\n\t...FORK_DEVSTACK_NETWORK_NAMES,\n] as const;\n\nexport type ActiveDevstackNetworkName = (typeof ACTIVE_DEVSTACK_NETWORK_NAMES)[number];\nexport type ForkDevstackNetworkName = (typeof FORK_DEVSTACK_NETWORK_NAMES)[number];\nexport type DevstackNetworkName = (typeof DEVSTACK_NETWORK_NAMES)[number];\nexport type LiveDevstackNetworkName = 'testnet' | 'mainnet' | 'devnet';\n\n/** The network every local stack actually runs, and the active key in the\n * generated `config.ts` (`config.network`, `networks.<key>`, each package's\n * `byNetwork.<key>`). A fork runs on a local node too, so this is `localnet`\n * for every mode — the fork's upstream identity lives separately in the\n * network entry's `forkUpstream`. The sui and package codegen contributions\n * share this one literal so their active-network keys can never drift. */\nexport const LOCAL_NETWORK_NAME = 'localnet' satisfies DevstackNetworkName;\n\nexport type ParsedDevstackNetwork =\n\t| {\n\t\t\treadonly mode: 'local';\n\t\t\treadonly name: 'localnet';\n\t }\n\t| {\n\t\t\treadonly mode: 'live';\n\t\t\treadonly name: LiveDevstackNetworkName;\n\t\t\treadonly network: LiveDevstackNetworkName;\n\t }\n\t| {\n\t\t\treadonly mode: 'fork';\n\t\t\treadonly name: ForkDevstackNetworkName;\n\t\t\treadonly upstream: LiveDevstackNetworkName;\n\t };\n\n/** Canonical network names accepted as input. There is no alias table:\n * a network has ONE spelling (`localnet`, `testnet`, …). The chain id is\n * that name with a `sui:` prefix (see {@link chainIdForNetwork}); the reverse\n * is stripping the prefix (see {@link networkNameFromChain}). */\nconst NETWORK_NAME_SET: ReadonlySet<string> = new Set(DEVSTACK_NETWORK_NAMES);\n\n/** The sole network→chain mapping: prefix the canonical name with `sui:`.\n * `localnet` → `sui:localnet`, `testnet` → `sui:testnet`. No lookup table. */\nexport const chainIdForNetwork = (name: DevstackNetworkName): string => `sui:${name}`;\n\n/** Inverse of {@link chainIdForNetwork}: strip the `sui:` prefix to recover the\n * network name. Returns the input unchanged if it carries no prefix. */\nexport const networkNameFromChain = (chain: string): string => chain.replace(/^sui:/, '');\n\nexport class DevstackNetworkParseError extends Error {\n\treadonly _tag = 'DevstackNetworkParseError';\n\treadonly value: string;\n\treadonly source: string;\n\treadonly supported: ReadonlyArray<DevstackNetworkName>;\n\n\tconstructor(args: { readonly value: string; readonly source: string }) {\n\t\tsuper(\n\t\t\t`${args.source} must be one of: ${DEVSTACK_NETWORK_NAMES.join(', ')} (got ${JSON.stringify(args.value)})`,\n\t\t);\n\t\tthis.name = 'DevstackNetworkParseError';\n\t\tthis.value = args.value;\n\t\tthis.source = args.source;\n\t\tthis.supported = DEVSTACK_NETWORK_NAMES;\n\t}\n}\n\nexport const parseDevstackNetwork = (\n\tvalue: string | undefined,\n\tsource = 'DEVSTACK_NETWORK',\n): ParsedDevstackNetwork => {\n\tconst raw = value?.trim();\n\tconst candidate = raw === undefined || raw.length === 0 ? 'localnet' : raw;\n\tif (!NETWORK_NAME_SET.has(candidate)) {\n\t\tthrow new DevstackNetworkParseError({ value: raw ?? '', source });\n\t}\n\tconst name = candidate as DevstackNetworkName;\n\tswitch (name) {\n\t\tcase 'localnet':\n\t\t\treturn { mode: 'local', name };\n\t\tcase 'testnet':\n\t\tcase 'mainnet':\n\t\tcase 'devnet':\n\t\t\treturn { mode: 'live', name, network: name };\n\t\tcase 'testnet-fork':\n\t\tcase 'mainnet-fork':\n\t\tcase 'devnet-fork':\n\t\t\treturn {\n\t\t\t\tmode: 'fork',\n\t\t\t\tname,\n\t\t\t\tupstream: name.replace(/-fork$/, '') as LiveDevstackNetworkName,\n\t\t\t};\n\t}\n};\n\nexport const parseDevstackNetworkName = (\n\tvalue: string | undefined,\n\tsource = 'DEVSTACK_NETWORK',\n): DevstackNetworkName => parseDevstackNetwork(value, source).name;\n\n// ---------------------------------------------------------------------------\n// resolveNetwork — single precedence ladder for every entry point.\n// ---------------------------------------------------------------------------\n\n/**\n * Options for `resolveNetwork`. `explicit` is the caller-supplied\n * override (typically a CLI flag or programmatic option). `env` is the\n * environment value (typically `process.env.DEVSTACK_NETWORK`).\n * `defaultName` is the final fallback when both miss. The precedence is\n * explicit > env > default. The `source` string is embedded in the\n * parse error's `source` field so the diagnosis names the offending\n * input (e.g. `--network` vs `DEVSTACK_NETWORK`).\n */\nexport interface ResolveNetworkOptions {\n\treadonly explicit?: string | undefined;\n\treadonly env?: string | undefined;\n\treadonly defaultName?: string;\n\treadonly explicitSource?: string;\n\treadonly envSource?: string;\n}\n\n/** Default network when neither caller nor env provides one — the canonical\n * name (not a chain id or a `ParsedDevstackNetwork`), because every call site\n * folds it through `parseDevstackNetwork` first, exactly like an env value. */\nexport const DEFAULT_DEVSTACK_NETWORK = 'localnet' as const;\n\n/**\n * Result shape — the typed parse of the network the resolver picked.\n * Consumers branch on `parsed.mode` (local/live/fork) or derive the chain id\n * via `chainIdForNetwork(parsed.name)`; there is no second \"raw spelling\"\n * field, because the chain id is canonical and never depends on input form.\n */\nexport interface ResolvedDevstackNetwork {\n\treadonly parsed: ParsedDevstackNetwork;\n}\n\nconst pickInput = (\n\toptions: ResolveNetworkOptions,\n): { readonly value: string; readonly source: string } => {\n\tif (options.explicit !== undefined && options.explicit.length > 0) {\n\t\treturn { value: options.explicit, source: options.explicitSource ?? '--network' };\n\t}\n\tif (options.env !== undefined && options.env.length > 0) {\n\t\treturn { value: options.env, source: options.envSource ?? 'DEVSTACK_NETWORK' };\n\t}\n\treturn { value: options.defaultName ?? DEFAULT_DEVSTACK_NETWORK, source: 'default' };\n};\n\n/**\n * Resolve a network from the canonical explicit > env > default ladder\n * and parse the winning value through `parseDevstackNetwork`. Returns\n * the typed parse alongside the raw input the resolver picked. A\n * malformed value surfaces as `DevstackNetworkParseError` on the\n * failure channel so CLI / library boot fail fast with a structured\n * error rather than a downstream cryptic message.\n *\n * Used by both `api/run-stack.ts` (library embedding) and the CLI\n * identity resolver so the two paths share one precedence rule + one\n * parse error type.\n */\nexport const resolveNetwork = (\n\toptions: ResolveNetworkOptions = {},\n): Effect.Effect<ResolvedDevstackNetwork, DevstackNetworkParseError> =>\n\tEffect.try({\n\t\ttry: (): ResolvedDevstackNetwork => {\n\t\t\tconst { value, source } = pickInput(options);\n\t\t\treturn { parsed: parseDevstackNetwork(value, source) };\n\t\t},\n\t\tcatch: (cause) => cause as DevstackNetworkParseError,\n\t});\n\n/**\n * Sync sibling of `resolveNetwork` for entry points that aren't running\n * inside an Effect (the CLI identity resolver builds a plain TS record).\n * Throws `DevstackNetworkParseError` on bad input — caller's job to lift\n * into a typed failure channel if needed.\n */\nexport const resolveNetworkSync = (\n\toptions: ResolveNetworkOptions = {},\n): ResolvedDevstackNetwork => {\n\tconst { value, source } = pickInput(options);\n\treturn { parsed: parseDevstackNetwork(value, source) };\n};\n\n// ---------------------------------------------------------------------------\n// resolveStateDir — single precedence ladder for the on-disk runtime root.\n// ---------------------------------------------------------------------------\n\nexport interface ResolveStateDirOptions {\n\t/** Caller-supplied `runtimeRoot` override (the canonical\n\t * `RunStackOptions.runtimeRoot` / CLI `--state-dir` field). Wins\n\t * over every other input. */\n\treadonly runtimeRoot?: string;\n\t/** Caller-supplied `stateDir` override (the\n\t * `DevstackOptions.stateDir` field on `defineDevstack(...)`).\n\t * Lower precedence than `runtimeRoot`. */\n\treadonly stateDir?: string;\n\t/** Env-var value (typically `process.env.DEVSTACK_STATE_DIR`).\n\t * Lower precedence than either explicit override. */\n\treadonly env?: string;\n\t/** Working directory used for the `.devstack` fallback and for\n\t * resolving any relative override path. */\n\treadonly cwd: string;\n}\n\n/**\n * Resolve the substrate's on-disk runtime root using the canonical\n * precedence ladder: `runtimeRoot` > `stateDir` > `env` > `<cwd>/.devstack`.\n * Relative paths are resolved against `cwd`; absolute paths pass through.\n *\n * Shared by `runStack`, the CLI bin, and the build-integrations\n * discover module so all three entry points agree on the same ladder.\n */\nexport const resolveStateDir = (options: ResolveStateDirOptions): string => {\n\tconst pick =\n\t\t(options.runtimeRoot !== undefined && options.runtimeRoot.length > 0\n\t\t\t? options.runtimeRoot\n\t\t\t: undefined) ??\n\t\t(options.stateDir !== undefined && options.stateDir.length > 0\n\t\t\t? options.stateDir\n\t\t\t: undefined) ??\n\t\t(options.env !== undefined && options.env.length > 0 ? options.env : undefined);\n\tif (pick === undefined) return resolve(options.cwd, '.devstack');\n\treturn resolve(options.cwd, pick);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"inference-network.mjs","names":[],"sources":["../../src/api/inference-network.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\n\nimport { Effect } from 'effect';\n\n/** Canonical default stack name (`'main'`). The literal's single source\n * of truth lives HERE: the discovery ladder\n * (`build-integrations/runtime/resolve-discovery-env.ts`) imports\n * `inferPackageNameFromCwd` from this module for its package-name\n * rung, so this module must not import the ladder's consts back — a\n * two-way top-level-const read would TDZ-crash under one import\n * order. The ladder re-exports this value as `DEFAULT_DISCOVERY_STACK`\n * (and the vitest env module as its own `DEFAULT_STACK_NAME`) so all\n * entry points still agree on one literal. */\nexport const DEFAULT_STACK_NAME = 'main';\n\nexport interface StackNameResolutionOptions {\n\treadonly explicit?: string;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly cwd?: string;\n\treadonly defaultName?: string;\n}\n\nexport interface AppNameResolutionOptions {\n\treadonly explicit?: string;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly cwd?: string;\n\treadonly defaultName?: string;\n}\n\n/** Normalize a candidate name: trim, strip an `@scope/` prefix, then\n * strip any leading non-alphanumeric run. Returns `undefined` for empty\n * or whitespace-only input, and for inputs that normalize to an empty\n * string. Shared by every inferred-name resolver so explicit / env /\n * package paths all reject the same junk. */\nconst usefulName = (value: string | undefined): string | undefined => {\n\tconst trimmed = value?.trim();\n\tif (trimmed === undefined || trimmed.length === 0) return undefined;\n\tconst unscoped = trimmed.replace(/^@[^/]+\\//, '').replace(/^[^a-zA-Z0-9]+/, '');\n\treturn unscoped.length > 0 ? unscoped : undefined;\n};\n\nexport const readPackageName = (dir: string): string | undefined => {\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(resolve(dir, 'package.json'), 'utf8')) as {\n\t\t\treadonly name?: string;\n\t\t};\n\t\treturn typeof pkg.name === 'string' ? usefulName(pkg.name) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n};\n\nexport const inferPackageNameFromCwd = (cwd: string = process.cwd()): string | undefined => {\n\tlet dir = resolve(cwd);\n\tfor (let i = 0; i < 32; i += 1) {\n\t\tconst name = readPackageName(dir);\n\t\tif (name !== undefined) return name;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return undefined;\n\t\tdir = parent;\n\t}\n\treturn undefined;\n};\n\n/** Shared `explicit > env > package > default` precedence ladder for the\n * app/stack name resolvers. Each input is fed through `usefulName` so a\n * whitespace-only or junk-prefixed value falls through to the next rung\n * rather than poisoning identity. `envKey` is the environment variable\n * whose value (under `env`) supplies the env rung. */\nconst resolveInferredName = (params: {\n\treadonly explicit: string | undefined;\n\treadonly envKey: string;\n\treadonly env: Readonly<Record<string, string | undefined>> | undefined;\n\treadonly cwd: string | undefined;\n\treadonly defaultName: string;\n}): string => {\n\tconst explicit = usefulName(params.explicit);\n\tif (explicit !== undefined) return explicit;\n\tconst env = params.env ?? process.env;\n\tconst fromEnv = usefulName(env[params.envKey]);\n\tif (fromEnv !== undefined) return fromEnv;\n\tconst fromPackage = inferPackageNameFromCwd(params.cwd);\n\tif (fromPackage !== undefined) return fromPackage;\n\treturn params.defaultName;\n};\n\nexport const resolveStackName = (options: StackNameResolutionOptions = {}): string =>\n\tresolveInferredName({\n\t\texplicit: options.explicit,\n\t\tenvKey: 'DEVSTACK_STACK',\n\t\tenv: options.env,\n\t\tcwd: options.cwd,\n\t\tdefaultName: options.defaultName ?? DEFAULT_STACK_NAME,\n\t});\n\nexport const resolveAppName = (options: AppNameResolutionOptions = {}): string =>\n\tresolveInferredName({\n\t\texplicit: options.explicit,\n\t\tenvKey: 'DEVSTACK_APP',\n\t\tenv: options.env,\n\t\tcwd: options.cwd,\n\t\tdefaultName: options.defaultName ?? 'devstack',\n\t});\n\nexport const ACTIVE_DEVSTACK_NETWORK_NAMES = ['localnet', 'testnet', 'mainnet', 'devnet'] as const;\n\nexport const FORK_DEVSTACK_NETWORK_NAMES = ['testnet-fork', 'mainnet-fork', 'devnet-fork'] as const;\n\nexport const DEVSTACK_NETWORK_NAMES = [\n\t...ACTIVE_DEVSTACK_NETWORK_NAMES,\n\t...FORK_DEVSTACK_NETWORK_NAMES,\n] as const;\n\nexport type ActiveDevstackNetworkName = (typeof ACTIVE_DEVSTACK_NETWORK_NAMES)[number];\nexport type ForkDevstackNetworkName = (typeof FORK_DEVSTACK_NETWORK_NAMES)[number];\nexport type DevstackNetworkName = (typeof DEVSTACK_NETWORK_NAMES)[number];\nexport type LiveDevstackNetworkName = 'testnet' | 'mainnet' | 'devnet';\n\n/** The network every local stack actually runs, and the active key in the\n * generated `config.ts` (`config.network`, `networks.<key>`). A fork runs on\n * a local node too, so this is `localnet` for every mode — the fork's\n * upstream identity lives separately in the network entry's `forkUpstream`.\n * The sui and package codegen contributions share this one literal so their\n * active-network keys can never drift. */\nexport const LOCAL_NETWORK_NAME = 'localnet' satisfies DevstackNetworkName;\n\nexport type ParsedDevstackNetwork =\n\t| {\n\t\t\treadonly mode: 'local';\n\t\t\treadonly name: 'localnet';\n\t }\n\t| {\n\t\t\treadonly mode: 'live';\n\t\t\treadonly name: LiveDevstackNetworkName;\n\t\t\treadonly network: LiveDevstackNetworkName;\n\t }\n\t| {\n\t\t\treadonly mode: 'fork';\n\t\t\treadonly name: ForkDevstackNetworkName;\n\t\t\treadonly upstream: LiveDevstackNetworkName;\n\t };\n\n/** Canonical network names accepted as input. There is no alias table:\n * a network has ONE spelling (`localnet`, `testnet`, …). The chain id is\n * that name with a `sui:` prefix (see {@link chainIdForNetwork}); the reverse\n * is stripping the prefix (see {@link networkNameFromChain}). */\nconst NETWORK_NAME_SET: ReadonlySet<string> = new Set(DEVSTACK_NETWORK_NAMES);\n\n/** The sole network→chain mapping: prefix the canonical name with `sui:`.\n * `localnet` → `sui:localnet`, `testnet` → `sui:testnet`. No lookup table. */\nexport const chainIdForNetwork = (name: DevstackNetworkName): string => `sui:${name}`;\n\n/** Inverse of {@link chainIdForNetwork}: strip the `sui:` prefix to recover the\n * network name. Returns the input unchanged if it carries no prefix. */\nexport const networkNameFromChain = (chain: string): string => chain.replace(/^sui:/, '');\n\nexport class DevstackNetworkParseError extends Error {\n\treadonly _tag = 'DevstackNetworkParseError';\n\treadonly value: string;\n\treadonly source: string;\n\treadonly supported: ReadonlyArray<DevstackNetworkName>;\n\n\tconstructor(args: { readonly value: string; readonly source: string }) {\n\t\tsuper(\n\t\t\t`${args.source} must be one of: ${DEVSTACK_NETWORK_NAMES.join(', ')} (got ${JSON.stringify(args.value)})`,\n\t\t);\n\t\tthis.name = 'DevstackNetworkParseError';\n\t\tthis.value = args.value;\n\t\tthis.source = args.source;\n\t\tthis.supported = DEVSTACK_NETWORK_NAMES;\n\t}\n}\n\nexport const parseDevstackNetwork = (\n\tvalue: string | undefined,\n\tsource = 'DEVSTACK_NETWORK',\n): ParsedDevstackNetwork => {\n\tconst raw = value?.trim();\n\tconst candidate = raw === undefined || raw.length === 0 ? 'localnet' : raw;\n\tif (!NETWORK_NAME_SET.has(candidate)) {\n\t\tthrow new DevstackNetworkParseError({ value: raw ?? '', source });\n\t}\n\tconst name = candidate as DevstackNetworkName;\n\tswitch (name) {\n\t\tcase 'localnet':\n\t\t\treturn { mode: 'local', name };\n\t\tcase 'testnet':\n\t\tcase 'mainnet':\n\t\tcase 'devnet':\n\t\t\treturn { mode: 'live', name, network: name };\n\t\tcase 'testnet-fork':\n\t\tcase 'mainnet-fork':\n\t\tcase 'devnet-fork':\n\t\t\treturn {\n\t\t\t\tmode: 'fork',\n\t\t\t\tname,\n\t\t\t\tupstream: name.replace(/-fork$/, '') as LiveDevstackNetworkName,\n\t\t\t};\n\t}\n};\n\nexport const parseDevstackNetworkName = (\n\tvalue: string | undefined,\n\tsource = 'DEVSTACK_NETWORK',\n): DevstackNetworkName => parseDevstackNetwork(value, source).name;\n\n// ---------------------------------------------------------------------------\n// resolveNetwork — single precedence ladder for every entry point.\n// ---------------------------------------------------------------------------\n\n/**\n * Options for `resolveNetwork`. `explicit` is the caller-supplied\n * override (typically a CLI flag or programmatic option). `env` is the\n * environment value (typically `process.env.DEVSTACK_NETWORK`).\n * `defaultName` is the final fallback when both miss. The precedence is\n * explicit > env > default. The `source` string is embedded in the\n * parse error's `source` field so the diagnosis names the offending\n * input (e.g. `--network` vs `DEVSTACK_NETWORK`).\n */\nexport interface ResolveNetworkOptions {\n\treadonly explicit?: string | undefined;\n\treadonly env?: string | undefined;\n\treadonly defaultName?: string;\n\treadonly explicitSource?: string;\n\treadonly envSource?: string;\n}\n\n/** Default network when neither caller nor env provides one — the canonical\n * name (not a chain id or a `ParsedDevstackNetwork`), because every call site\n * folds it through `parseDevstackNetwork` first, exactly like an env value. */\nexport const DEFAULT_DEVSTACK_NETWORK = 'localnet' as const;\n\n/**\n * Result shape — the typed parse of the network the resolver picked.\n * Consumers branch on `parsed.mode` (local/live/fork) or derive the chain id\n * via `chainIdForNetwork(parsed.name)`; there is no second \"raw spelling\"\n * field, because the chain id is canonical and never depends on input form.\n */\nexport interface ResolvedDevstackNetwork {\n\treadonly parsed: ParsedDevstackNetwork;\n}\n\nconst pickInput = (\n\toptions: ResolveNetworkOptions,\n): { readonly value: string; readonly source: string } => {\n\tif (options.explicit !== undefined && options.explicit.length > 0) {\n\t\treturn { value: options.explicit, source: options.explicitSource ?? '--network' };\n\t}\n\tif (options.env !== undefined && options.env.length > 0) {\n\t\treturn { value: options.env, source: options.envSource ?? 'DEVSTACK_NETWORK' };\n\t}\n\treturn { value: options.defaultName ?? DEFAULT_DEVSTACK_NETWORK, source: 'default' };\n};\n\n/**\n * Resolve a network from the canonical explicit > env > default ladder\n * and parse the winning value through `parseDevstackNetwork`. Returns\n * the typed parse alongside the raw input the resolver picked. A\n * malformed value surfaces as `DevstackNetworkParseError` on the\n * failure channel so CLI / library boot fail fast with a structured\n * error rather than a downstream cryptic message.\n *\n * Used by both `api/run-stack.ts` (library embedding) and the CLI\n * identity resolver so the two paths share one precedence rule + one\n * parse error type.\n */\nexport const resolveNetwork = (\n\toptions: ResolveNetworkOptions = {},\n): Effect.Effect<ResolvedDevstackNetwork, DevstackNetworkParseError> =>\n\tEffect.try({\n\t\ttry: (): ResolvedDevstackNetwork => {\n\t\t\tconst { value, source } = pickInput(options);\n\t\t\treturn { parsed: parseDevstackNetwork(value, source) };\n\t\t},\n\t\tcatch: (cause) => cause as DevstackNetworkParseError,\n\t});\n\n/**\n * Sync sibling of `resolveNetwork` for entry points that aren't running\n * inside an Effect (the CLI identity resolver builds a plain TS record).\n * Throws `DevstackNetworkParseError` on bad input — caller's job to lift\n * into a typed failure channel if needed.\n */\nexport const resolveNetworkSync = (\n\toptions: ResolveNetworkOptions = {},\n): ResolvedDevstackNetwork => {\n\tconst { value, source } = pickInput(options);\n\treturn { parsed: parseDevstackNetwork(value, source) };\n};\n\n// ---------------------------------------------------------------------------\n// resolveStateDir — single precedence ladder for the on-disk runtime root.\n// ---------------------------------------------------------------------------\n\nexport interface ResolveStateDirOptions {\n\t/** Caller-supplied `runtimeRoot` override (the canonical\n\t * `RunStackOptions.runtimeRoot` / CLI `--state-dir` field). Wins\n\t * over every other input. */\n\treadonly runtimeRoot?: string;\n\t/** Caller-supplied `stateDir` override (the\n\t * `DevstackOptions.stateDir` field on `defineDevstack(...)`).\n\t * Lower precedence than `runtimeRoot`. */\n\treadonly stateDir?: string;\n\t/** Env-var value (typically `process.env.DEVSTACK_STATE_DIR`).\n\t * Lower precedence than either explicit override. */\n\treadonly env?: string;\n\t/** Working directory used for the `.devstack` fallback and for\n\t * resolving any relative override path. */\n\treadonly cwd: string;\n}\n\n/**\n * Resolve the substrate's on-disk runtime root using the canonical\n * precedence ladder: `runtimeRoot` > `stateDir` > `env` > `<cwd>/.devstack`.\n * Relative paths are resolved against `cwd`; absolute paths pass through.\n *\n * Shared by `runStack`, the CLI bin, and the build-integrations\n * discover module so all three entry points agree on the same ladder.\n */\nexport const resolveStateDir = (options: ResolveStateDirOptions): string => {\n\tconst pick =\n\t\t(options.runtimeRoot !== undefined && options.runtimeRoot.length > 0\n\t\t\t? options.runtimeRoot\n\t\t\t: undefined) ??\n\t\t(options.stateDir !== undefined && options.stateDir.length > 0\n\t\t\t? options.stateDir\n\t\t\t: undefined) ??\n\t\t(options.env !== undefined && options.env.length > 0 ? options.env : undefined);\n\tif (pick === undefined) return resolve(options.cwd, '.devstack');\n\treturn resolve(options.cwd, pick);\n};\n"],"mappings":"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"}
|
|
@@ -2,8 +2,8 @@ import { readStackEngine } from "./define-devstack.mjs";
|
|
|
2
2
|
import { appName, stackName } from "../substrate/brand.mjs";
|
|
3
3
|
import { makeProjectionRefSync } from "../substrate/runtime/projection/state-ref.mjs";
|
|
4
4
|
import "../substrate/runtime/index.mjs";
|
|
5
|
-
import { buildSubstrateLayers, layerProductionOrchestrators, resolveProductionCodegenOptions, superviseStackWithProductionBoot } from "../orchestrators/boot.mjs";
|
|
6
5
|
import { resolveAppName, resolveNetworkSync, resolveStackName, resolveStateDir } from "./inference-network.mjs";
|
|
6
|
+
import { buildSubstrateLayers, layerProductionOrchestrators, resolveProductionCodegenOptions, superviseStackWithProductionBoot } from "../orchestrators/boot.mjs";
|
|
7
7
|
import { Cause, Deferred, Effect, Exit, Fiber, Layer, Logger, Queue, Ref, Stream } from "effect";
|
|
8
8
|
//#region src/api/run-stack-internal.ts
|
|
9
9
|
const resolveIdentity = (stack, opts, cwd) => {
|
|
@@ -115,7 +115,6 @@ const runStackWithBoot = (stack, opts = {}) => {
|
|
|
115
115
|
const { state, bootDeferred, stopRequested, eventQueueRef, commandQueue, runCommandRef, fiberRef, startClaim, midRunCauseRef } = makeRunHandleSlots();
|
|
116
116
|
const substrate = layerProductionOrchestrators({ codegen: resolveProductionCodegenOptions({
|
|
117
117
|
appRoot,
|
|
118
|
-
effectiveStack: String(identity.stack),
|
|
119
118
|
codegen
|
|
120
119
|
}) }).pipe(Layer.provideMerge(buildSubstrateLayers(identity, runtimeRoot)));
|
|
121
120
|
const runCommand = (command) => Effect.flatMap(Deferred.await(runCommandRef), (dispatch) => dispatch(command));
|
|
@@ -128,7 +127,6 @@ const runStackWithBoot = (stack, opts = {}) => {
|
|
|
128
127
|
const commandHandler = opts.commandHandler === void 0 ? void 0 : yield* opts.commandHandler;
|
|
129
128
|
yield* superviseStackWithProductionBoot(supervisedStack, identity, state, {
|
|
130
129
|
extras: stack.options.extras,
|
|
131
|
-
networkOptions: stack.options.networkOptions,
|
|
132
130
|
...opts.emitBindings === void 0 ? {} : { emitBindings: opts.emitBindings },
|
|
133
131
|
...opts.boot?.devstackVersion === void 0 ? {} : { devstackVersion: opts.boot.devstackVersion },
|
|
134
132
|
...commandHandler === void 0 ? {} : { commandHandler },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-stack-internal.mjs","names":[],"sources":["../../src/api/run-stack-internal.ts"],"sourcesContent":["// `runStackWithBoot(stack, optsWithBoot)` — the NON-PUBLIC seam under\n// `runStack`.\n//\n// `api/run-stack.ts` is the thin PUBLIC facade: `runStack` delegates here\n// with a zero-`boot` bag, so the public surface (`RunHandle`,\n// `RunStackOptions`, `BootError`) and the zero-`boot` path stay\n// byte-identical. This module owns the supervised-body construction and\n// the caller-injectable boot hook seam the CLI `up` verb routes through.\n//\n// Why a seam at all: the CLI `up` verb needs the SAME substrate Layer\n// composition + supervised body `runStack` builds, but wrapped with extra\n// `beforeInitialAcquire`/`withinScope` work (interrupted-restore recovery,\n// the roster lock, the command-channel IPC bridge, and the TUI surface).\n// `runStackWithBoot` threads those as CALLER-INJECTED hooks so\n// the CLI consumes ONE boot core instead of forking it. Because the bag is\n// non-public, its hooks receive an `InternalRunHandle` that additionally\n// carries the live `SupervisorHandle` (the seam the CLI hooks drain) —\n// substrate detail that NEVER reaches the public `RunHandle`.\n//\n// PR#21 boot-ordering invariant (do not reorder): the composed hooks run\n// the BUILT-IN work FIRST, then the caller's. For `beforeInitialAcquire`\n// this means the event-queue handoff + stop-bridge + command-pump are armed\n// before any caller hook runs (so a `stop()` during a caller hook always\n// has a bridge) AND before the first acquire (`superviseStackEffect` runs\n// the whole composed `beforeInitialAcquire` ahead of `runInitialAcquire`).\n// For `withinScope` the built-in readiness-gate resolution (the status\n// scan that resolves `bootDeferred`) runs first, so a caller `withinScope`\n// can never delay `handle.start`.\n\nimport {\n\tCause,\n\tDeferred,\n\tEffect,\n\tExit,\n\tFiber,\n\ttype FileSystem,\n\tLayer,\n\tLogger,\n\tQueue,\n\tRef,\n\ttype Scope,\n\tStream,\n\ttype SubscriptionRef,\n} from 'effect';\n\nimport { appName, stackName } from '../substrate/brand.ts';\nimport type { Identity } from '../substrate/identity.ts';\nimport type { EngineCommand, EngineEvent } from '../substrate/events.ts';\nimport { makeProjectionRefSync } from '../substrate/runtime/index.ts';\nimport type { StackPathsService } from '../substrate/runtime/paths.ts';\nimport type { SnapshotOrchestratorService } from '../orchestrators/snapshot/index.ts';\nimport type {\n\tSupervisorCommandHandler,\n\tSupervisorHandle,\n} from '../substrate/runtime/supervisor/index.ts';\nimport {\n\tbuildSubstrateLayers,\n\tlayerProductionOrchestrators,\n\tresolveProductionCodegenOptions,\n\tsuperviseStackWithProductionBoot,\n} from '../orchestrators/boot.ts';\nimport type { Codegenable } from '../orchestrators/codegen/service.ts';\nimport { readStackEngine, type Stack } from './define-devstack.ts';\nimport type { AnyPlugin } from '../substrate/plugin.ts';\nimport {\n\tresolveAppName,\n\tresolveNetworkSync,\n\tresolveStackName,\n\tresolveStateDir,\n} from './inference-network.ts';\nimport type {\n\tBootError,\n\tRunHandle,\n\tRunStackIdentityOptions,\n\tRunStackOptions,\n} from './run-stack.ts';\n\n// -----------------------------------------------------------------------------\n// Internal handle + boot bag\n// -----------------------------------------------------------------------------\n\n/** The handle the caller-injected `boot` hooks receive. It is the public\n * `RunHandle` PLUS the live `SupervisorHandle` — the substrate seam the\n * CLI `up` hooks drain (roster lock keys off it, the IPC bridge tails\n * `supervisor.events` / pumps `supervisor.commands`, the TUI mounts\n * `supervisor.state`). NON-PUBLIC: `index.ts` never re-exports it, so the\n * `SupervisorHandle` substrate type stays off the public surface. */\nexport interface InternalRunHandle extends RunHandle {\n\t/** The live supervisor handle for this boot. Available inside both\n\t * composed hooks; carries `events`/`commands`/`runCommand`/`state`/\n\t * `graph`/`registry` the CLI hooks consume directly. */\n\treadonly supervisor: SupervisorHandle;\n}\n\n/** The substrate services the seam already has in scope when it runs the\n * caller hooks (the composed `beforeInitialAcquire`/`withinScope` run\n * inside `superviseStackEffect`, which is `Effect.provide(substrate)`).\n * The bag hooks may yield these directly — the CLI `up` hooks drive the\n * ONE `SnapshotOrchestratorService` instance the supervisor's contribution\n * dispatcher registers participants on (so an operator `snapshot save`\n * captures the LIVE participant set, not an empty one off a sibling\n * orchestrator), plus `FileSystem`/`StackPathsService` for the\n * interrupted-restore + roster paths. `Scope.Scope` lets a hook fork\n * scoped fibers (the IPC pump, the TUI mount) onto the supervised scope.\n * NON-PUBLIC: like `InternalRunHandle`, this never reaches the public\n * `runStack` facade. */\nexport type BootHookServices =\n\t| Scope.Scope\n\t| SnapshotOrchestratorService\n\t| FileSystem.FileSystem\n\t| StackPathsService;\n\n/** Caller-injected boot hooks. Both run AFTER their built-in counterpart\n * (see the PR#21 ordering note at the top of this file). Their failures\n * fold into `BootError.cause` via the same `catchCause` tee the built-in\n * work uses, so `handle.start` stays `Effect<void, BootError, never>` —\n * the bag does NOT widen the public error channel. The R-channel is\n * `BootHookServices` (substrate services the seam already provides), NOT\n * `never`: the hooks run inside the supervised scope and may drive the\n * seam's live substrate (notably the supervisor's snapshot orchestrator). */\nexport interface RunStackBootBag {\n\treadonly devstackVersion?: string;\n\treadonly beforeInitialAcquire?: (\n\t\thandle: InternalRunHandle,\n\t) => Effect.Effect<void, unknown, BootHookServices>;\n\treadonly withinScope?: (\n\t\thandle: InternalRunHandle,\n\t) => Effect.Effect<void, unknown, BootHookServices>;\n}\n\n/** The substrate services a {@link CommandHandlerFactory} may yield — the\n * same singletons the contribution dispatcher + boot hooks see, minus\n * `Scope.Scope` (the factory runs before the supervised scope opens, so it\n * forks nothing). A handler closes over `SnapshotOrchestratorService` to\n * capture the LIVE participant set and `FileSystem` to thread it into the\n * snapshot I/O. */\ntype CommandHandlerServices =\n\t| SnapshotOrchestratorService\n\t| FileSystem.FileSystem\n\t| StackPathsService;\n\n/** A FACTORY for the supervisor command handler. The seam runs it ONCE,\n * inside the supervised body (so `CommandHandlerServices` — notably the live\n * `SnapshotOrchestratorService` + `FileSystem` — are in scope), BEFORE it\n * hands `startSupervisor` the resolved handler. So the resulting handler\n * (whose own R-channel is `never`, as the supervisor command loop requires)\n * closes over the seam's REAL substrate instances directly — no Deferred\n * hand-off. Resolving the snapshot orchestrator here yields the SAME\n * instance the contribution dispatcher registers live participants on, so an\n * operator `snapshot save` captures the LIVE chain/blob/db state, not an\n * empty set off a sibling orchestrator. */\nexport type CommandHandlerFactory = Effect.Effect<\n\tSupervisorCommandHandler,\n\tnever,\n\tCommandHandlerServices\n>;\n\n/** `RunStackOptions` plus the non-public injection points. `commandHandler`\n * is a FACTORY the seam resolves against its live substrate (see\n * {@link CommandHandlerFactory}); `boot` carries the caller-injected hooks. */\nexport interface RunStackOptionsWithBoot extends RunStackOptions {\n\treadonly commandHandler?: CommandHandlerFactory;\n\treadonly boot?: RunStackBootBag;\n\t/** Dev-`up` only: the STATIC (id-free) codegen contributions used to\n\t * regenerate the committed `src/generated` tree on every (re)acquire so a\n\t * Move-source edit refreshes the bindings WITHOUT baking live on-chain ids\n\t * into `config.ts`. The `up` verb sets it (`deriveContributions(...)`); the\n\t * public `runStack` facade never does (committed bindings are an input\n\t * there, not an output). */\n\treadonly emitBindings?: ReadonlyArray<Codegenable>;\n}\n\n// -----------------------------------------------------------------------------\n// Identity resolution\n// -----------------------------------------------------------------------------\n\nconst resolveIdentity = (\n\tstack: Stack<ReadonlyArray<AnyPlugin>>,\n\topts: RunStackIdentityOptions | undefined,\n\tcwd: string,\n): Identity => {\n\tconst app = resolveAppName({\n\t\texplicit: opts?.app,\n\t\tcwd,\n\t});\n\tconst stackNameStr = resolveStackName({\n\t\texplicit: opts?.stack ?? stack.options.stackName,\n\t\tcwd,\n\t});\n\t// Parse + validate up-front so a malformed value fails here rather\n\t// than downstream when a plugin probes the network. The identity's\n\t// network is the canonical name (`localnet`, `testnet`, …) — never the\n\t// `sui:`-prefixed wallet-standard form, which devstack does not use\n\t// internally — so plugin checks and on-disk cache namespaces key on one\n\t// stable name regardless of how it was spelled on the CLI.\n\tconst resolved = resolveNetworkSync({\n\t\texplicit: opts?.network,\n\t\tenv: process.env.DEVSTACK_NETWORK,\n\t\texplicitSource: 'runStack({ identity.network })',\n\t});\n\treturn {\n\t\tapp: appName(app),\n\t\tstack: stackName(stackNameStr),\n\t\tnetwork: resolved.parsed.name,\n\t};\n};\n\nconst toBootError = (cause: Cause.Cause<unknown>): BootError => ({\n\t_tag: 'BootError',\n\tcause,\n});\n\n// -----------------------------------------------------------------------------\n// Boot-time handle slots\n// -----------------------------------------------------------------------------\n\n/** The mutable cells + ref the handle's lifecycle effects close over. Every\n * field is allocated SYNCHRONOUSLY in `makeRunHandleSlots` (see its\n * sync-safety contract) so the public handle can expose a live `state` /\n * `commands` queue and let callers subscribe to `state.changes` BEFORE\n * `start` ever forks the supervisor fiber. */\ninterface RunHandleSlots {\n\t/** The supervisor's live projection. Seeded sync; populated by the\n\t * supervised body once it boots. */\n\treadonly state: SubscriptionRef.SubscriptionRef<\n\t\timport('../substrate/projection.ts').SubscribableState\n\t>;\n\t/** Resolved (or failed with `BootError`) by the readiness gate; `start`\n\t * awaits it. */\n\treadonly bootDeferred: Deferred.Deferred<void, BootError>;\n\t/** Set by `stop`; the built-in stop-bridge awaits it and offers\n\t * `shutdown.requested` onto the supervisor's command channel. */\n\treadonly stopRequested: Deferred.Deferred<void>;\n\t/** Handed the supervisor's event dequeue by `beforeInitialAcquire`; the\n\t * public `events` stream unwraps it. */\n\treadonly eventQueueRef: Deferred.Deferred<Queue.Dequeue<EngineEvent>>;\n\t/** Public command channel, pumped onto the supervisor's own queue once it\n\t * boots — the TUI/IPC publish target. */\n\treadonly commandQueue: Queue.Queue<EngineCommand>;\n\t/** Handed the supervisor's submit-and-await dispatch by\n\t * `beforeInitialAcquire`; the public `runCommand` awaits it. */\n\treadonly runCommandRef: Deferred.Deferred<\n\t\t(command: EngineCommand) => Effect.Effect<void, unknown, never>\n\t>;\n\t/** Holds the forked supervisor fiber; `stop`/`awaitShutdown` await it. */\n\treadonly fiberRef: Deferred.Deferred<Fiber.Fiber<void, never>>;\n\t/** Single-fire guard so a second `start` is a no-op. */\n\treadonly startClaim: Ref.Ref<boolean>;\n\t/** Tee for a mid-run defect/failure raised AFTER boot completed, so\n\t * `awaitShutdown` can re-surface it (a post-boot `Deferred.fail` is a\n\t * no-op). */\n\treadonly midRunCauseRef: Ref.Ref<Cause.Cause<unknown> | null>;\n}\n\n/**\n * Allocate every boot-time handle slot in ONE place.\n *\n * SYNC-SAFETY CONTRACT (the reason this is a sync constructor, not an\n * `Effect`): the handle must expose a real `state` / `commands` queue and\n * be subscribable BEFORE `start` forks the supervisor. `Deferred.make`,\n * `Queue.unbounded`, and `Ref.make` are pure sync effects (no side effects,\n * no async), so `Effect.runSync` is safe for all of them. The projection ref\n * goes through the explicit `makeProjectionRefSync` so the sync contract is\n * pinned at the substrate constructor — if `makeProjectionRef` ever picks up\n * an async/Layer wrapper (`withSpan`, annotation), `makeProjectionRefSync`\n * must stay sync-only or this whole allocation must move behind a\n * Deferred-handoff seam.\n */\nconst makeRunHandleSlots = (): RunHandleSlots => ({\n\tstate: makeProjectionRefSync(),\n\tbootDeferred: Effect.runSync(Deferred.make<void, BootError>()),\n\tstopRequested: Effect.runSync(Deferred.make<void>()),\n\teventQueueRef: Effect.runSync(Deferred.make<Queue.Dequeue<EngineEvent>>()),\n\tcommandQueue: Effect.runSync(Queue.unbounded<EngineCommand>()),\n\trunCommandRef: Effect.runSync(\n\t\tDeferred.make<(command: EngineCommand) => Effect.Effect<void, unknown, never>>(),\n\t),\n\tfiberRef: Effect.runSync(Deferred.make<Fiber.Fiber<void, never>>()),\n\tstartClaim: Effect.runSync(Ref.make(false)),\n\tmidRunCauseRef: Effect.runSync(Ref.make<Cause.Cause<unknown> | null>(null)),\n});\n\n/** Compose the public handle + the live `SupervisorHandle` into the\n * `InternalRunHandle` the caller boot hooks receive. The two composed hooks\n * build it identically; factored here so the closure-soundness note (the\n * hooks only deref `publicHandle` at boot, after the body assigns it) lives\n * in one place. */\nconst buildInternalHandle = (\n\tpublicHandle: RunHandle,\n\tsupervisor: SupervisorHandle,\n): InternalRunHandle => ({ ...publicHandle, supervisor });\n\n// -----------------------------------------------------------------------------\n// runStackWithBoot\n// -----------------------------------------------------------------------------\n\n/**\n * The implementation behind `runStack`. Boots a `Stack` for programmatic\n * embedding and returns a `RunHandle` synchronously; the supervisor fiber\n * is forked on `start`.\n *\n * Lifecycle (identical to `runStack`'s documented contract):\n *\n * 1. `runStackWithBoot(stack, opts)` — synchronous; no fiber forked yet.\n * 2. `await Effect.runPromise(handle.start)` — forks the supervisor,\n * blocks until every plugin reaches `ready` (or fails with\n * `BootError`).\n * 3. Use `handle.events` / `handle.state` to observe.\n * 4. `await Effect.runPromise(handle.stop)` — graceful shutdown.\n * 5. `handle.awaitShutdown` resolves once finalizers complete.\n *\n * Internal architecture: the handle stores a `Deferred` for boot\n * completion. The supervised body resolves it from the SUPERVISOR-OWNED\n * readiness signal — `superviseStackEffect`'s `withinScope` hook fires\n * only after `runInitialAcquire` wins its `raceFirst` against\n * `awaitShutdown` (i.e. once `allReadyOrTerminal` — `ready || done` — is\n * true). This is the same `done`-tolerant gate the long-running CLI path\n * uses; it does NOT watch per-node ready-gates (a non-failed terminal\n * `done` node need never resolve its `awaitReady` gate). Initial-acquire\n * failures surface through the supervised body's `catchCause` tee, which\n * fails the deferred with a `BootError`. `start` awaits it.\n *\n * `opts.commandHandler` threads straight into `superviseStackEffect`'s\n * `commandHandler` slot. `opts.boot` injects caller hooks that run AFTER\n * their built-in counterpart (the PR#21 ordering); their failures fold\n * into `BootError` via the same `catchCause` tee, so `start` keeps its\n * `Effect<void, BootError, never>` type.\n */\nexport const runStackWithBoot = (\n\tstack: Stack<ReadonlyArray<AnyPlugin>>,\n\topts: RunStackOptionsWithBoot = {},\n): RunHandle => {\n\tconst engineStack = readStackEngine(stack);\n\tconst runtimeRoot = resolveStateDir({\n\t\truntimeRoot: opts.runtimeRoot,\n\t\tstateDir: opts.stateDir ?? engineStack.options.stateDir,\n\t\tenv: process.env.DEVSTACK_STATE_DIR,\n\t\tcwd: process.cwd(),\n\t});\n\tconst appRoot = opts.appRoot ?? process.cwd();\n\tconst identity = resolveIdentity(stack, opts.identity, appRoot);\n\tconst codegen = opts.codegen ?? engineStack.options.codegen;\n\n\tconst supervisedStack = {\n\t\t_tag: 'Stack' as const,\n\t\tmembers: engineStack.members,\n\t\toptions: engineStack.options,\n\t};\n\n\t// Every boot-time handle slot is allocated in ONE place (the sync-safety\n\t// contract lives on `makeRunHandleSlots`): the handle must expose a live\n\t// `state` / `commands` queue and be subscribable BEFORE `start` forks the\n\t// supervisor fiber.\n\tconst {\n\t\tstate,\n\t\tbootDeferred,\n\t\tstopRequested,\n\t\teventQueueRef,\n\t\tcommandQueue,\n\t\trunCommandRef,\n\t\tfiberRef,\n\t\tstartClaim,\n\t\tmidRunCauseRef,\n\t} = makeRunHandleSlots();\n\n\t// Resolve the per-stack LIVE codegen output location through the ONE\n\t// shared boot seam: EVERY live run emits into\n\t// `.devstack/stacks/<stack>/generated/` (gitignored), so the id-bearing\n\t// tree never lands in committed source. The committed `src/generated`\n\t// tree is owned solely by the stack-free `codegen` verb. An explicit\n\t// `opts.codegen` (or the stack's own `codegen`) is honored verbatim by\n\t// the resolver. The effective stack (the resolved `identity.stack`) is\n\t// in scope here, exactly as in the CLI's `buildVerbLayers` seam — both\n\t// route through `resolveProductionCodegenOptions`.\n\tconst substrate = layerProductionOrchestrators({\n\t\tcodegen: resolveProductionCodegenOptions({\n\t\t\tappRoot,\n\t\t\teffectiveStack: String(identity.stack),\n\t\t\tcodegen,\n\t\t}),\n\t}).pipe(Layer.provideMerge(buildSubstrateLayers(identity, runtimeRoot)));\n\n\t// The submit-and-await dispatch surfaced on the public handle. Awaits\n\t// the supervisor's `runCommand` (wired in the built-in\n\t// `beforeInitialAcquire`), then delegates.\n\tconst runCommand = (command: EngineCommand): Effect.Effect<void, unknown, never> =>\n\t\tEffect.flatMap(Deferred.await(runCommandRef), (dispatch) => dispatch(command));\n\n\tconst events: Stream.Stream<EngineEvent, never, never> = Stream.unwrap(\n\t\tEffect.gen(function* () {\n\t\t\tconst queue = yield* Deferred.await(eventQueueRef);\n\t\t\treturn Stream.fromQueue(queue);\n\t\t}),\n\t);\n\n\t// The PUBLIC handle is assigned exactly once at the end of this body\n\t// (after the lifecycle effects exist) and returned. The composed boot\n\t// hooks below close over `publicHandle` to build an `InternalRunHandle`\n\t// (the public handle + the live `SupervisorHandle`), but they only\n\t// deref it at BOOT time — long after this synchronous body finishes and\n\t// assigns it — so the closure is sound. Declared with a forward `let`\n\t// rather than mutated readonly fields so `RunHandle` stays immutable.\n\tlet publicHandle: RunHandle;\n\n\tconst supervised = Effect.gen(function* () {\n\t\t// Resolve the command-handler FACTORY against the seam's live\n\t\t// substrate (in scope here via `Effect.provide(substrate)` below)\n\t\t// BEFORE `startSupervisor` consumes the handler. The factory closes\n\t\t// over the same `SnapshotOrchestratorService` the contribution\n\t\t// dispatcher registers participants on, so the resulting handler\n\t\t// (R = `never`, as the command loop requires) drives the LIVE state.\n\t\tconst commandHandler =\n\t\t\topts.commandHandler === undefined ? undefined : yield* opts.commandHandler;\n\t\t// The shared production-boot assembly (contribution dispatcher +\n\t\t// post-acquire hook + built-in plugin-context extension, then\n\t\t// `layerBuiltInPluginRuntime`) lives in ONE place\n\t\t// (`orchestrators/boot.ts superviseStackWithProductionBoot`). This\n\t\t// seam supplies only the LONG-RUNNING-specific wrapping: the\n\t\t// resolved command handler, the public `runStack({ extendContext })`\n\t\t// chained after the built-in, and the composed boot hooks below.\n\t\tyield* superviseStackWithProductionBoot(supervisedStack, identity, state, {\n\t\t\textras: stack.options.extras,\n\t\t\tnetworkOptions: stack.options.networkOptions,\n\t\t\t...(opts.emitBindings === undefined ? {} : { emitBindings: opts.emitBindings }),\n\t\t\t...(opts.boot?.devstackVersion === undefined\n\t\t\t\t? {}\n\t\t\t\t: { devstackVersion: opts.boot.devstackVersion }),\n\t\t\t...(commandHandler === undefined ? {} : { commandHandler }),\n\t\t\t...(opts.extendContext === undefined\n\t\t\t\t? {}\n\t\t\t\t: { extendContextAfterBuiltIn: opts.extendContext }),\n\t\t\t// ── COMPOSED `beforeInitialAcquire` (ONE ordered gen) ──────────\n\t\t\t// ORDER (PR#21-load-bearing): BUILT-IN work first, THEN caller.\n\t\t\t// 1. built-in: event-queue handoff + runCommand surface +\n\t\t\t// command-pump fork + stop-bridge fork.\n\t\t\t// 2. caller: `opts.boot?.beforeInitialAcquire(...)`.\n\t\t\t// `superviseStackEffect` runs this whole composed effect BEFORE\n\t\t\t// `runInitialAcquire`, so the caller hook (the CLI's recover/\n\t\t\t// roster/IPC/TUI bundle) runs before first acquire too; and because\n\t\t\t// the built-in stop-bridge is armed first, a `stop()` raised during\n\t\t\t// a caller hook always has a command bridge.\n\t\t\tbeforeInitialAcquire: (handle) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Deferred.succeed(eventQueueRef, handle.events).pipe(\n\t\t\t\t\t\tEffect.catch(() => Effect.void),\n\t\t\t\t\t);\n\t\t\t\t\t// Surface the supervisor's submit-and-await dispatch onto the\n\t\t\t\t\t// public handle BEFORE acquire so a boot failure can't leave\n\t\t\t\t\t// the handle without a command bridge.\n\t\t\t\t\tyield* Deferred.succeed(runCommandRef, handle.runCommand).pipe(\n\t\t\t\t\t\tEffect.catch(() => Effect.void),\n\t\t\t\t\t);\n\t\t\t\t\t// Pump the eager public command queue onto the supervisor's\n\t\t\t\t\t// own command queue (`handle.commands` — the seam the signal\n\t\t\t\t\t// handler + command loop drain). A publish on the public\n\t\t\t\t\t// queue therefore always reaches the supervisor's drain.\n\t\t\t\t\tyield* Effect.forkScoped(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\twhile (true) {\n\t\t\t\t\t\t\t\tconst command = yield* Queue.take(commandQueue);\n\t\t\t\t\t\t\t\tyield* Queue.offer(handle.commands, command);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\n\t\t\t\t\t// Bridge `stop` requests onto the supervisor's command\n\t\t\t\t\t// channel before acquire starts so a boot failure cannot\n\t\t\t\t\t// leave the public handle without an event/command bridge.\n\t\t\t\t\tyield* Effect.forkScoped(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tyield* Deferred.await(stopRequested);\n\t\t\t\t\t\t\tyield* Queue.offer(handle.commands, {\n\t\t\t\t\t\t\t\ttag: 'shutdown.requested',\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\n\t\t\t\t\t// Caller-injected work runs AFTER the built-in bridges are\n\t\t\t\t\t// armed. Its failures fold into the supervised body's\n\t\t\t\t\t// `catchCause` below (and thence into `BootError`).\n\t\t\t\t\tif (opts.boot?.beforeInitialAcquire !== undefined) {\n\t\t\t\t\t\tyield* opts.boot.beforeInitialAcquire(buildInternalHandle(publicHandle, handle));\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t// ── COMPOSED `withinScope` (ONE ordered gen) ───────────────────\n\t\t\t// ORDER: BUILT-IN readiness-gate resolution first, THEN caller.\n\t\t\t// 1. built-in: the status scan that resolves `bootDeferred`.\n\t\t\t// 2. caller: `opts.boot?.withinScope(...)`.\n\t\t\t// Resolving the gate first means a caller `withinScope` can never\n\t\t\t// delay `handle.start`.\n\t\t\twithinScope: (handle) =>\n\t\t\t\t// Resolve the boot gate from the SUPERVISOR-OWNED readiness\n\t\t\t\t// signal. `superviseStackEffect` only calls `withinScope`\n\t\t\t\t// after `runInitialAcquire` wins its `raceFirst` against\n\t\t\t\t// `awaitShutdown` and yields `'booted'` (boot.ts) — i.e. once\n\t\t\t\t// `allReadyOrTerminal` is true (`ready || done`). This is the\n\t\t\t\t// SAME `done`-tolerant gate the long-running CLI path uses,\n\t\t\t\t// not a per-node `awaitReady` watcher (whose ready-gate a\n\t\t\t\t// non-failed terminal `done` node need never resolve, so it\n\t\t\t\t// would hang here).\n\t\t\t\t//\n\t\t\t\t// `runInitialAcquire` does NOT itself fail on a per-PLUGIN\n\t\t\t\t// acquire failure: `acquireFullGraph` swallows node failures\n\t\t\t\t// into the registry's `failed` status (the CLI surfaces them as\n\t\t\t\t// red TUI rows and keeps the stack up). runStack's contract is\n\t\t\t\t// stricter — `start` FAILS with `BootError` if any node failed\n\t\t\t\t// initial acquire. So gate on the STATUS contract here: succeed\n\t\t\t\t// iff every node is `ready`/`done`, else fail with the first\n\t\t\t\t// `failed` node's `PluginAcquireFailed` cause. A failed node's\n\t\t\t\t// ready-gate IS resolved (markFailed failed it), so reading it\n\t\t\t\t// is hang-free — the `done`-tolerant equivalent of the old\n\t\t\t\t// watcher's failure detection. (Post-acquire HOOK failures —\n\t\t\t\t// codegen / manifest — fail `runInitialAcquire` directly and\n\t\t\t\t// tee through the supervised body's `catchCause` below; we\n\t\t\t\t// never reach here on that path.)\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tlet firstFailure: BootError | null = null;\n\t\t\t\t\tfor (const [key] of handle.graph.nodes) {\n\t\t\t\t\t\tconst status = yield* handle.registry\n\t\t\t\t\t\t\t.getStatus(key)\n\t\t\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed('failed' as const)));\n\t\t\t\t\t\tif (status === 'failed') {\n\t\t\t\t\t\t\tconst failureExit = yield* Effect.exit(handle.registry.awaitReady(key));\n\t\t\t\t\t\t\tfirstFailure = toBootError(\n\t\t\t\t\t\t\t\tExit.isFailure(failureExit) ? failureExit.cause : Cause.empty,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (firstFailure === null) {\n\t\t\t\t\t\tyield* Deferred.succeed(bootDeferred, undefined).pipe(Effect.asVoid);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tyield* Deferred.fail(bootDeferred, firstFailure).pipe(Effect.asVoid);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Caller-injected work runs AFTER the boot gate is resolved,\n\t\t\t\t\t// so it can never delay `handle.start`. Its failures fold\n\t\t\t\t\t// into the supervised body's `catchCause` below; since the\n\t\t\t\t\t// boot gate already succeeded by this point, such a failure\n\t\t\t\t\t// surfaces on `awaitShutdown` (mid-run), not `start` — the\n\t\t\t\t\t// same place a built-in `withinScope` failure would land.\n\t\t\t\t\tif (opts.boot?.withinScope !== undefined) {\n\t\t\t\t\t\tyield* opts.boot.withinScope(buildInternalHandle(publicHandle, handle));\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t});\n\t});\n\n\tconst loggerLayer = Logger.layer([]);\n\tconst layered = supervised.pipe(Effect.provide(substrate), Effect.provide(loggerLayer));\n\n\t// Convert any uncaught cause into a boot failure if the deferred\n\t// hasn't completed. If `bootDeferred` HAS already succeeded then\n\t// `Deferred.fail` is a no-op — the cause is a mid-run failure\n\t// (e.g. a plugin scope finalizer defect) and would otherwise be\n\t// silently dropped: the fiber exits `Success(void)` and\n\t// `awaitShutdown` resolves clean. Tee the cause into\n\t// `midRunCauseRef` so `awaitShutdown` can re-surface it, AND emit\n\t// `Effect.logError(Cause.pretty(cause))` so observability stays\n\t// loud regardless of whether anyone awaits. Boot failures (still in\n\t// the bootDeferred-pending window) only surface via `start`, not\n\t// here — we'd otherwise re-raise them on `awaitShutdown` after\n\t// `start` already rejected. NOTE: a CALLER `boot` hook failure folds\n\t// through this SAME tee, so its failure surfaces as `BootError` on\n\t// `start` (when raised before the gate resolves) or re-raises on\n\t// `awaitShutdown` (after) — never widening `start`'s error channel.\n\tconst supervisedProgram: Effect.Effect<void, never, never> = layered.pipe(\n\t\tEffect.catchCause((cause) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst bootAlreadyCompleted = yield* Deferred.isDone(bootDeferred);\n\t\t\t\tyield* Deferred.fail(bootDeferred, toBootError(cause)).pipe(\n\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\tEffect.catch(() => Effect.void),\n\t\t\t\t);\n\t\t\t\tif (bootAlreadyCompleted) {\n\t\t\t\t\tyield* Ref.set(midRunCauseRef, cause);\n\t\t\t\t}\n\t\t\t\tyield* Effect.logError(`devstack runStack: supervisor died\\n${Cause.pretty(cause)}`);\n\t\t\t}),\n\t\t),\n\t);\n\n\tconst start: Effect.Effect<void, BootError, never> = Effect.uninterruptibleMask((restore) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst shouldStart = yield* Ref.modify(startClaim, (started) =>\n\t\t\t\tstarted ? [false, true] : [true, true],\n\t\t\t);\n\t\t\tif (shouldStart) {\n\t\t\t\t// `forkDetach` decouples the supervisor fiber from the `start`\n\t\t\t\t// fiber's scope. `forkChild`\n\t\t\t\t// would tie the supervisor to whatever fiber runs `start`, and\n\t\t\t\t// once `start` resolves (after `bootDeferred` succeeds) the\n\t\t\t\t// runtime would interrupt the supervisor — transitioning every\n\t\t\t\t// plugin `ready → stopping → stopped` before the caller can\n\t\t\t\t// read post-ready state or call `handle.stop`. The handle's\n\t\t\t\t// explicit `stop` + `awaitShutdown` paths are the only\n\t\t\t\t// shutdown signals; the captured `Fiber` reference is how the\n\t\t\t\t// daemon stays releasable.\n\t\t\t\tconst fiber = yield* Effect.forkDetach(supervisedProgram);\n\t\t\t\tyield* Deferred.succeed(fiberRef, fiber);\n\t\t\t}\n\t\t\tyield* restore(Deferred.await(bootDeferred));\n\t\t}),\n\t);\n\n\tconst stop: Effect.Effect<void, never, never> = Effect.gen(function* () {\n\t\tyield* Deferred.succeed(stopRequested, undefined).pipe(Effect.catch(() => Effect.void));\n\t\tconst alreadyStarted = yield* Deferred.isDone(fiberRef);\n\t\tif (!alreadyStarted) {\n\t\t\treturn;\n\t\t}\n\t\tconst fiber = yield* Deferred.await(fiberRef);\n\t\t// `Fiber.await` returns an `Exit` without raising — handles both\n\t\t// success and interrupt-cause cases (the supervisor's\n\t\t// graceful-shutdown path closes the scope, which surfaces as an\n\t\t// interrupt cause if the fiber was mid-await on the latch poll).\n\t\tyield* Fiber.await(fiber);\n\t});\n\n\tconst awaitShutdown: Effect.Effect<void, unknown, never> = Effect.gen(function* () {\n\t\tconst alreadyStarted = yield* Deferred.isDone(fiberRef);\n\t\tif (!alreadyStarted) {\n\t\t\treturn;\n\t\t}\n\t\tconst fiber = yield* Deferred.await(fiberRef);\n\t\tyield* Fiber.await(fiber);\n\t\t// Re-surface any mid-run defect/failure captured by the\n\t\t// supervised body's `catchCause` (boot failures are already\n\t\t// surfaced via `start`). Without this re-raise a plugin scope\n\t\t// finalizer defect would silently drop and operators get no\n\t\t// signal — see the comment on `midRunCauseRef` above.\n\t\tconst midRunCause = yield* Ref.get(midRunCauseRef);\n\t\tif (midRunCause !== null) {\n\t\t\treturn yield* Effect.failCause(midRunCause);\n\t\t}\n\t});\n\n\t// Assign the handle the composed boot hooks close over (they only deref\n\t// it at boot, after this assignment) and return it.\n\tpublicHandle = {\n\t\tstart,\n\t\tstop,\n\t\tawaitShutdown,\n\t\tevents,\n\t\tstate,\n\t\tcommands: commandQueue,\n\t\trunCommand,\n\t\tidentity,\n\t};\n\n\treturn publicHandle;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"run-stack-internal.mjs","names":[],"sources":["../../src/api/run-stack-internal.ts"],"sourcesContent":["// `runStackWithBoot(stack, optsWithBoot)` — the NON-PUBLIC seam under\n// `runStack`.\n//\n// `api/run-stack.ts` is the thin PUBLIC facade: `runStack` delegates here\n// with a zero-`boot` bag, so the public surface (`RunHandle`,\n// `RunStackOptions`, `BootError`) and the zero-`boot` path stay\n// byte-identical. This module owns the supervised-body construction and\n// the caller-injectable boot hook seam the CLI `up` verb routes through.\n//\n// Why a seam at all: the CLI `up` verb needs the SAME substrate Layer\n// composition + supervised body `runStack` builds, but wrapped with extra\n// `beforeInitialAcquire`/`withinScope` work (interrupted-restore recovery,\n// the roster lock, the command-channel IPC bridge, and the TUI surface).\n// `runStackWithBoot` threads those as CALLER-INJECTED hooks so\n// the CLI consumes ONE boot core instead of forking it. Because the bag is\n// non-public, its hooks receive an `InternalRunHandle` that additionally\n// carries the live `SupervisorHandle` (the seam the CLI hooks drain) —\n// substrate detail that NEVER reaches the public `RunHandle`.\n//\n// PR#21 boot-ordering invariant (do not reorder): the composed hooks run\n// the BUILT-IN work FIRST, then the caller's. For `beforeInitialAcquire`\n// this means the event-queue handoff + stop-bridge + command-pump are armed\n// before any caller hook runs (so a `stop()` during a caller hook always\n// has a bridge) AND before the first acquire (`superviseStackEffect` runs\n// the whole composed `beforeInitialAcquire` ahead of `runInitialAcquire`).\n// For `withinScope` the built-in readiness-gate resolution (the status\n// scan that resolves `bootDeferred`) runs first, so a caller `withinScope`\n// can never delay `handle.start`.\n\nimport {\n\tCause,\n\tDeferred,\n\tEffect,\n\tExit,\n\tFiber,\n\ttype FileSystem,\n\tLayer,\n\tLogger,\n\tQueue,\n\tRef,\n\ttype Scope,\n\tStream,\n\ttype SubscriptionRef,\n} from 'effect';\n\nimport { appName, stackName } from '../substrate/brand.ts';\nimport type { Identity } from '../substrate/identity.ts';\nimport type { EngineCommand, EngineEvent } from '../substrate/events.ts';\nimport { makeProjectionRefSync } from '../substrate/runtime/index.ts';\nimport type { StackPathsService } from '../substrate/runtime/paths.ts';\nimport type { SnapshotOrchestratorService } from '../orchestrators/snapshot/index.ts';\nimport type {\n\tSupervisorCommandHandler,\n\tSupervisorHandle,\n} from '../substrate/runtime/supervisor/index.ts';\nimport {\n\tbuildSubstrateLayers,\n\tlayerProductionOrchestrators,\n\tresolveProductionCodegenOptions,\n\tsuperviseStackWithProductionBoot,\n} from '../orchestrators/boot.ts';\nimport type { Codegenable } from '../orchestrators/codegen/service.ts';\nimport { readStackEngine, type Stack } from './define-devstack.ts';\nimport type { AnyPlugin } from '../substrate/plugin.ts';\nimport {\n\tresolveAppName,\n\tresolveNetworkSync,\n\tresolveStackName,\n\tresolveStateDir,\n} from './inference-network.ts';\nimport type {\n\tBootError,\n\tRunHandle,\n\tRunStackIdentityOptions,\n\tRunStackOptions,\n} from './run-stack.ts';\n\n// -----------------------------------------------------------------------------\n// Internal handle + boot bag\n// -----------------------------------------------------------------------------\n\n/** The handle the caller-injected `boot` hooks receive. It is the public\n * `RunHandle` PLUS the live `SupervisorHandle` — the substrate seam the\n * CLI `up` hooks drain (roster lock keys off it, the IPC bridge tails\n * `supervisor.events` / pumps `supervisor.commands`, the TUI mounts\n * `supervisor.state`). NON-PUBLIC: `index.ts` never re-exports it, so the\n * `SupervisorHandle` substrate type stays off the public surface. */\nexport interface InternalRunHandle extends RunHandle {\n\t/** The live supervisor handle for this boot. Available inside both\n\t * composed hooks; carries `events`/`commands`/`runCommand`/`state`/\n\t * `graph`/`registry` the CLI hooks consume directly. */\n\treadonly supervisor: SupervisorHandle;\n}\n\n/** The substrate services the seam already has in scope when it runs the\n * caller hooks (the composed `beforeInitialAcquire`/`withinScope` run\n * inside `superviseStackEffect`, which is `Effect.provide(substrate)`).\n * The bag hooks may yield these directly — the CLI `up` hooks drive the\n * ONE `SnapshotOrchestratorService` instance the supervisor's contribution\n * dispatcher registers participants on (so an operator `snapshot save`\n * captures the LIVE participant set, not an empty one off a sibling\n * orchestrator), plus `FileSystem`/`StackPathsService` for the\n * interrupted-restore + roster paths. `Scope.Scope` lets a hook fork\n * scoped fibers (the IPC pump, the TUI mount) onto the supervised scope.\n * NON-PUBLIC: like `InternalRunHandle`, this never reaches the public\n * `runStack` facade. */\nexport type BootHookServices =\n\t| Scope.Scope\n\t| SnapshotOrchestratorService\n\t| FileSystem.FileSystem\n\t| StackPathsService;\n\n/** Caller-injected boot hooks. Both run AFTER their built-in counterpart\n * (see the PR#21 ordering note at the top of this file). Their failures\n * fold into `BootError.cause` via the same `catchCause` tee the built-in\n * work uses, so `handle.start` stays `Effect<void, BootError, never>` —\n * the bag does NOT widen the public error channel. The R-channel is\n * `BootHookServices` (substrate services the seam already provides), NOT\n * `never`: the hooks run inside the supervised scope and may drive the\n * seam's live substrate (notably the supervisor's snapshot orchestrator). */\nexport interface RunStackBootBag {\n\treadonly devstackVersion?: string;\n\treadonly beforeInitialAcquire?: (\n\t\thandle: InternalRunHandle,\n\t) => Effect.Effect<void, unknown, BootHookServices>;\n\treadonly withinScope?: (\n\t\thandle: InternalRunHandle,\n\t) => Effect.Effect<void, unknown, BootHookServices>;\n}\n\n/** The substrate services a {@link CommandHandlerFactory} may yield — the\n * same singletons the contribution dispatcher + boot hooks see, minus\n * `Scope.Scope` (the factory runs before the supervised scope opens, so it\n * forks nothing). A handler closes over `SnapshotOrchestratorService` to\n * capture the LIVE participant set and `FileSystem` to thread it into the\n * snapshot I/O. */\ntype CommandHandlerServices =\n\t| SnapshotOrchestratorService\n\t| FileSystem.FileSystem\n\t| StackPathsService;\n\n/** A FACTORY for the supervisor command handler. The seam runs it ONCE,\n * inside the supervised body (so `CommandHandlerServices` — notably the live\n * `SnapshotOrchestratorService` + `FileSystem` — are in scope), BEFORE it\n * hands `startSupervisor` the resolved handler. So the resulting handler\n * (whose own R-channel is `never`, as the supervisor command loop requires)\n * closes over the seam's REAL substrate instances directly — no Deferred\n * hand-off. Resolving the snapshot orchestrator here yields the SAME\n * instance the contribution dispatcher registers live participants on, so an\n * operator `snapshot save` captures the LIVE chain/blob/db state, not an\n * empty set off a sibling orchestrator. */\nexport type CommandHandlerFactory = Effect.Effect<\n\tSupervisorCommandHandler,\n\tnever,\n\tCommandHandlerServices\n>;\n\n/** `RunStackOptions` plus the non-public injection points. `commandHandler`\n * is a FACTORY the seam resolves against its live substrate (see\n * {@link CommandHandlerFactory}); `boot` carries the caller-injected hooks. */\nexport interface RunStackOptionsWithBoot extends RunStackOptions {\n\treadonly commandHandler?: CommandHandlerFactory;\n\treadonly boot?: RunStackBootBag;\n\t/** Dev-`up` only: the STATIC (id-free) codegen contributions used to\n\t * regenerate the committed `src/generated` tree on every (re)acquire so a\n\t * Move-source edit refreshes the bindings WITHOUT baking live on-chain ids\n\t * into `config.ts`. The `up` verb sets it (`deriveContributions(...)`); the\n\t * public `runStack` facade never does (committed bindings are an input\n\t * there, not an output). */\n\treadonly emitBindings?: ReadonlyArray<Codegenable>;\n}\n\n// -----------------------------------------------------------------------------\n// Identity resolution\n// -----------------------------------------------------------------------------\n\nconst resolveIdentity = (\n\tstack: Stack<ReadonlyArray<AnyPlugin>>,\n\topts: RunStackIdentityOptions | undefined,\n\tcwd: string,\n): Identity => {\n\tconst app = resolveAppName({\n\t\texplicit: opts?.app,\n\t\tcwd,\n\t});\n\tconst stackNameStr = resolveStackName({\n\t\texplicit: opts?.stack ?? stack.options.stackName,\n\t\tcwd,\n\t});\n\t// Parse + validate up-front so a malformed value fails here rather\n\t// than downstream when a plugin probes the network. The identity's\n\t// network is the canonical name (`localnet`, `testnet`, …) — never the\n\t// `sui:`-prefixed wallet-standard form, which devstack does not use\n\t// internally — so plugin checks and on-disk cache namespaces key on one\n\t// stable name regardless of how it was spelled on the CLI.\n\tconst resolved = resolveNetworkSync({\n\t\texplicit: opts?.network,\n\t\tenv: process.env.DEVSTACK_NETWORK,\n\t\texplicitSource: 'runStack({ identity.network })',\n\t});\n\treturn {\n\t\tapp: appName(app),\n\t\tstack: stackName(stackNameStr),\n\t\tnetwork: resolved.parsed.name,\n\t};\n};\n\nconst toBootError = (cause: Cause.Cause<unknown>): BootError => ({\n\t_tag: 'BootError',\n\tcause,\n});\n\n// -----------------------------------------------------------------------------\n// Boot-time handle slots\n// -----------------------------------------------------------------------------\n\n/** The mutable cells + ref the handle's lifecycle effects close over. Every\n * field is allocated SYNCHRONOUSLY in `makeRunHandleSlots` (see its\n * sync-safety contract) so the public handle can expose a live `state` /\n * `commands` queue and let callers subscribe to `state.changes` BEFORE\n * `start` ever forks the supervisor fiber. */\ninterface RunHandleSlots {\n\t/** The supervisor's live projection. Seeded sync; populated by the\n\t * supervised body once it boots. */\n\treadonly state: SubscriptionRef.SubscriptionRef<\n\t\timport('../substrate/projection.ts').SubscribableState\n\t>;\n\t/** Resolved (or failed with `BootError`) by the readiness gate; `start`\n\t * awaits it. */\n\treadonly bootDeferred: Deferred.Deferred<void, BootError>;\n\t/** Set by `stop`; the built-in stop-bridge awaits it and offers\n\t * `shutdown.requested` onto the supervisor's command channel. */\n\treadonly stopRequested: Deferred.Deferred<void>;\n\t/** Handed the supervisor's event dequeue by `beforeInitialAcquire`; the\n\t * public `events` stream unwraps it. */\n\treadonly eventQueueRef: Deferred.Deferred<Queue.Dequeue<EngineEvent>>;\n\t/** Public command channel, pumped onto the supervisor's own queue once it\n\t * boots — the TUI/IPC publish target. */\n\treadonly commandQueue: Queue.Queue<EngineCommand>;\n\t/** Handed the supervisor's submit-and-await dispatch by\n\t * `beforeInitialAcquire`; the public `runCommand` awaits it. */\n\treadonly runCommandRef: Deferred.Deferred<\n\t\t(command: EngineCommand) => Effect.Effect<void, unknown, never>\n\t>;\n\t/** Holds the forked supervisor fiber; `stop`/`awaitShutdown` await it. */\n\treadonly fiberRef: Deferred.Deferred<Fiber.Fiber<void, never>>;\n\t/** Single-fire guard so a second `start` is a no-op. */\n\treadonly startClaim: Ref.Ref<boolean>;\n\t/** Tee for a mid-run defect/failure raised AFTER boot completed, so\n\t * `awaitShutdown` can re-surface it (a post-boot `Deferred.fail` is a\n\t * no-op). */\n\treadonly midRunCauseRef: Ref.Ref<Cause.Cause<unknown> | null>;\n}\n\n/**\n * Allocate every boot-time handle slot in ONE place.\n *\n * SYNC-SAFETY CONTRACT (the reason this is a sync constructor, not an\n * `Effect`): the handle must expose a real `state` / `commands` queue and\n * be subscribable BEFORE `start` forks the supervisor. `Deferred.make`,\n * `Queue.unbounded`, and `Ref.make` are pure sync effects (no side effects,\n * no async), so `Effect.runSync` is safe for all of them. The projection ref\n * goes through the explicit `makeProjectionRefSync` so the sync contract is\n * pinned at the substrate constructor — if `makeProjectionRef` ever picks up\n * an async/Layer wrapper (`withSpan`, annotation), `makeProjectionRefSync`\n * must stay sync-only or this whole allocation must move behind a\n * Deferred-handoff seam.\n */\nconst makeRunHandleSlots = (): RunHandleSlots => ({\n\tstate: makeProjectionRefSync(),\n\tbootDeferred: Effect.runSync(Deferred.make<void, BootError>()),\n\tstopRequested: Effect.runSync(Deferred.make<void>()),\n\teventQueueRef: Effect.runSync(Deferred.make<Queue.Dequeue<EngineEvent>>()),\n\tcommandQueue: Effect.runSync(Queue.unbounded<EngineCommand>()),\n\trunCommandRef: Effect.runSync(\n\t\tDeferred.make<(command: EngineCommand) => Effect.Effect<void, unknown, never>>(),\n\t),\n\tfiberRef: Effect.runSync(Deferred.make<Fiber.Fiber<void, never>>()),\n\tstartClaim: Effect.runSync(Ref.make(false)),\n\tmidRunCauseRef: Effect.runSync(Ref.make<Cause.Cause<unknown> | null>(null)),\n});\n\n/** Compose the public handle + the live `SupervisorHandle` into the\n * `InternalRunHandle` the caller boot hooks receive. The two composed hooks\n * build it identically; factored here so the closure-soundness note (the\n * hooks only deref `publicHandle` at boot, after the body assigns it) lives\n * in one place. */\nconst buildInternalHandle = (\n\tpublicHandle: RunHandle,\n\tsupervisor: SupervisorHandle,\n): InternalRunHandle => ({ ...publicHandle, supervisor });\n\n// -----------------------------------------------------------------------------\n// runStackWithBoot\n// -----------------------------------------------------------------------------\n\n/**\n * The implementation behind `runStack`. Boots a `Stack` for programmatic\n * embedding and returns a `RunHandle` synchronously; the supervisor fiber\n * is forked on `start`.\n *\n * Lifecycle (identical to `runStack`'s documented contract):\n *\n * 1. `runStackWithBoot(stack, opts)` — synchronous; no fiber forked yet.\n * 2. `await Effect.runPromise(handle.start)` — forks the supervisor,\n * blocks until every plugin reaches `ready` (or fails with\n * `BootError`).\n * 3. Use `handle.events` / `handle.state` to observe.\n * 4. `await Effect.runPromise(handle.stop)` — graceful shutdown.\n * 5. `handle.awaitShutdown` resolves once finalizers complete.\n *\n * Internal architecture: the handle stores a `Deferred` for boot\n * completion. The supervised body resolves it from the SUPERVISOR-OWNED\n * readiness signal — `superviseStackEffect`'s `withinScope` hook fires\n * only after `runInitialAcquire` wins its `raceFirst` against\n * `awaitShutdown` (i.e. once `allReadyOrTerminal` — `ready || done` — is\n * true). This is the same `done`-tolerant gate the long-running CLI path\n * uses; it does NOT watch per-node ready-gates (a non-failed terminal\n * `done` node need never resolve its `awaitReady` gate). Initial-acquire\n * failures surface through the supervised body's `catchCause` tee, which\n * fails the deferred with a `BootError`. `start` awaits it.\n *\n * `opts.commandHandler` threads straight into `superviseStackEffect`'s\n * `commandHandler` slot. `opts.boot` injects caller hooks that run AFTER\n * their built-in counterpart (the PR#21 ordering); their failures fold\n * into `BootError` via the same `catchCause` tee, so `start` keeps its\n * `Effect<void, BootError, never>` type.\n */\nexport const runStackWithBoot = (\n\tstack: Stack<ReadonlyArray<AnyPlugin>>,\n\topts: RunStackOptionsWithBoot = {},\n): RunHandle => {\n\tconst engineStack = readStackEngine(stack);\n\tconst runtimeRoot = resolveStateDir({\n\t\truntimeRoot: opts.runtimeRoot,\n\t\tstateDir: opts.stateDir ?? engineStack.options.stateDir,\n\t\tenv: process.env.DEVSTACK_STATE_DIR,\n\t\tcwd: process.cwd(),\n\t});\n\tconst appRoot = opts.appRoot ?? process.cwd();\n\tconst identity = resolveIdentity(stack, opts.identity, appRoot);\n\tconst codegen = opts.codegen ?? engineStack.options.codegen;\n\n\tconst supervisedStack = {\n\t\t_tag: 'Stack' as const,\n\t\tmembers: engineStack.members,\n\t\toptions: engineStack.options,\n\t};\n\n\t// Every boot-time handle slot is allocated in ONE place (the sync-safety\n\t// contract lives on `makeRunHandleSlots`): the handle must expose a live\n\t// `state` / `commands` queue and be subscribable BEFORE `start` forks the\n\t// supervisor fiber.\n\tconst {\n\t\tstate,\n\t\tbootDeferred,\n\t\tstopRequested,\n\t\teventQueueRef,\n\t\tcommandQueue,\n\t\trunCommandRef,\n\t\tfiberRef,\n\t\tstartClaim,\n\t\tmidRunCauseRef,\n\t} = makeRunHandleSlots();\n\n\t// Resolve the per-stack LIVE codegen output location through the ONE\n\t// shared boot seam: EVERY live run emits into\n\t// `.devstack/stacks/<stack>/generated/` (gitignored), so the id-bearing\n\t// tree never lands in committed source. The committed `src/generated`\n\t// tree is owned solely by the stack-free `codegen` verb. An explicit\n\t// `opts.codegen` (or the stack's own `codegen`) is honored verbatim by\n\t// the resolver. The effective stack (the resolved `identity.stack`) is\n\t// in scope here, exactly as in the CLI's `buildVerbLayers` seam — both\n\t// route through `resolveProductionCodegenOptions`.\n\tconst substrate = layerProductionOrchestrators({\n\t\tcodegen: resolveProductionCodegenOptions({\n\t\t\tappRoot,\n\t\t\tcodegen,\n\t\t}),\n\t}).pipe(Layer.provideMerge(buildSubstrateLayers(identity, runtimeRoot)));\n\n\t// The submit-and-await dispatch surfaced on the public handle. Awaits\n\t// the supervisor's `runCommand` (wired in the built-in\n\t// `beforeInitialAcquire`), then delegates.\n\tconst runCommand = (command: EngineCommand): Effect.Effect<void, unknown, never> =>\n\t\tEffect.flatMap(Deferred.await(runCommandRef), (dispatch) => dispatch(command));\n\n\tconst events: Stream.Stream<EngineEvent, never, never> = Stream.unwrap(\n\t\tEffect.gen(function* () {\n\t\t\tconst queue = yield* Deferred.await(eventQueueRef);\n\t\t\treturn Stream.fromQueue(queue);\n\t\t}),\n\t);\n\n\t// The PUBLIC handle is assigned exactly once at the end of this body\n\t// (after the lifecycle effects exist) and returned. The composed boot\n\t// hooks below close over `publicHandle` to build an `InternalRunHandle`\n\t// (the public handle + the live `SupervisorHandle`), but they only\n\t// deref it at BOOT time — long after this synchronous body finishes and\n\t// assigns it — so the closure is sound. Declared with a forward `let`\n\t// rather than mutated readonly fields so `RunHandle` stays immutable.\n\tlet publicHandle: RunHandle;\n\n\tconst supervised = Effect.gen(function* () {\n\t\t// Resolve the command-handler FACTORY against the seam's live\n\t\t// substrate (in scope here via `Effect.provide(substrate)` below)\n\t\t// BEFORE `startSupervisor` consumes the handler. The factory closes\n\t\t// over the same `SnapshotOrchestratorService` the contribution\n\t\t// dispatcher registers participants on, so the resulting handler\n\t\t// (R = `never`, as the command loop requires) drives the LIVE state.\n\t\tconst commandHandler =\n\t\t\topts.commandHandler === undefined ? undefined : yield* opts.commandHandler;\n\t\t// The shared production-boot assembly (contribution dispatcher +\n\t\t// post-acquire hook + built-in plugin-context extension, then\n\t\t// `layerBuiltInPluginRuntime`) lives in ONE place\n\t\t// (`orchestrators/boot.ts superviseStackWithProductionBoot`). This\n\t\t// seam supplies only the LONG-RUNNING-specific wrapping: the\n\t\t// resolved command handler, the public `runStack({ extendContext })`\n\t\t// chained after the built-in, and the composed boot hooks below.\n\t\tyield* superviseStackWithProductionBoot(supervisedStack, identity, state, {\n\t\t\textras: stack.options.extras,\n\t\t\t...(opts.emitBindings === undefined ? {} : { emitBindings: opts.emitBindings }),\n\t\t\t...(opts.boot?.devstackVersion === undefined\n\t\t\t\t? {}\n\t\t\t\t: { devstackVersion: opts.boot.devstackVersion }),\n\t\t\t...(commandHandler === undefined ? {} : { commandHandler }),\n\t\t\t...(opts.extendContext === undefined\n\t\t\t\t? {}\n\t\t\t\t: { extendContextAfterBuiltIn: opts.extendContext }),\n\t\t\t// ── COMPOSED `beforeInitialAcquire` (ONE ordered gen) ──────────\n\t\t\t// ORDER (PR#21-load-bearing): BUILT-IN work first, THEN caller.\n\t\t\t// 1. built-in: event-queue handoff + runCommand surface +\n\t\t\t// command-pump fork + stop-bridge fork.\n\t\t\t// 2. caller: `opts.boot?.beforeInitialAcquire(...)`.\n\t\t\t// `superviseStackEffect` runs this whole composed effect BEFORE\n\t\t\t// `runInitialAcquire`, so the caller hook (the CLI's recover/\n\t\t\t// roster/IPC/TUI bundle) runs before first acquire too; and because\n\t\t\t// the built-in stop-bridge is armed first, a `stop()` raised during\n\t\t\t// a caller hook always has a command bridge.\n\t\t\tbeforeInitialAcquire: (handle) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Deferred.succeed(eventQueueRef, handle.events).pipe(\n\t\t\t\t\t\tEffect.catch(() => Effect.void),\n\t\t\t\t\t);\n\t\t\t\t\t// Surface the supervisor's submit-and-await dispatch onto the\n\t\t\t\t\t// public handle BEFORE acquire so a boot failure can't leave\n\t\t\t\t\t// the handle without a command bridge.\n\t\t\t\t\tyield* Deferred.succeed(runCommandRef, handle.runCommand).pipe(\n\t\t\t\t\t\tEffect.catch(() => Effect.void),\n\t\t\t\t\t);\n\t\t\t\t\t// Pump the eager public command queue onto the supervisor's\n\t\t\t\t\t// own command queue (`handle.commands` — the seam the signal\n\t\t\t\t\t// handler + command loop drain). A publish on the public\n\t\t\t\t\t// queue therefore always reaches the supervisor's drain.\n\t\t\t\t\tyield* Effect.forkScoped(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\twhile (true) {\n\t\t\t\t\t\t\t\tconst command = yield* Queue.take(commandQueue);\n\t\t\t\t\t\t\t\tyield* Queue.offer(handle.commands, command);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\n\t\t\t\t\t// Bridge `stop` requests onto the supervisor's command\n\t\t\t\t\t// channel before acquire starts so a boot failure cannot\n\t\t\t\t\t// leave the public handle without an event/command bridge.\n\t\t\t\t\tyield* Effect.forkScoped(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tyield* Deferred.await(stopRequested);\n\t\t\t\t\t\t\tyield* Queue.offer(handle.commands, {\n\t\t\t\t\t\t\t\ttag: 'shutdown.requested',\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\n\t\t\t\t\t// Caller-injected work runs AFTER the built-in bridges are\n\t\t\t\t\t// armed. Its failures fold into the supervised body's\n\t\t\t\t\t// `catchCause` below (and thence into `BootError`).\n\t\t\t\t\tif (opts.boot?.beforeInitialAcquire !== undefined) {\n\t\t\t\t\t\tyield* opts.boot.beforeInitialAcquire(buildInternalHandle(publicHandle, handle));\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t// ── COMPOSED `withinScope` (ONE ordered gen) ───────────────────\n\t\t\t// ORDER: BUILT-IN readiness-gate resolution first, THEN caller.\n\t\t\t// 1. built-in: the status scan that resolves `bootDeferred`.\n\t\t\t// 2. caller: `opts.boot?.withinScope(...)`.\n\t\t\t// Resolving the gate first means a caller `withinScope` can never\n\t\t\t// delay `handle.start`.\n\t\t\twithinScope: (handle) =>\n\t\t\t\t// Resolve the boot gate from the SUPERVISOR-OWNED readiness\n\t\t\t\t// signal. `superviseStackEffect` only calls `withinScope`\n\t\t\t\t// after `runInitialAcquire` wins its `raceFirst` against\n\t\t\t\t// `awaitShutdown` and yields `'booted'` (boot.ts) — i.e. once\n\t\t\t\t// `allReadyOrTerminal` is true (`ready || done`). This is the\n\t\t\t\t// SAME `done`-tolerant gate the long-running CLI path uses,\n\t\t\t\t// not a per-node `awaitReady` watcher (whose ready-gate a\n\t\t\t\t// non-failed terminal `done` node need never resolve, so it\n\t\t\t\t// would hang here).\n\t\t\t\t//\n\t\t\t\t// `runInitialAcquire` does NOT itself fail on a per-PLUGIN\n\t\t\t\t// acquire failure: `acquireFullGraph` swallows node failures\n\t\t\t\t// into the registry's `failed` status (the CLI surfaces them as\n\t\t\t\t// red TUI rows and keeps the stack up). runStack's contract is\n\t\t\t\t// stricter — `start` FAILS with `BootError` if any node failed\n\t\t\t\t// initial acquire. So gate on the STATUS contract here: succeed\n\t\t\t\t// iff every node is `ready`/`done`, else fail with the first\n\t\t\t\t// `failed` node's `PluginAcquireFailed` cause. A failed node's\n\t\t\t\t// ready-gate IS resolved (markFailed failed it), so reading it\n\t\t\t\t// is hang-free — the `done`-tolerant equivalent of the old\n\t\t\t\t// watcher's failure detection. (Post-acquire HOOK failures —\n\t\t\t\t// codegen / manifest — fail `runInitialAcquire` directly and\n\t\t\t\t// tee through the supervised body's `catchCause` below; we\n\t\t\t\t// never reach here on that path.)\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tlet firstFailure: BootError | null = null;\n\t\t\t\t\tfor (const [key] of handle.graph.nodes) {\n\t\t\t\t\t\tconst status = yield* handle.registry\n\t\t\t\t\t\t\t.getStatus(key)\n\t\t\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed('failed' as const)));\n\t\t\t\t\t\tif (status === 'failed') {\n\t\t\t\t\t\t\tconst failureExit = yield* Effect.exit(handle.registry.awaitReady(key));\n\t\t\t\t\t\t\tfirstFailure = toBootError(\n\t\t\t\t\t\t\t\tExit.isFailure(failureExit) ? failureExit.cause : Cause.empty,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (firstFailure === null) {\n\t\t\t\t\t\tyield* Deferred.succeed(bootDeferred, undefined).pipe(Effect.asVoid);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tyield* Deferred.fail(bootDeferred, firstFailure).pipe(Effect.asVoid);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Caller-injected work runs AFTER the boot gate is resolved,\n\t\t\t\t\t// so it can never delay `handle.start`. Its failures fold\n\t\t\t\t\t// into the supervised body's `catchCause` below; since the\n\t\t\t\t\t// boot gate already succeeded by this point, such a failure\n\t\t\t\t\t// surfaces on `awaitShutdown` (mid-run), not `start` — the\n\t\t\t\t\t// same place a built-in `withinScope` failure would land.\n\t\t\t\t\tif (opts.boot?.withinScope !== undefined) {\n\t\t\t\t\t\tyield* opts.boot.withinScope(buildInternalHandle(publicHandle, handle));\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t});\n\t});\n\n\tconst loggerLayer = Logger.layer([]);\n\tconst layered = supervised.pipe(Effect.provide(substrate), Effect.provide(loggerLayer));\n\n\t// Convert any uncaught cause into a boot failure if the deferred\n\t// hasn't completed. If `bootDeferred` HAS already succeeded then\n\t// `Deferred.fail` is a no-op — the cause is a mid-run failure\n\t// (e.g. a plugin scope finalizer defect) and would otherwise be\n\t// silently dropped: the fiber exits `Success(void)` and\n\t// `awaitShutdown` resolves clean. Tee the cause into\n\t// `midRunCauseRef` so `awaitShutdown` can re-surface it, AND emit\n\t// `Effect.logError(Cause.pretty(cause))` so observability stays\n\t// loud regardless of whether anyone awaits. Boot failures (still in\n\t// the bootDeferred-pending window) only surface via `start`, not\n\t// here — we'd otherwise re-raise them on `awaitShutdown` after\n\t// `start` already rejected. NOTE: a CALLER `boot` hook failure folds\n\t// through this SAME tee, so its failure surfaces as `BootError` on\n\t// `start` (when raised before the gate resolves) or re-raises on\n\t// `awaitShutdown` (after) — never widening `start`'s error channel.\n\tconst supervisedProgram: Effect.Effect<void, never, never> = layered.pipe(\n\t\tEffect.catchCause((cause) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst bootAlreadyCompleted = yield* Deferred.isDone(bootDeferred);\n\t\t\t\tyield* Deferred.fail(bootDeferred, toBootError(cause)).pipe(\n\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\tEffect.catch(() => Effect.void),\n\t\t\t\t);\n\t\t\t\tif (bootAlreadyCompleted) {\n\t\t\t\t\tyield* Ref.set(midRunCauseRef, cause);\n\t\t\t\t}\n\t\t\t\tyield* Effect.logError(`devstack runStack: supervisor died\\n${Cause.pretty(cause)}`);\n\t\t\t}),\n\t\t),\n\t);\n\n\tconst start: Effect.Effect<void, BootError, never> = Effect.uninterruptibleMask((restore) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst shouldStart = yield* Ref.modify(startClaim, (started) =>\n\t\t\t\tstarted ? [false, true] : [true, true],\n\t\t\t);\n\t\t\tif (shouldStart) {\n\t\t\t\t// `forkDetach` decouples the supervisor fiber from the `start`\n\t\t\t\t// fiber's scope. `forkChild`\n\t\t\t\t// would tie the supervisor to whatever fiber runs `start`, and\n\t\t\t\t// once `start` resolves (after `bootDeferred` succeeds) the\n\t\t\t\t// runtime would interrupt the supervisor — transitioning every\n\t\t\t\t// plugin `ready → stopping → stopped` before the caller can\n\t\t\t\t// read post-ready state or call `handle.stop`. The handle's\n\t\t\t\t// explicit `stop` + `awaitShutdown` paths are the only\n\t\t\t\t// shutdown signals; the captured `Fiber` reference is how the\n\t\t\t\t// daemon stays releasable.\n\t\t\t\tconst fiber = yield* Effect.forkDetach(supervisedProgram);\n\t\t\t\tyield* Deferred.succeed(fiberRef, fiber);\n\t\t\t}\n\t\t\tyield* restore(Deferred.await(bootDeferred));\n\t\t}),\n\t);\n\n\tconst stop: Effect.Effect<void, never, never> = Effect.gen(function* () {\n\t\tyield* Deferred.succeed(stopRequested, undefined).pipe(Effect.catch(() => Effect.void));\n\t\tconst alreadyStarted = yield* Deferred.isDone(fiberRef);\n\t\tif (!alreadyStarted) {\n\t\t\treturn;\n\t\t}\n\t\tconst fiber = yield* Deferred.await(fiberRef);\n\t\t// `Fiber.await` returns an `Exit` without raising — handles both\n\t\t// success and interrupt-cause cases (the supervisor's\n\t\t// graceful-shutdown path closes the scope, which surfaces as an\n\t\t// interrupt cause if the fiber was mid-await on the latch poll).\n\t\tyield* Fiber.await(fiber);\n\t});\n\n\tconst awaitShutdown: Effect.Effect<void, unknown, never> = Effect.gen(function* () {\n\t\tconst alreadyStarted = yield* Deferred.isDone(fiberRef);\n\t\tif (!alreadyStarted) {\n\t\t\treturn;\n\t\t}\n\t\tconst fiber = yield* Deferred.await(fiberRef);\n\t\tyield* Fiber.await(fiber);\n\t\t// Re-surface any mid-run defect/failure captured by the\n\t\t// supervised body's `catchCause` (boot failures are already\n\t\t// surfaced via `start`). Without this re-raise a plugin scope\n\t\t// finalizer defect would silently drop and operators get no\n\t\t// signal — see the comment on `midRunCauseRef` above.\n\t\tconst midRunCause = yield* Ref.get(midRunCauseRef);\n\t\tif (midRunCause !== null) {\n\t\t\treturn yield* Effect.failCause(midRunCause);\n\t\t}\n\t});\n\n\t// Assign the handle the composed boot hooks close over (they only deref\n\t// it at boot, after this assignment) and return it.\n\tpublicHandle = {\n\t\tstart,\n\t\tstop,\n\t\tawaitShutdown,\n\t\tevents,\n\t\tstate,\n\t\tcommands: commandQueue,\n\t\trunCommand,\n\t\tidentity,\n\t};\n\n\treturn publicHandle;\n};\n"],"mappings":"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"}
|
|
@@ -26,6 +26,10 @@ interface TestableDAppKit<W extends UiWalletLike> {
|
|
|
26
26
|
switchAccount(args: {
|
|
27
27
|
account: W['accounts'][number];
|
|
28
28
|
}): void;
|
|
29
|
+
/** dApp Kit's public network switcher — the same surface the app's own UI
|
|
30
|
+
* uses. The bridge calls this so a test can flip networks exactly as it
|
|
31
|
+
* flips accounts. */
|
|
32
|
+
switchNetwork(network: string): void;
|
|
29
33
|
readonly stores: {
|
|
30
34
|
readonly $wallets: {
|
|
31
35
|
get(): readonly W[];
|
|
@@ -38,6 +42,9 @@ interface TestableDAppKit<W extends UiWalletLike> {
|
|
|
38
42
|
} | null;
|
|
39
43
|
};
|
|
40
44
|
};
|
|
45
|
+
readonly $currentNetwork: {
|
|
46
|
+
get(): string;
|
|
47
|
+
};
|
|
41
48
|
};
|
|
42
49
|
}
|
|
43
50
|
/**
|
|
@@ -54,6 +61,11 @@ interface TestableDAppKit<W extends UiWalletLike> {
|
|
|
54
61
|
* to the dev wallet it switches the active account; otherwise it connects,
|
|
55
62
|
* selecting the requested account. Account names resolve to the dev wallet's
|
|
56
63
|
* wallet-standard accounts by `label` (the devstack account name).
|
|
64
|
+
*
|
|
65
|
+
* The slot also publishes `switchNetwork` (+ `currentNetwork`): the SAME bridge,
|
|
66
|
+
* one level over — a test flips the active dApp Kit network exactly as it flips
|
|
67
|
+
* the account, driving the public `switchNetwork`. The dev wallet stays
|
|
68
|
+
* registered across the switch; only the active network/client changes.
|
|
57
69
|
*/
|
|
58
70
|
declare function registerDAppKitForTesting<W extends UiWalletLike>(dAppKit: TestableDAppKit<W>): void;
|
|
59
71
|
//#endregion
|
|
@@ -20,6 +20,11 @@ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
20
20
|
* to the dev wallet it switches the active account; otherwise it connects,
|
|
21
21
|
* selecting the requested account. Account names resolve to the dev wallet's
|
|
22
22
|
* wallet-standard accounts by `label` (the devstack account name).
|
|
23
|
+
*
|
|
24
|
+
* The slot also publishes `switchNetwork` (+ `currentNetwork`): the SAME bridge,
|
|
25
|
+
* one level over — a test flips the active dApp Kit network exactly as it flips
|
|
26
|
+
* the account, driving the public `switchNetwork`. The dev wallet stays
|
|
27
|
+
* registered across the switch; only the active network/client changes.
|
|
23
28
|
*/
|
|
24
29
|
function registerDAppKitForTesting(dAppKit) {
|
|
25
30
|
const resolveLabelledAccount = async (accountName) => {
|
|
@@ -50,7 +55,15 @@ function registerDAppKitForTesting(dAppKit) {
|
|
|
50
55
|
account
|
|
51
56
|
});
|
|
52
57
|
};
|
|
53
|
-
|
|
58
|
+
const switchNetwork = (network) => {
|
|
59
|
+
dAppKit.switchNetwork(network);
|
|
60
|
+
};
|
|
61
|
+
const currentNetwork = () => dAppKit.stores.$currentNetwork.get();
|
|
62
|
+
globalThis.__devstackDAppKit__ = {
|
|
63
|
+
selectAccount,
|
|
64
|
+
switchNetwork,
|
|
65
|
+
currentNetwork
|
|
66
|
+
};
|
|
54
67
|
}
|
|
55
68
|
//#endregion
|
|
56
69
|
export { registerDAppKitForTesting };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/build-integrations/dapp-kit/index.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n//\n// dApp Kit test bridge — registers an app's dApp Kit instance so the Playwright\n// `connectAs` helper can drive a REAL connection during e2e.\n//\n// This is a TESTING feature, not an app feature. The app calls\n// `registerDAppKitForTesting(dAppKit)` DEV-only (it is a no-op consumer in\n// production: a `vite build` strips the `import.meta.env.DEV` branch and the\n// dev wallet is never injected). It performs NO connection on its own — the app\n// loads disconnected, dApp Kit's own `autoConnect` only re-connects a genuine\n// prior session, and an actual connection happens solely when a test calls the\n// published slot.\n//\n// The connection goes through dApp Kit's public API (`connectWallet` /\n// `switchAccount`) — no localStorage seeding, no narrowing/widening the\n// wallet's exposed accounts.\n//\n// Browser-safe: this module (and the `dapp-kit-slot` contract it imports) must\n// never pull in `node:*` — it is bundled into app pages by Vite.\n\nimport { DAPP_KIT_SLOT_KEY, type DAppKitSlot } from '../runtime/dapp-kit-slot.ts';\n\n/** A wallet-standard account as surfaced by dApp Kit. The dev wallet sets each\n * account's `label` to its devstack account name (`alice`, `bob`, …). */\ninterface UiWalletAccountLike {\n\treadonly address: string;\n\treadonly label?: string;\n}\n\ninterface UiWalletLike {\n\treadonly name: string;\n\treadonly accounts: readonly UiWalletAccountLike[];\n}\n\n/**\n * The slice of a dApp Kit instance this bridge drives, generic over the app's\n * concrete wallet type `W` (inferred from `$wallets`). The account type is\n * DERIVED as `W['accounts'][number]` rather than inferred separately, so the\n * objects pulled from `$wallets` flow back into `connectWallet` /\n * `switchAccount` as their real (branded `UiWallet` / `UiWalletAccount`) types\n * — a structural stand-in is not assignable to dApp Kit's branded parameters.\n * The app passes its real `createDAppKit(...)` return value and `W` is inferred.\n */\nexport interface TestableDAppKit<W extends UiWalletLike> {\n\tconnectWallet(args: { wallet: W; account: W['accounts'][number] }): Promise<unknown>;\n\tswitchAccount(args: { account: W['accounts'][number] }): void;\n\treadonly stores: {\n\t\treadonly $wallets: { get(): readonly W[] };\n\t\treadonly $connection: {\n\t\t\tget(): { readonly isConnected: boolean; readonly wallet: { readonly name: string } | null };\n\t\t};\n\t};\n}\n\n/** How long `selectAccount` waits for the dev wallet (and the requested\n * account) to register before giving up — the wallet comes up asynchronously,\n * so a `connectAs(...)` fired right after page load may beat it. */\nconst RESOLVE_TIMEOUT_MS = 10_000;\nconst RESOLVE_POLL_MS = 50;\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Register `dAppKit` with the `globalThis.__devstackDAppKit__` test slot that\n * Playwright's `connectAs` / `selectAccount` helpers drive. Call once, DEV-only,\n * after `createDAppKit(...)`:\n *\n * ```ts\n * export const dAppKit = createDAppKit({ ... });\n * if (import.meta.env.DEV) registerDAppKitForTesting(dAppKit);\n * ```\n *\n * The published `selectAccount` performs a real connection: if already connected\n * to the dev wallet it switches the active account; otherwise it connects,\n * selecting the requested account. Account names resolve to the dev wallet's\n * wallet-standard accounts by `label` (the devstack account name).\n */\nexport function registerDAppKitForTesting<W extends UiWalletLike>(\n\tdAppKit: TestableDAppKit<W>,\n): void {\n\tconst resolveLabelledAccount = async (\n\t\taccountName: string,\n\t): Promise<{ wallet: W; account: W['accounts'][number] } | null> => {\n\t\tconst deadline = Date.now() + RESOLVE_TIMEOUT_MS;\n\t\tfor (;;) {\n\t\t\tfor (const wallet of dAppKit.stores.$wallets.get()) {\n\t\t\t\tconst account = wallet.accounts.find((candidate) => candidate.label === accountName);\n\t\t\t\tif (account !== undefined) return { wallet, account };\n\t\t\t}\n\t\t\tif (Date.now() >= deadline) return null;\n\t\t\tawait sleep(RESOLVE_POLL_MS);\n\t\t}\n\t};\n\n\tconst selectAccount = async (accountName: string): Promise<void> => {\n\t\tconst found = await resolveLabelledAccount(accountName);\n\t\tif (found === null) {\n\t\t\tthrow new Error(\n\t\t\t\t`connectAs(\"${accountName}\"): no dev-wallet account labelled \"${accountName}\" ` +\n\t\t\t\t\t`registered within ${RESOLVE_TIMEOUT_MS}ms. Confirm the dev wallet is injected ` +\n\t\t\t\t\t`and the account name exists.`,\n\t\t\t);\n\t\t}\n\t\tconst { wallet, account } = found;\n\t\tconst connection = dAppKit.stores.$connection.get();\n\t\tif (connection.isConnected && connection.wallet?.name === wallet.name) {\n\t\t\t// Already connected to the dev wallet — just switch the active account.\n\t\t\tdAppKit.switchAccount({ account });\n\t\t\treturn;\n\t\t}\n\t\tawait dAppKit.connectWallet({ wallet, account });\n\t};\n\n\t(globalThis as { [DAPP_KIT_SLOT_KEY]?: DAppKitSlot }).__devstackDAppKit__ = {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/build-integrations/dapp-kit/index.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n//\n// dApp Kit test bridge — registers an app's dApp Kit instance so the Playwright\n// `connectAs` helper can drive a REAL connection during e2e.\n//\n// This is a TESTING feature, not an app feature. The app calls\n// `registerDAppKitForTesting(dAppKit)` DEV-only (it is a no-op consumer in\n// production: a `vite build` strips the `import.meta.env.DEV` branch and the\n// dev wallet is never injected). It performs NO connection on its own — the app\n// loads disconnected, dApp Kit's own `autoConnect` only re-connects a genuine\n// prior session, and an actual connection happens solely when a test calls the\n// published slot.\n//\n// The connection goes through dApp Kit's public API (`connectWallet` /\n// `switchAccount`) — no localStorage seeding, no narrowing/widening the\n// wallet's exposed accounts.\n//\n// Browser-safe: this module (and the `dapp-kit-slot` contract it imports) must\n// never pull in `node:*` — it is bundled into app pages by Vite.\n\nimport { DAPP_KIT_SLOT_KEY, type DAppKitSlot } from '../runtime/dapp-kit-slot.ts';\n\n/** A wallet-standard account as surfaced by dApp Kit. The dev wallet sets each\n * account's `label` to its devstack account name (`alice`, `bob`, …). */\ninterface UiWalletAccountLike {\n\treadonly address: string;\n\treadonly label?: string;\n}\n\ninterface UiWalletLike {\n\treadonly name: string;\n\treadonly accounts: readonly UiWalletAccountLike[];\n}\n\n/**\n * The slice of a dApp Kit instance this bridge drives, generic over the app's\n * concrete wallet type `W` (inferred from `$wallets`). The account type is\n * DERIVED as `W['accounts'][number]` rather than inferred separately, so the\n * objects pulled from `$wallets` flow back into `connectWallet` /\n * `switchAccount` as their real (branded `UiWallet` / `UiWalletAccount`) types\n * — a structural stand-in is not assignable to dApp Kit's branded parameters.\n * The app passes its real `createDAppKit(...)` return value and `W` is inferred.\n */\nexport interface TestableDAppKit<W extends UiWalletLike> {\n\tconnectWallet(args: { wallet: W; account: W['accounts'][number] }): Promise<unknown>;\n\tswitchAccount(args: { account: W['accounts'][number] }): void;\n\t/** dApp Kit's public network switcher — the same surface the app's own UI\n\t * uses. The bridge calls this so a test can flip networks exactly as it\n\t * flips accounts. */\n\tswitchNetwork(network: string): void;\n\treadonly stores: {\n\t\treadonly $wallets: { get(): readonly W[] };\n\t\treadonly $connection: {\n\t\t\tget(): { readonly isConnected: boolean; readonly wallet: { readonly name: string } | null };\n\t\t};\n\t\treadonly $currentNetwork: { get(): string };\n\t};\n}\n\n/** How long `selectAccount` waits for the dev wallet (and the requested\n * account) to register before giving up — the wallet comes up asynchronously,\n * so a `connectAs(...)` fired right after page load may beat it. */\nconst RESOLVE_TIMEOUT_MS = 10_000;\nconst RESOLVE_POLL_MS = 50;\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Register `dAppKit` with the `globalThis.__devstackDAppKit__` test slot that\n * Playwright's `connectAs` / `selectAccount` helpers drive. Call once, DEV-only,\n * after `createDAppKit(...)`:\n *\n * ```ts\n * export const dAppKit = createDAppKit({ ... });\n * if (import.meta.env.DEV) registerDAppKitForTesting(dAppKit);\n * ```\n *\n * The published `selectAccount` performs a real connection: if already connected\n * to the dev wallet it switches the active account; otherwise it connects,\n * selecting the requested account. Account names resolve to the dev wallet's\n * wallet-standard accounts by `label` (the devstack account name).\n *\n * The slot also publishes `switchNetwork` (+ `currentNetwork`): the SAME bridge,\n * one level over — a test flips the active dApp Kit network exactly as it flips\n * the account, driving the public `switchNetwork`. The dev wallet stays\n * registered across the switch; only the active network/client changes.\n */\nexport function registerDAppKitForTesting<W extends UiWalletLike>(\n\tdAppKit: TestableDAppKit<W>,\n): void {\n\tconst resolveLabelledAccount = async (\n\t\taccountName: string,\n\t): Promise<{ wallet: W; account: W['accounts'][number] } | null> => {\n\t\tconst deadline = Date.now() + RESOLVE_TIMEOUT_MS;\n\t\tfor (;;) {\n\t\t\tfor (const wallet of dAppKit.stores.$wallets.get()) {\n\t\t\t\tconst account = wallet.accounts.find((candidate) => candidate.label === accountName);\n\t\t\t\tif (account !== undefined) return { wallet, account };\n\t\t\t}\n\t\t\tif (Date.now() >= deadline) return null;\n\t\t\tawait sleep(RESOLVE_POLL_MS);\n\t\t}\n\t};\n\n\tconst selectAccount = async (accountName: string): Promise<void> => {\n\t\tconst found = await resolveLabelledAccount(accountName);\n\t\tif (found === null) {\n\t\t\tthrow new Error(\n\t\t\t\t`connectAs(\"${accountName}\"): no dev-wallet account labelled \"${accountName}\" ` +\n\t\t\t\t\t`registered within ${RESOLVE_TIMEOUT_MS}ms. Confirm the dev wallet is injected ` +\n\t\t\t\t\t`and the account name exists.`,\n\t\t\t);\n\t\t}\n\t\tconst { wallet, account } = found;\n\t\tconst connection = dAppKit.stores.$connection.get();\n\t\tif (connection.isConnected && connection.wallet?.name === wallet.name) {\n\t\t\t// Already connected to the dev wallet — just switch the active account.\n\t\t\tdAppKit.switchAccount({ account });\n\t\t\treturn;\n\t\t}\n\t\tawait dAppKit.connectWallet({ wallet, account });\n\t};\n\n\t// Network switching rides the SAME bridge as account switching: a test\n\t// calls the published slot, which drives dApp Kit's public `switchNetwork`.\n\t// The dev wallet (a wallet-standard wallet registered once) stays mounted\n\t// across the switch — only the active network/client changes.\n\tconst switchNetwork = (network: string): void => {\n\t\tdAppKit.switchNetwork(network);\n\t};\n\tconst currentNetwork = (): string => dAppKit.stores.$currentNetwork.get();\n\n\t(globalThis as { [DAPP_KIT_SLOT_KEY]?: DAppKitSlot }).__devstackDAppKit__ = {\n\t\tselectAccount,\n\t\tswitchNetwork,\n\t\tcurrentNetwork,\n\t};\n}\n"],"mappings":";;;;;AA+DA,MAAM,qBAAqB;AAC3B,MAAM,kBAAkB;AAExB,MAAM,SAAS,OAA8B,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;AAsB7F,SAAgB,0BACf,SACO;CACP,MAAM,yBAAyB,OAC9B,gBACmE;EACnE,MAAM,WAAW,KAAK,IAAI,IAAI;EAC9B,SAAS;GACR,KAAK,MAAM,UAAU,QAAQ,OAAO,SAAS,IAAI,GAAG;IACnD,MAAM,UAAU,OAAO,SAAS,MAAM,cAAc,UAAU,UAAU,WAAW;IACnF,IAAI,YAAY,KAAA,GAAW,OAAO;KAAE;KAAQ;IAAQ;GACrD;GACA,IAAI,KAAK,IAAI,KAAK,UAAU,OAAO;GACnC,MAAM,MAAM,eAAe;EAC5B;CACD;CAEA,MAAM,gBAAgB,OAAO,gBAAuC;EACnE,MAAM,QAAQ,MAAM,uBAAuB,WAAW;EACtD,IAAI,UAAU,MACb,MAAM,IAAI,MACT,cAAc,YAAY,sCAAsC,YAAY,sBACtD,mBAAmB,oEAE1C;EAED,MAAM,EAAE,QAAQ,YAAY;EAC5B,MAAM,aAAa,QAAQ,OAAO,YAAY,IAAI;EAClD,IAAI,WAAW,eAAe,WAAW,QAAQ,SAAS,OAAO,MAAM;GAEtE,QAAQ,cAAc,EAAE,QAAQ,CAAC;GACjC;EACD;EACA,MAAM,QAAQ,cAAc;GAAE;GAAQ;EAAQ,CAAC;CAChD;CAMA,MAAM,iBAAiB,YAA0B;EAChD,QAAQ,cAAc,OAAO;CAC9B;CACA,MAAM,uBAA+B,QAAQ,OAAO,gBAAgB,IAAI;CAExE,WAAsD,sBAAsB;EAC3E;EACA;EACA;CACD;AACD"}
|
|
@@ -67,7 +67,7 @@ declare const PlaywrightWalletAdapterError_base: new <A extends Record<string, a
|
|
|
67
67
|
*/
|
|
68
68
|
declare class PlaywrightWalletAdapterError extends PlaywrightWalletAdapterError_base<{
|
|
69
69
|
readonly message: string;
|
|
70
|
-
readonly operation: 'sign-tx' | 'list-accounts' | 'switch-account' | 'fetch';
|
|
70
|
+
readonly operation: 'sign-tx' | 'list-accounts' | 'switch-account' | 'switch-network' | 'fetch';
|
|
71
71
|
readonly url?: string;
|
|
72
72
|
readonly status?: number;
|
|
73
73
|
readonly cause?: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/build-integrations/playwright/errors.ts"],"sourcesContent":["// Playwright build-integration errors.\n//\n// Architecture (distilled/23-build-integrations.md § Playwright):\n// every failure surface for the preset is a tagged error so the\n// config-load (synchronous, runs before any supervisor exists) can\n// raise a structured signal with an embedded recovery recipe rather\n// than an opaque downstream NPE.\n//\n// Scope discipline: this surface owns FOUR error tags. Anything that\n// would be a fifth tag is either upstream (manifest schema error from\n// the L0 substrate, which we re-raise unchanged) or a programmer\n// defect (Effect.die / throw — not part of this contract).\n//\n// The Playwright config-load is synchronous (Playwright's\n// `defineConfig` is sync), so these errors are constructed and\n// thrown rather than `Effect.fail`-ed. The surface mirrors the\n// `Data.TaggedError` shape so the tags are uniform across the\n// codebase, but the catch sites are `try` / `instanceof` in\n// Playwright host code.\n\nimport { Data } from 'effect';\n\n// -----------------------------------------------------------------------------\n// Discovery — manifest could not be located and no conventional URL\n// fallback exists for the requested endpoint.\n// -----------------------------------------------------------------------------\n\n/**\n * The manifest could not be located on disk (no stack-scoped file\n * along the walk-up path) AND the requested endpoint has no\n * conventional URL fallback in the endpoint declaration registry.\n *\n * Recovery: run `devstack up` once to materialize the manifest, OR\n * pass an explicit `baseURL` to the Playwright config helpers to\n * bypass the discovery path.\n */\nexport class PlaywrightManifestDiscoveryError extends Data.TaggedError(\n\t'PlaywrightManifestDiscoveryError',\n)<{\n\treadonly message: string;\n\treadonly searchedPaths: ReadonlyArray<string>;\n\treadonly endpointKey?: string;\n\treadonly recoveryHint: string;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Shape — manifest exists but failed to decode against the L0\n// envelope schema.\n// -----------------------------------------------------------------------------\n\n/**\n * The manifest file was located but failed to decode against\n * `ManifestEnvelopeSchema`. Wraps the substrate's `ManifestError`\n * (reason = `decode-failed` | `version-mismatch`) with a recovery\n * recipe targeted at the Playwright user (delete + re-run `devstack\n * up` rather than the engine-side recovery).\n */\nexport class PlaywrightManifestShapeError extends Data.TaggedError('PlaywrightManifestShapeError')<{\n\treadonly message: string;\n\treadonly manifestPath: string;\n\treadonly phase: 'shape' | 'parse' | 'version-mismatch';\n\treadonly recoveryHint: string;\n\treadonly cause?: unknown;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Endpoint lookup — manifest decoded fine but the requested endpoint\n// is absent from the flat lookup.\n// -----------------------------------------------------------------------------\n\n/**\n * The manifest decoded but the requested endpoint name/alias is not\n * present.\n * Typically caused by a typo in user config or by referencing an\n * endpoint owned by a plugin not present in the resolved stack.\n */\nexport class PlaywrightEndpointNotFoundError extends Data.TaggedError(\n\t'PlaywrightEndpointNotFoundError',\n)<{\n\treadonly message: string;\n\treadonly endpointKey: string;\n\treadonly endpointName?: string;\n\treadonly available: ReadonlyArray<string>;\n\treadonly manifestKeys?: ReadonlyArray<string>;\n\treadonly recoveryHint: string;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Wallet adapter — the in-spec wallet helper could not talk to the\n// dev wallet's HTTP API.\n// -----------------------------------------------------------------------------\n\n/**\n * The wallet-context test helper failed to reach the dev wallet's\n * HTTP API (the supervisor's wallet plugin exposes an endpoint that\n * tests POST to for sign-tx flows). Typical causes: supervisor not\n * up, wallet endpoint not yet registered in the manifest, network\n * partition between test runner and supervisor.\n *\n * This tag is throwable from in-spec helpers AND constructible by\n * `wallet-context.ts` callers; it carries enough detail to format a\n * Playwright-friendly assertion failure.\n */\nexport class PlaywrightWalletAdapterError extends Data.TaggedError('PlaywrightWalletAdapterError')<{\n\treadonly message: string;\n\treadonly operation: 'sign-tx' | 'list-accounts' | 'switch-account' | 'fetch';\n\treadonly url?: string;\n\treadonly status?: number;\n\treadonly cause?: unknown;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Union for catch sites that want to handle every surface tag.\n// -----------------------------------------------------------------------------\n\n/** Discriminated union of every error the Playwright preset emits. */\nexport type PlaywrightIntegrationError =\n\t| PlaywrightManifestDiscoveryError\n\t| PlaywrightManifestShapeError\n\t| PlaywrightEndpointNotFoundError\n\t| PlaywrightWalletAdapterError;\n"],"mappings":";;;;;;;;;;;AAoCA,IAAa,mCAAb,cAAsD,KAAK,YAC1D,kCACD,CAAC,CAKE,CAAC;;;;;;;;AAcJ,IAAa,+BAAb,cAAkD,KAAK,YAAY,8BAA8B,CAAC,CAM/F,CAAC;;;;;;;AAaJ,IAAa,kCAAb,cAAqD,KAAK,YACzD,iCACD,CAAC,CAOE,CAAC;;;;;;;;;;;;AAkBJ,IAAa,+BAAb,cAAkD,KAAK,YAAY,8BAA8B,CAAC,CAM/F,CAAC"}
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/build-integrations/playwright/errors.ts"],"sourcesContent":["// Playwright build-integration errors.\n//\n// Architecture (distilled/23-build-integrations.md § Playwright):\n// every failure surface for the preset is a tagged error so the\n// config-load (synchronous, runs before any supervisor exists) can\n// raise a structured signal with an embedded recovery recipe rather\n// than an opaque downstream NPE.\n//\n// Scope discipline: this surface owns FOUR error tags. Anything that\n// would be a fifth tag is either upstream (manifest schema error from\n// the L0 substrate, which we re-raise unchanged) or a programmer\n// defect (Effect.die / throw — not part of this contract).\n//\n// The Playwright config-load is synchronous (Playwright's\n// `defineConfig` is sync), so these errors are constructed and\n// thrown rather than `Effect.fail`-ed. The surface mirrors the\n// `Data.TaggedError` shape so the tags are uniform across the\n// codebase, but the catch sites are `try` / `instanceof` in\n// Playwright host code.\n\nimport { Data } from 'effect';\n\n// -----------------------------------------------------------------------------\n// Discovery — manifest could not be located and no conventional URL\n// fallback exists for the requested endpoint.\n// -----------------------------------------------------------------------------\n\n/**\n * The manifest could not be located on disk (no stack-scoped file\n * along the walk-up path) AND the requested endpoint has no\n * conventional URL fallback in the endpoint declaration registry.\n *\n * Recovery: run `devstack up` once to materialize the manifest, OR\n * pass an explicit `baseURL` to the Playwright config helpers to\n * bypass the discovery path.\n */\nexport class PlaywrightManifestDiscoveryError extends Data.TaggedError(\n\t'PlaywrightManifestDiscoveryError',\n)<{\n\treadonly message: string;\n\treadonly searchedPaths: ReadonlyArray<string>;\n\treadonly endpointKey?: string;\n\treadonly recoveryHint: string;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Shape — manifest exists but failed to decode against the L0\n// envelope schema.\n// -----------------------------------------------------------------------------\n\n/**\n * The manifest file was located but failed to decode against\n * `ManifestEnvelopeSchema`. Wraps the substrate's `ManifestError`\n * (reason = `decode-failed` | `version-mismatch`) with a recovery\n * recipe targeted at the Playwright user (delete + re-run `devstack\n * up` rather than the engine-side recovery).\n */\nexport class PlaywrightManifestShapeError extends Data.TaggedError('PlaywrightManifestShapeError')<{\n\treadonly message: string;\n\treadonly manifestPath: string;\n\treadonly phase: 'shape' | 'parse' | 'version-mismatch';\n\treadonly recoveryHint: string;\n\treadonly cause?: unknown;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Endpoint lookup — manifest decoded fine but the requested endpoint\n// is absent from the flat lookup.\n// -----------------------------------------------------------------------------\n\n/**\n * The manifest decoded but the requested endpoint name/alias is not\n * present.\n * Typically caused by a typo in user config or by referencing an\n * endpoint owned by a plugin not present in the resolved stack.\n */\nexport class PlaywrightEndpointNotFoundError extends Data.TaggedError(\n\t'PlaywrightEndpointNotFoundError',\n)<{\n\treadonly message: string;\n\treadonly endpointKey: string;\n\treadonly endpointName?: string;\n\treadonly available: ReadonlyArray<string>;\n\treadonly manifestKeys?: ReadonlyArray<string>;\n\treadonly recoveryHint: string;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Wallet adapter — the in-spec wallet helper could not talk to the\n// dev wallet's HTTP API.\n// -----------------------------------------------------------------------------\n\n/**\n * The wallet-context test helper failed to reach the dev wallet's\n * HTTP API (the supervisor's wallet plugin exposes an endpoint that\n * tests POST to for sign-tx flows). Typical causes: supervisor not\n * up, wallet endpoint not yet registered in the manifest, network\n * partition between test runner and supervisor.\n *\n * This tag is throwable from in-spec helpers AND constructible by\n * `wallet-context.ts` callers; it carries enough detail to format a\n * Playwright-friendly assertion failure.\n */\nexport class PlaywrightWalletAdapterError extends Data.TaggedError('PlaywrightWalletAdapterError')<{\n\treadonly message: string;\n\treadonly operation: 'sign-tx' | 'list-accounts' | 'switch-account' | 'switch-network' | 'fetch';\n\treadonly url?: string;\n\treadonly status?: number;\n\treadonly cause?: unknown;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Union for catch sites that want to handle every surface tag.\n// -----------------------------------------------------------------------------\n\n/** Discriminated union of every error the Playwright preset emits. */\nexport type PlaywrightIntegrationError =\n\t| PlaywrightManifestDiscoveryError\n\t| PlaywrightManifestShapeError\n\t| PlaywrightEndpointNotFoundError\n\t| PlaywrightWalletAdapterError;\n"],"mappings":";;;;;;;;;;;AAoCA,IAAa,mCAAb,cAAsD,KAAK,YAC1D,kCACD,CAAC,CAKE,CAAC;;;;;;;;AAcJ,IAAa,+BAAb,cAAkD,KAAK,YAAY,8BAA8B,CAAC,CAM/F,CAAC;;;;;;;AAaJ,IAAa,kCAAb,cAAqD,KAAK,YACzD,iCACD,CAAC,CAOE,CAAC;;;;;;;;;;;;AAkBJ,IAAa,+BAAb,cAAkD,KAAK,YAAY,8BAA8B,CAAC,CAM/F,CAAC"}
|
|
@@ -3,6 +3,6 @@ import { WALLET_ENDPOINT_KEY } from "../../contracts/wallet-protocol.mjs";
|
|
|
3
3
|
import { PLAYWRIGHT_ENV, ResolveStackContextOptions, ResolvedEndpoint, StackContext, conventionalUrlFor, discoverManifestPath, makeStackContext, readManifestSync, readStackContext, resolveEndpointUrl } from "./stack-context.mjs";
|
|
4
4
|
import { DefineGlobalSetupOptions, PlaywrightGlobalSetup, PlaywrightStackFixture, STACK_CONTEXT_SLOT, buildGlobalSetup, readStashedFixture } from "./global-setup.mjs";
|
|
5
5
|
import { DevstackPlaywrightBaseConfigOptions, DevstackPlaywrightEndpointOptions, DevstackPlaywrightProjectsOptions, DevstackPlaywrightUseOptions, PlaywrightBaseConfigShape, PlaywrightProjectShape, PlaywrightUseConfigShape, devstackPlaywrightBaseConfig, devstackPlaywrightProjects, devstackPlaywrightUse, resolveDevstackPlaywrightBaseURL } from "./config.mjs";
|
|
6
|
-
import { DAPP_KIT_SLOT, DevAccount, PlaywrightPageLike, SignTxRequest, SignTxResponse, WalletAdapter, WalletAdapterOptions, connectAs, createWalletAdapter, loadStackManifest, selectAccount } from "./wallet-context.mjs";
|
|
6
|
+
import { DAPP_KIT_SLOT, DevAccount, PlaywrightPageLike, SignTxRequest, SignTxResponse, WalletAdapter, WalletAdapterOptions, connectAs, createWalletAdapter, loadStackManifest, selectAccount, switchNetwork } from "./wallet-context.mjs";
|
|
7
7
|
import { PlaywrightEndpointNotFoundError, PlaywrightIntegrationError, PlaywrightManifestDiscoveryError, PlaywrightManifestShapeError, PlaywrightWalletAdapterError } from "./errors.mjs";
|
|
8
|
-
export { DAPP_KIT_SLOT, type DefineGlobalSetupOptions, type DevAccount, type DevstackPlaywrightBaseConfigOptions, type DevstackPlaywrightEndpointOptions, type DevstackPlaywrightProjectsOptions, type DevstackPlaywrightUseOptions, PLAYWRIGHT_ENV, PLAYWRIGHT_STACK_CONTEXT_SLOT_KEY, type PlaywrightBaseConfigShape, PlaywrightEndpointNotFoundError, type PlaywrightGlobalSetup, type PlaywrightIntegrationError, PlaywrightManifestDiscoveryError, PlaywrightManifestShapeError, type PlaywrightPageLike, type PlaywrightProjectShape, type PlaywrightStackFixture, type PlaywrightUseConfigShape, PlaywrightWalletAdapterError, type ResolveStackContextOptions, type ResolvedEndpoint, STACK_CONTEXT_SLOT, type SignTxRequest, type SignTxResponse, type StackContext, WALLET_ENDPOINT_KEY, type WalletAdapter, type WalletAdapterOptions, buildGlobalSetup, connectAs, conventionalUrlFor, createWalletAdapter, devstackPlaywrightBaseConfig, devstackPlaywrightProjects, devstackPlaywrightUse, discoverManifestPath, loadStackManifest, makeStackContext, readManifestSync, readStackContext, readStashedFixture, resolveDevstackPlaywrightBaseURL, resolveEndpointUrl, selectAccount };
|
|
8
|
+
export { DAPP_KIT_SLOT, type DefineGlobalSetupOptions, type DevAccount, type DevstackPlaywrightBaseConfigOptions, type DevstackPlaywrightEndpointOptions, type DevstackPlaywrightProjectsOptions, type DevstackPlaywrightUseOptions, PLAYWRIGHT_ENV, PLAYWRIGHT_STACK_CONTEXT_SLOT_KEY, type PlaywrightBaseConfigShape, PlaywrightEndpointNotFoundError, type PlaywrightGlobalSetup, type PlaywrightIntegrationError, PlaywrightManifestDiscoveryError, PlaywrightManifestShapeError, type PlaywrightPageLike, type PlaywrightProjectShape, type PlaywrightStackFixture, type PlaywrightUseConfigShape, PlaywrightWalletAdapterError, type ResolveStackContextOptions, type ResolvedEndpoint, STACK_CONTEXT_SLOT, type SignTxRequest, type SignTxResponse, type StackContext, WALLET_ENDPOINT_KEY, type WalletAdapter, type WalletAdapterOptions, buildGlobalSetup, connectAs, conventionalUrlFor, createWalletAdapter, devstackPlaywrightBaseConfig, devstackPlaywrightProjects, devstackPlaywrightUse, discoverManifestPath, loadStackManifest, makeStackContext, readManifestSync, readStackContext, readStashedFixture, resolveDevstackPlaywrightBaseURL, resolveEndpointUrl, selectAccount, switchNetwork };
|
|
@@ -4,5 +4,5 @@ import { PlaywrightEndpointNotFoundError, PlaywrightManifestDiscoveryError, Play
|
|
|
4
4
|
import { PLAYWRIGHT_ENV, conventionalUrlFor, discoverManifestPath, makeStackContext, readManifestSync, readStackContext, resolveEndpointUrl } from "./stack-context.mjs";
|
|
5
5
|
import { devstackPlaywrightBaseConfig, devstackPlaywrightProjects, devstackPlaywrightUse, resolveDevstackPlaywrightBaseURL } from "./config.mjs";
|
|
6
6
|
import { STACK_CONTEXT_SLOT, buildGlobalSetup, readStashedFixture } from "./global-setup.mjs";
|
|
7
|
-
import { DAPP_KIT_SLOT, connectAs, createWalletAdapter, loadStackManifest, selectAccount } from "./wallet-context.mjs";
|
|
8
|
-
export { DAPP_KIT_SLOT, PLAYWRIGHT_ENV, PLAYWRIGHT_STACK_CONTEXT_SLOT_KEY, PlaywrightEndpointNotFoundError, PlaywrightManifestDiscoveryError, PlaywrightManifestShapeError, PlaywrightWalletAdapterError, STACK_CONTEXT_SLOT, WALLET_ENDPOINT_KEY, buildGlobalSetup, connectAs, conventionalUrlFor, createWalletAdapter, devstackPlaywrightBaseConfig, devstackPlaywrightProjects, devstackPlaywrightUse, discoverManifestPath, loadStackManifest, makeStackContext, readManifestSync, readStackContext, readStashedFixture, resolveDevstackPlaywrightBaseURL, resolveEndpointUrl, selectAccount };
|
|
7
|
+
import { DAPP_KIT_SLOT, connectAs, createWalletAdapter, loadStackManifest, selectAccount, switchNetwork } from "./wallet-context.mjs";
|
|
8
|
+
export { DAPP_KIT_SLOT, PLAYWRIGHT_ENV, PLAYWRIGHT_STACK_CONTEXT_SLOT_KEY, PlaywrightEndpointNotFoundError, PlaywrightManifestDiscoveryError, PlaywrightManifestShapeError, PlaywrightWalletAdapterError, STACK_CONTEXT_SLOT, WALLET_ENDPOINT_KEY, buildGlobalSetup, connectAs, conventionalUrlFor, createWalletAdapter, devstackPlaywrightBaseConfig, devstackPlaywrightProjects, devstackPlaywrightUse, discoverManifestPath, loadStackManifest, makeStackContext, readManifestSync, readStackContext, readStashedFixture, resolveDevstackPlaywrightBaseURL, resolveEndpointUrl, selectAccount, switchNetwork };
|
|
@@ -77,8 +77,9 @@ declare const connectAs: (page: PlaywrightPageLike, accountName: string) => Prom
|
|
|
77
77
|
* faster + less flaky than clicking the wallet's UI.
|
|
78
78
|
*/
|
|
79
79
|
declare const selectAccount: (page: PlaywrightPageLike, accountName: string) => Promise<void>;
|
|
80
|
+
declare const switchNetwork: (page: PlaywrightPageLike, network: string) => Promise<void>;
|
|
80
81
|
/** Test-side accessor for the full manifest (already decoded). */
|
|
81
82
|
declare const loadStackManifest: (options?: ResolveStackContextOptions) => StackContext;
|
|
82
83
|
//#endregion
|
|
83
|
-
export { DAPP_KIT_SLOT, DevAccount, PlaywrightPageLike, SignTxRequest, SignTxResponse, WalletAdapter, WalletAdapterOptions, connectAs, createWalletAdapter, loadStackManifest, selectAccount };
|
|
84
|
+
export { DAPP_KIT_SLOT, DevAccount, PlaywrightPageLike, SignTxRequest, SignTxResponse, WalletAdapter, WalletAdapterOptions, connectAs, createWalletAdapter, loadStackManifest, selectAccount, switchNetwork };
|
|
84
85
|
//# sourceMappingURL=wallet-context.d.mts.map
|