@graphrefly/graphrefly 0.47.1 → 0.47.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/dist/base/composition/index.cjs +24 -16
  2. package/dist/base/composition/index.cjs.map +1 -1
  3. package/dist/base/composition/index.js +6 -6
  4. package/dist/base/index.cjs +142 -86
  5. package/dist/base/index.cjs.map +1 -1
  6. package/dist/base/index.js +11 -11
  7. package/dist/base/io/index.cjs +114 -68
  8. package/dist/base/io/index.cjs.map +1 -1
  9. package/dist/base/io/index.js +5 -5
  10. package/dist/base/sources/browser/index.cjs +13 -9
  11. package/dist/base/sources/browser/index.cjs.map +1 -1
  12. package/dist/base/sources/browser/index.js +13 -9
  13. package/dist/base/sources/browser/index.js.map +1 -1
  14. package/dist/base/sources/event/index.cjs +1 -1
  15. package/dist/base/sources/event/index.cjs.map +1 -1
  16. package/dist/base/sources/event/index.js +1 -1
  17. package/dist/base/sources/index.cjs +21 -13
  18. package/dist/base/sources/index.cjs.map +1 -1
  19. package/dist/base/sources/index.js +3 -3
  20. package/dist/base/sources/node/index.cjs +43 -37
  21. package/dist/base/sources/node/index.cjs.map +1 -1
  22. package/dist/base/sources/node/index.js +43 -37
  23. package/dist/base/sources/node/index.js.map +1 -1
  24. package/dist/{chunk-VLAGJZSL.js → chunk-3O3NKZJW.js} +2 -2
  25. package/dist/{chunk-YJ4U2D2C.js → chunk-446I4EGD.js} +9 -7
  26. package/dist/chunk-446I4EGD.js.map +1 -0
  27. package/dist/{chunk-DKNHAICT.js → chunk-5GVURVIG.js} +14 -8
  28. package/dist/chunk-5GVURVIG.js.map +1 -0
  29. package/dist/{chunk-2OB3CEJS.js → chunk-6MRSX3YK.js} +2 -2
  30. package/dist/{chunk-EVYY4X5A.js → chunk-6ZLCPUXS.js} +2 -2
  31. package/dist/{chunk-U225SKB4.js → chunk-7AVQIGF6.js} +61 -10
  32. package/dist/chunk-7AVQIGF6.js.map +1 -0
  33. package/dist/{chunk-7EGRP2VX.js → chunk-7BULJTL6.js} +2 -2
  34. package/dist/{chunk-7EGRP2VX.js.map → chunk-7BULJTL6.js.map} +1 -1
  35. package/dist/{chunk-7ADWWI2T.js → chunk-DDTS7F5O.js} +6 -4
  36. package/dist/chunk-DDTS7F5O.js.map +1 -0
  37. package/dist/{chunk-OCUDSN63.js → chunk-EL5VHUGK.js} +79 -47
  38. package/dist/chunk-EL5VHUGK.js.map +1 -0
  39. package/dist/{chunk-4GYMCUDZ.js → chunk-EP4WVQLX.js} +5 -5
  40. package/dist/{chunk-SOOKUYVM.js → chunk-F7EKHR32.js} +13 -9
  41. package/dist/chunk-F7EKHR32.js.map +1 -0
  42. package/dist/{chunk-RGMTUZCL.js → chunk-FQSQONOU.js} +3 -3
  43. package/dist/{chunk-RAGGHLCV.js → chunk-GUNIRPEJ.js} +8 -6
  44. package/dist/{chunk-RAGGHLCV.js.map → chunk-GUNIRPEJ.js.map} +1 -1
  45. package/dist/{chunk-BU3SEFA5.js → chunk-IOJDYUA7.js} +2 -2
  46. package/dist/{chunk-Y52CS6YA.js → chunk-JA67ZQG2.js} +2 -2
  47. package/dist/{chunk-Y52CS6YA.js.map → chunk-JA67ZQG2.js.map} +1 -1
  48. package/dist/{chunk-DM4OMPWK.js → chunk-KNU73RZW.js} +2 -2
  49. package/dist/{chunk-K7PDZYQE.js → chunk-KRFGO5QH.js} +18 -14
  50. package/dist/{chunk-K7PDZYQE.js.map → chunk-KRFGO5QH.js.map} +1 -1
  51. package/dist/{chunk-Z4YXAUDN.js → chunk-KUFXLAEY.js} +11 -7
  52. package/dist/{chunk-Z4YXAUDN.js.map → chunk-KUFXLAEY.js.map} +1 -1
  53. package/dist/{chunk-YXCPV26R.js → chunk-MS3WPRJR.js} +37 -25
  54. package/dist/chunk-MS3WPRJR.js.map +1 -0
  55. package/dist/{chunk-CXANAIZU.js → chunk-N65E26UL.js} +3 -3
  56. package/dist/{chunk-O3MT7DYI.js → chunk-N6MNJNHB.js} +2 -2
  57. package/dist/{chunk-A7KV5UK4.js → chunk-OXD5LFQP.js} +2 -2
  58. package/dist/{chunk-V4Y3TM7U.js → chunk-PTWADEH3.js} +8 -6
  59. package/dist/chunk-PTWADEH3.js.map +1 -0
  60. package/dist/{chunk-IHTWQEDR.js → chunk-QFE5BQH7.js} +2 -2
  61. package/dist/{chunk-IHTWQEDR.js.map → chunk-QFE5BQH7.js.map} +1 -1
  62. package/dist/{chunk-J5WFUEO4.js → chunk-R6ZCSXKX.js} +3 -3
  63. package/dist/{chunk-PZWISPIQ.js → chunk-S7HN5FHL.js} +17 -11
  64. package/dist/chunk-S7HN5FHL.js.map +1 -0
  65. package/dist/{chunk-RJOG4IJU.js → chunk-T7SP3EYR.js} +18 -12
  66. package/dist/chunk-T7SP3EYR.js.map +1 -0
  67. package/dist/{chunk-4S53H2KR.js → chunk-VAZXUK6G.js} +2 -2
  68. package/dist/{chunk-IJRR6YAI.js → chunk-VLDRAMP7.js} +18 -12
  69. package/dist/chunk-VLDRAMP7.js.map +1 -0
  70. package/dist/{chunk-B4AKFXGE.js → chunk-VNXAF2KE.js} +3 -3
  71. package/dist/{chunk-MTTRCEJT.js → chunk-VP3TIUDF.js} +2 -2
  72. package/dist/{chunk-6XZYT4SW.js → chunk-WGDEBIP4.js} +5 -5
  73. package/dist/{chunk-E5OZPDIW.js → chunk-X7BA5PWG.js} +7 -5
  74. package/dist/chunk-X7BA5PWG.js.map +1 -0
  75. package/dist/compat/index.cjs +1 -1
  76. package/dist/compat/index.cjs.map +1 -1
  77. package/dist/compat/index.js +2 -2
  78. package/dist/compat/nestjs/index.cjs +1 -1
  79. package/dist/compat/nestjs/index.cjs.map +1 -1
  80. package/dist/compat/nestjs/index.js +2 -2
  81. package/dist/index.cjs +283 -142
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.js +32 -32
  84. package/dist/presets/ai/index.cjs +42 -26
  85. package/dist/presets/ai/index.cjs.map +1 -1
  86. package/dist/presets/ai/index.js +10 -10
  87. package/dist/presets/harness/index.cjs +53 -33
  88. package/dist/presets/harness/index.cjs.map +1 -1
  89. package/dist/presets/harness/index.js +21 -21
  90. package/dist/presets/index.cjs +76 -48
  91. package/dist/presets/index.cjs.map +1 -1
  92. package/dist/presets/index.js +24 -24
  93. package/dist/presets/resilience/index.cjs +35 -23
  94. package/dist/presets/resilience/index.cjs.map +1 -1
  95. package/dist/presets/resilience/index.js +5 -5
  96. package/dist/solutions/index.cjs +71 -45
  97. package/dist/solutions/index.cjs.map +1 -1
  98. package/dist/solutions/index.js +21 -21
  99. package/dist/{timeout-U5O4ESK3.js → timeout-BEABACRP.js} +2 -2
  100. package/dist/utils/ai/browser.cjs.map +1 -1
  101. package/dist/utils/ai/browser.js +9 -9
  102. package/dist/utils/ai/index.cjs +41 -25
  103. package/dist/utils/ai/index.cjs.map +1 -1
  104. package/dist/utils/ai/index.js +17 -17
  105. package/dist/utils/ai/node.js +3 -3
  106. package/dist/utils/domain-templates/index.cjs +1 -1
  107. package/dist/utils/domain-templates/index.cjs.map +1 -1
  108. package/dist/utils/domain-templates/index.js +2 -2
  109. package/dist/utils/graphspec/index.cjs +1 -1
  110. package/dist/utils/graphspec/index.cjs.map +1 -1
  111. package/dist/utils/graphspec/index.js +2 -2
  112. package/dist/utils/harness/index.cjs +16 -10
  113. package/dist/utils/harness/index.cjs.map +1 -1
  114. package/dist/utils/harness/index.js +1 -1
  115. package/dist/utils/index.cjs +138 -55
  116. package/dist/utils/index.cjs.map +1 -1
  117. package/dist/utils/index.js +21 -21
  118. package/dist/utils/memory/index.cjs +51 -0
  119. package/dist/utils/memory/index.cjs.map +1 -1
  120. package/dist/utils/memory/index.d.cts +58 -9
  121. package/dist/utils/memory/index.d.ts +58 -9
  122. package/dist/utils/memory/index.js +1 -1
  123. package/dist/utils/orchestration/index.cjs +5 -3
  124. package/dist/utils/orchestration/index.cjs.map +1 -1
  125. package/dist/utils/orchestration/index.js +1 -1
  126. package/dist/utils/process/index.js +2 -2
  127. package/dist/utils/reduction/index.cjs +1 -1
  128. package/dist/utils/reduction/index.cjs.map +1 -1
  129. package/dist/utils/reduction/index.js +1 -1
  130. package/dist/utils/resilience/index.cjs +35 -23
  131. package/dist/utils/resilience/index.cjs.map +1 -1
  132. package/dist/utils/resilience/index.js +4 -4
  133. package/dist/utils/surface/index.cjs +1 -1
  134. package/dist/utils/surface/index.cjs.map +1 -1
  135. package/dist/utils/surface/index.js +3 -3
  136. package/package.json +1 -1
  137. package/dist/chunk-7ADWWI2T.js.map +0 -1
  138. package/dist/chunk-DKNHAICT.js.map +0 -1
  139. package/dist/chunk-E5OZPDIW.js.map +0 -1
  140. package/dist/chunk-IJRR6YAI.js.map +0 -1
  141. package/dist/chunk-OCUDSN63.js.map +0 -1
  142. package/dist/chunk-PZWISPIQ.js.map +0 -1
  143. package/dist/chunk-RJOG4IJU.js.map +0 -1
  144. package/dist/chunk-SOOKUYVM.js.map +0 -1
  145. package/dist/chunk-U225SKB4.js.map +0 -1
  146. package/dist/chunk-V4Y3TM7U.js.map +0 -1
  147. package/dist/chunk-YJ4U2D2C.js.map +0 -1
  148. package/dist/chunk-YXCPV26R.js.map +0 -1
  149. /package/dist/{chunk-VLAGJZSL.js.map → chunk-3O3NKZJW.js.map} +0 -0
  150. /package/dist/{chunk-2OB3CEJS.js.map → chunk-6MRSX3YK.js.map} +0 -0
  151. /package/dist/{chunk-EVYY4X5A.js.map → chunk-6ZLCPUXS.js.map} +0 -0
  152. /package/dist/{chunk-4GYMCUDZ.js.map → chunk-EP4WVQLX.js.map} +0 -0
  153. /package/dist/{chunk-RGMTUZCL.js.map → chunk-FQSQONOU.js.map} +0 -0
  154. /package/dist/{chunk-BU3SEFA5.js.map → chunk-IOJDYUA7.js.map} +0 -0
  155. /package/dist/{chunk-DM4OMPWK.js.map → chunk-KNU73RZW.js.map} +0 -0
  156. /package/dist/{chunk-CXANAIZU.js.map → chunk-N65E26UL.js.map} +0 -0
  157. /package/dist/{chunk-O3MT7DYI.js.map → chunk-N6MNJNHB.js.map} +0 -0
  158. /package/dist/{chunk-A7KV5UK4.js.map → chunk-OXD5LFQP.js.map} +0 -0
  159. /package/dist/{chunk-J5WFUEO4.js.map → chunk-R6ZCSXKX.js.map} +0 -0
  160. /package/dist/{chunk-4S53H2KR.js.map → chunk-VAZXUK6G.js.map} +0 -0
  161. /package/dist/{chunk-B4AKFXGE.js.map → chunk-VNXAF2KE.js.map} +0 -0
  162. /package/dist/{chunk-MTTRCEJT.js.map → chunk-VP3TIUDF.js.map} +0 -0
  163. /package/dist/{chunk-6XZYT4SW.js.map → chunk-WGDEBIP4.js.map} +0 -0
  164. /package/dist/{timeout-U5O4ESK3.js.map → timeout-BEABACRP.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/base/sources/node/index.ts","../../../../src/base/sources/node/fs-root.ts","../../../../src/base/sources/node/git-hook.ts","../../../../src/base/sources/node/process.ts"],"sourcesContent":["/**\n * Node-only sources — fs, git, git-hook, process.\n *\n * All entries in this subpath may import node:* builtins.\n * Import via @graphrefly/graphrefly/base/sources/node.\n *\n * @module\n */\n\nexport * from \"./fs.js\";\nexport * from \"./git.js\";\nexport * from \"./git-hook.js\";\nexport * from \"./process.js\";\n","/**\n * Filesystem-watching source. Isolated from `./sources.ts` so bundlers\n * targeting the browser can import browser-safe sources (`fromTimer`,\n * `fromRaf`, etc.) without pulling in `node:fs`/`node:path`.\n */\n\nimport { existsSync, watch } from \"node:fs\";\nimport { resolve as resolvePath } from \"node:path\";\nimport {\n\tDATA,\n\tERROR,\n\ttype Message,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n\twallClockNs,\n} from \"@graphrefly/pure-ts/core\";\nimport { globToRegExp, matchesAnyPattern } from \"@graphrefly/pure-ts/extra\";\n\ntype ExtraOpts = Omit<NodeOptions<unknown>, \"describeKind\">;\n\nfunction sourceOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\nexport type FSEventType = \"change\" | \"rename\" | \"create\" | \"delete\";\nexport type FSEvent = {\n\ttype: FSEventType;\n\tpath: string;\n\troot: string;\n\trelative_path: string;\n\tsrc_path?: string;\n\tdest_path?: string;\n\ttimestamp_ns: number;\n};\n\nexport type FromFSWatchOptions = ExtraOpts & {\n\trecursive?: boolean;\n\tdebounce?: number;\n\tinclude?: string[];\n\texclude?: string[];\n};\n\n/**\n * Watches filesystem paths and emits debounced change events.\n *\n * Uses `fs.watch` only (no polling fallback). Teardown closes all watchers.\n *\n * @category extra\n */\nexport function fromFSWatch(paths: string | string[], opts?: FromFSWatchOptions): Node<FSEvent> {\n\tconst list = Array.isArray(paths) ? paths : [paths];\n\tif (list.length === 0) {\n\t\tthrow new RangeError(\"fromFSWatch expects at least one path\");\n\t}\n\tconst { recursive = true, debounce = 100, include, exclude, ...rest } = opts ?? {};\n\tconst includePatterns = include?.map(globToRegExp) ?? [];\n\tconst excludePatterns = (exclude ?? [\"**/node_modules/**\", \"**/.git/**\", \"**/dist/**\"]).map(\n\t\tglobToRegExp,\n\t);\n\treturn node<FSEvent>((_data, a) => {\n\t\tconst pending = new Map<string, FSEvent>();\n\t\tconst watchers: ReturnType<typeof watch>[] = [];\n\t\tlet stopped = false;\n\t\tlet terminalEmitted = false;\n\t\tlet generation = 0;\n\t\tconst closeWatchers = () => {\n\t\t\tfor (const watcher of watchers.splice(0)) watcher.close();\n\t\t};\n\t\tconst emitError = (err: unknown) => {\n\t\t\tif (terminalEmitted) return;\n\t\t\tterminalEmitted = true;\n\t\t\tstopped = true;\n\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t\tpending.clear();\n\t\t\tcloseWatchers();\n\t\t\ta.down([[ERROR, err]]);\n\t\t};\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst flush = (token: number) => {\n\t\t\ttimer = undefined;\n\t\t\tif (stopped || terminalEmitted) return;\n\t\t\tif (pending.size === 0) return;\n\t\t\tconst batchMessages: Message[] = [];\n\t\t\tfor (const evt of pending.values()) batchMessages.push([DATA, evt]);\n\t\t\tpending.clear();\n\t\t\tif (stopped || terminalEmitted || token !== generation) return;\n\t\t\ta.down(batchMessages);\n\t\t};\n\t\ttry {\n\t\t\tfor (const basePath of list) {\n\t\t\t\tconst watcher = watch(\n\t\t\t\t\tbasePath,\n\t\t\t\t\t{ recursive },\n\t\t\t\t\t(eventType: \"rename\" | \"change\", fileName: string | Buffer | null) => {\n\t\t\t\t\t\tif (stopped || terminalEmitted) return;\n\t\t\t\t\t\tif (fileName == null) return;\n\t\t\t\t\t\tconst rel = String(fileName).replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst abs = resolvePath(basePath, String(fileName));\n\t\t\t\t\t\tconst normalized = abs.replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst root = resolvePath(basePath).replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst relForMatch = rel.startsWith(\"./\") ? rel.slice(2) : rel;\n\t\t\t\t\t\tconst included =\n\t\t\t\t\t\t\tincludePatterns.length === 0 ||\n\t\t\t\t\t\t\tmatchesAnyPattern(normalized, includePatterns) ||\n\t\t\t\t\t\t\tmatchesAnyPattern(relForMatch, includePatterns);\n\t\t\t\t\t\tif (!included) return;\n\t\t\t\t\t\tconst excluded =\n\t\t\t\t\t\t\tmatchesAnyPattern(normalized, excludePatterns) ||\n\t\t\t\t\t\t\tmatchesAnyPattern(relForMatch, excludePatterns);\n\t\t\t\t\t\tif (excluded) return;\n\t\t\t\t\t\tlet kind: FSEventType = \"change\";\n\t\t\t\t\t\tif (eventType === \"rename\") {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tkind = existsSync(normalized) ? \"create\" : \"delete\";\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\tkind = \"rename\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpending.set(normalized, {\n\t\t\t\t\t\t\ttype: kind,\n\t\t\t\t\t\t\tpath: normalized,\n\t\t\t\t\t\t\troot,\n\t\t\t\t\t\t\trelative_path: relForMatch,\n\t\t\t\t\t\t\ttimestamp_ns: wallClockNs(),\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\t\t\t\tconst token = generation;\n\t\t\t\t\t\ttimer = setTimeout(() => flush(token), debounce);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\twatcher.on(\"error\", (err) => emitError(err));\n\t\t\t\twatchers.push(watcher);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\temitError(err);\n\t\t}\n\t\treturn () => {\n\t\t\tstopped = true;\n\t\t\tgeneration += 1;\n\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t\tcloseWatchers();\n\t\t\tpending.clear();\n\t\t};\n\t}, sourceOpts(rest));\n}\n","/**\n * Git hook source — Node-only reactive source that polls a repository's HEAD\n * and emits structured `GitEvent`s on every new commit.\n *\n * Isolated from `./adapters.ts` so that the universal `extra/index` barrel\n * stays browser-safe. Access via `@graphrefly/graphrefly/extra/node`, which\n * re-exports this module.\n *\n * @module\n */\n\nimport { ERROR, type Node, type NodeOptions, node, wallClockNs } from \"@graphrefly/pure-ts/core\";\nimport { fromTimer, globToRegExp, matchesAnyPattern, switchMap } from \"@graphrefly/pure-ts/extra\";\n\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\n\n/** Git hook type for {@link fromGitHook}. */\nexport type GitHookType = \"post-commit\" | \"post-merge\" | \"post-checkout\" | \"post-rewrite\";\n\n/** Structured git event emitted by {@link fromGitHook}. */\nexport type GitEvent = {\n\thook: GitHookType;\n\tcommit: string;\n\tfiles: string[];\n\tmessage: string;\n\tauthor: string;\n\ttimestamp_ns: number;\n};\n\n/** Options for {@link fromGitHook}. */\nexport type FromGitHookOptions = ExtraOpts & {\n\tpollMs?: number;\n\tinclude?: string[];\n\texclude?: string[];\n\t/**\n\t * Maximum consecutive poll errors before terminating the source. Prevents\n\t * error storms when the repository is unavailable (e.g. deleted, corrupt,\n\t * permissions lost). Default: `1` (terminate on first error — preserves\n\t * pre-switchMap back-compat). Raise it (or set `Infinity`) to keep\n\t * retrying indefinitely (legacy behavior).\n\t */\n\tmaxConsecutiveErrors?: number;\n};\n\n/**\n * Git change detection as a reactive source.\n *\n * @category extra\n */\nexport function fromGitHook(repoPath: string, opts?: FromGitHookOptions): Node<GitEvent> {\n\tconst { pollMs = 5000, include, exclude, maxConsecutiveErrors = 1 } = opts ?? {};\n\tconst includePatterns = include?.map(globToRegExp) ?? [];\n\tconst excludePatterns = exclude?.map(globToRegExp) ?? [];\n\tconst { execFileSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\n\tconst gitQuery = (args: string[]): string =>\n\t\texecFileSync(\"git\", args, { cwd: repoPath, encoding: \"utf-8\" }).trim();\n\n\t// Shared across ticks: the previous HEAD we committed to. Undefined on the\n\t// very first poll (we record the initial HEAD without emitting).\n\tlet lastSeen: string | undefined;\n\t// Circuit breaker: consecutive error count. Resets on any successful poll.\n\tlet consecutiveErrors = 0;\n\n\t// `fromTimer | switchMap(sync-git-diff)` — ticks drive the poll, switchMap\n\t// cancels any in-flight inner on next tick. First tick at t=0 records the\n\t// baseline HEAD silently; subsequent ticks emit `GitEvent` on HEAD change.\n\treturn switchMap(fromTimer(0, { period: pollMs }), () =>\n\t\tnode<GitEvent>((_data, a) => {\n\t\t\ttry {\n\t\t\t\tconst head = gitQuery([\"rev-parse\", \"HEAD\"]);\n\t\t\t\tif (!head) {\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn () => {};\n\t\t\t\t}\n\t\t\t\tif (lastSeen === undefined) {\n\t\t\t\t\t// First poll: record baseline; stay idle until next tick\n\t\t\t\t\t// disposes this inner.\n\t\t\t\t\tlastSeen = head;\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn () => {};\n\t\t\t\t}\n\t\t\t\tif (head === lastSeen) {\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn () => {};\n\t\t\t\t}\n\t\t\t\tlet files = gitQuery([\"diff\", \"--name-only\", `${lastSeen}..${head}`])\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.filter(Boolean);\n\t\t\t\tif (includePatterns.length > 0) {\n\t\t\t\t\tfiles = files.filter((f) => matchesAnyPattern(f, includePatterns));\n\t\t\t\t}\n\t\t\t\tif (excludePatterns.length > 0) {\n\t\t\t\t\tfiles = files.filter((f) => !matchesAnyPattern(f, excludePatterns));\n\t\t\t\t}\n\t\t\t\tconst message = gitQuery([\"log\", \"-1\", \"--format=%s\", head]);\n\t\t\t\tconst author = gitQuery([\"log\", \"-1\", \"--format=%an\", head]);\n\t\t\t\ta.emit({\n\t\t\t\t\thook: \"post-commit\" as GitHookType,\n\t\t\t\t\tcommit: head,\n\t\t\t\t\tfiles,\n\t\t\t\t\tmessage,\n\t\t\t\t\tauthor,\n\t\t\t\t\ttimestamp_ns: wallClockNs(),\n\t\t\t\t});\n\t\t\t\tlastSeen = head;\n\t\t\t\tconsecutiveErrors = 0;\n\t\t\t} catch (err) {\n\t\t\t\tconsecutiveErrors += 1;\n\t\t\t\tif (consecutiveErrors >= maxConsecutiveErrors) {\n\t\t\t\t\ta.down([[ERROR, err]]);\n\t\t\t\t}\n\t\t\t\t// else: transient error — next tick will retry; don't spam ERROR.\n\t\t\t}\n\t\t\treturn () => {};\n\t\t}),\n\t);\n}\n","/**\n * Child-process reactive sources — Node-only.\n *\n * Isolated from `./sources.ts` so bundlers targeting the browser can import\n * browser-safe sources without pulling in `node:child_process`.\n *\n * Access via `@graphrefly/graphrefly/extra/node`, which re-exports this module.\n *\n * @module\n */\n\nimport { type SpawnOptions, spawn } from \"node:child_process\";\nimport { COMPLETE, DATA, ERROR, type Messages, type Node, node } from \"@graphrefly/pure-ts/core\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * SpawnEvent — discriminated stream emitted by {@link fromSpawn}.\n *\n * @category extra\n */\nexport type SpawnEvent =\n\t| { kind: \"stdout\"; chunk: Buffer }\n\t| { kind: \"stderr\"; chunk: Buffer }\n\t| { kind: \"exit\"; code: number | null; signal: NodeJS.Signals | null };\n\n/** Options for {@link fromSpawn}. Mirrors `child_process.SpawnOptions`. */\nexport interface FromSpawnOptions {\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\tshell?: boolean | string;\n\t/**\n\t * Optional caller-owned AbortSignal. When fired, the subprocess is sent\n\t * SIGTERM (per `child_process.spawn` signal semantics). The producer's own\n\t * teardown also sends SIGTERM regardless of caller signal — so `switchMap`\n\t * supersede in `actuatorExecutor` cancels in-flight subprocesses without\n\t * the caller wiring extra signals.\n\t */\n\tsignal?: AbortSignal;\n\t/** Extra args forwarded to spawn — e.g. stdio configuration. */\n\tstdio?: \"pipe\" | readonly (\"pipe\" | \"ignore\" | \"inherit\")[];\n}\n\n// ---------------------------------------------------------------------------\n// fromSpawn\n// ---------------------------------------------------------------------------\n\n/**\n * Spawn `cmd args` as a child process and stream stdout/stderr/exit as a\n * single discriminated `SpawnEvent` stream.\n *\n * Lifecycle:\n * - Stdout/stderr chunks emit as `DATA { kind: \"stdout\"|\"stderr\", chunk }`.\n * - Process exit emits one final `DATA { kind: \"exit\", code, signal }` then\n * `COMPLETE`.\n * - Spawn-error (ENOENT, EPERM, …) emits `ERROR`.\n * - Producer teardown sends `SIGTERM` to the subprocess if it is still alive.\n *\n * **Multicast semantics:** `fromSpawn` returns a node backed by a single\n * `producer` activation — the subprocess is spawned once when the first\n * subscriber connects, and all subsequent subscribers share the same event\n * stream. Unsubscribing the last subscriber tears down the subprocess.\n *\n * @example\n * ```ts\n * import { fromSpawn } from \"@graphrefly/graphrefly/extra/node\";\n *\n * const stream = fromSpawn(\"git\", [\"log\", \"--oneline\"]);\n * stream.subscribe((msgs) => {\n * for (const [type, value] of msgs) {\n * if (type === DATA) console.log(value);\n * }\n * });\n * ```\n *\n * @category extra\n */\nexport function fromSpawn(\n\tcmd: string,\n\targs: readonly string[],\n\topts?: FromSpawnOptions,\n): Node<SpawnEvent> {\n\treturn node<SpawnEvent>(\n\t\t(_data, actions) => {\n\t\t\tconst child = spawn(cmd, args as string[], {\n\t\t\t\tcwd: opts?.cwd,\n\t\t\t\tenv: opts?.env,\n\t\t\t\tshell: opts?.shell,\n\t\t\t\tsignal: opts?.signal,\n\t\t\t\tstdio: (opts?.stdio as SpawnOptions[\"stdio\"]) ?? \"pipe\",\n\t\t\t});\n\n\t\t\tlet alive = true;\n\t\t\tlet exitInfo: { code: number | null; signal: NodeJS.Signals | null } | null = null;\n\n\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tactions.down([[DATA, { kind: \"stdout\", chunk }]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tactions.down([[DATA, { kind: \"stderr\", chunk }]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tactions.down([[ERROR, err]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"exit\", (code, signal) => {\n\t\t\t\t// Capture exit info, but defer terminal emission to \"close\" — by which\n\t\t\t\t// time all stdout/stderr \"data\" events have been delivered.\n\t\t\t\tif (exitInfo == null) exitInfo = { code, signal: signal as NodeJS.Signals | null };\n\t\t\t});\n\n\t\t\tchild.on(\"close\", () => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tconst info = exitInfo ?? { code: null, signal: null };\n\t\t\t\tactions.down([\n\t\t\t\t\t[DATA, { kind: \"exit\", code: info.code, signal: info.signal }],\n\t\t\t\t\t[COMPLETE],\n\t\t\t\t] satisfies Messages);\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tif (alive) {\n\t\t\t\t\talive = false;\n\t\t\t\t\tchild.stdout?.removeAllListeners();\n\t\t\t\t\tchild.stderr?.removeAllListeners();\n\t\t\t\t\tchild.removeAllListeners(\"error\");\n\t\t\t\t\tchild.removeAllListeners(\"exit\");\n\t\t\t\t\tchild.removeAllListeners(\"close\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// already dead — ignore\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\t{ name: \"from_spawn\" },\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// runProcess\n// ---------------------------------------------------------------------------\n\n/**\n * Run `cmd args` to completion and emit one DATA with aggregated output.\n *\n * Convenience over {@link fromSpawn} for the \"wait for the process to finish,\n * capture stdout/stderr as strings, get exit code\" case.\n *\n * Aggregation policy: stdout and stderr are concatenated as `Buffer`s and\n * decoded as utf-8 once at exit, so multi-byte sequences split across chunks\n * are handled correctly. Actuators that need byte-exact stdout should use\n * `fromSpawn` directly.\n *\n * @example\n * ```ts\n * import { runProcess } from \"@graphrefly/graphrefly/extra/node\";\n *\n * const result = runProcess(\"git\", [\"rev-parse\", \"HEAD\"]);\n * result.subscribe((msgs) => {\n * for (const [type, value] of msgs) {\n * if (type === DATA) console.log(value.stdout.trim());\n * }\n * });\n * ```\n *\n * @category extra\n */\nexport function runProcess(\n\tcmd: string,\n\targs: readonly string[],\n\topts?: FromSpawnOptions,\n): Node<{\n\tstdout: string;\n\tstderr: string;\n\texitCode: number | null;\n\tsignal: NodeJS.Signals | null;\n}> {\n\ttype Result = {\n\t\tstdout: string;\n\t\tstderr: string;\n\t\texitCode: number | null;\n\t\tsignal: NodeJS.Signals | null;\n\t};\n\treturn node<Result>(\n\t\t(_data, actions) => {\n\t\t\tconst child = spawn(cmd, args as string[], {\n\t\t\t\tcwd: opts?.cwd,\n\t\t\t\tenv: opts?.env,\n\t\t\t\tshell: opts?.shell,\n\t\t\t\tsignal: opts?.signal,\n\t\t\t\tstdio: (opts?.stdio as SpawnOptions[\"stdio\"]) ?? \"pipe\",\n\t\t\t});\n\n\t\t\tlet alive = true;\n\t\t\tconst stdoutChunks: Buffer[] = [];\n\t\t\tconst stderrChunks: Buffer[] = [];\n\t\t\tlet exitInfo: { code: number | null; signal: NodeJS.Signals | null } | null = null;\n\n\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tstdoutChunks.push(chunk);\n\t\t\t});\n\n\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tstderrChunks.push(chunk);\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tactions.down([[ERROR, err]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"exit\", (code, signal) => {\n\t\t\t\t// Capture exit info, but defer terminal emission to \"close\" — by which\n\t\t\t\t// time all stdout/stderr \"data\" events have been delivered.\n\t\t\t\tif (exitInfo == null) exitInfo = { code, signal: signal as NodeJS.Signals | null };\n\t\t\t});\n\n\t\t\tchild.on(\"close\", () => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tconst info = exitInfo ?? { code: null, signal: null };\n\t\t\t\tconst stdout = Buffer.concat(stdoutChunks).toString(\"utf8\");\n\t\t\t\tconst stderr = Buffer.concat(stderrChunks).toString(\"utf8\");\n\t\t\t\tactions.down([\n\t\t\t\t\t[\n\t\t\t\t\t\tDATA,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstdout,\n\t\t\t\t\t\t\tstderr,\n\t\t\t\t\t\t\texitCode: info.code,\n\t\t\t\t\t\t\tsignal: info.signal,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\t[COMPLETE],\n\t\t\t\t] satisfies Messages);\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tif (alive) {\n\t\t\t\t\talive = false;\n\t\t\t\t\tchild.stdout?.removeAllListeners();\n\t\t\t\t\tchild.stderr?.removeAllListeners();\n\t\t\t\t\tchild.removeAllListeners(\"error\");\n\t\t\t\t\tchild.removeAllListeners(\"exit\");\n\t\t\t\t\tchild.removeAllListeners(\"close\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// already dead — ignore\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\t{ name: \"run_process\" },\n\t);\n}\n"],"mappings":"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","names":["resolvePath","import_core","import_extra","import_core"]}
1
+ {"version":3,"sources":["../../../../src/base/sources/node/index.ts","../../../../src/base/sources/node/fs-root.ts","../../../../src/base/sources/node/git-hook.ts","../../../../src/base/sources/node/process.ts"],"sourcesContent":["/**\n * Node-only sources — fs, git, git-hook, process.\n *\n * All entries in this subpath may import node:* builtins.\n * Import via @graphrefly/graphrefly/base/sources/node.\n *\n * @module\n */\n\nexport * from \"./fs.js\";\nexport * from \"./git.js\";\nexport * from \"./git-hook.js\";\nexport * from \"./process.js\";\n","/**\n * Filesystem-watching source. Isolated from `./sources.ts` so bundlers\n * targeting the browser can import browser-safe sources (`fromTimer`,\n * `fromRaf`, etc.) without pulling in `node:fs`/`node:path`.\n */\n\nimport { existsSync, watch } from \"node:fs\";\nimport { resolve as resolvePath } from \"node:path\";\nimport {\n\tDATA,\n\tERROR,\n\ttype Message,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n\twallClockNs,\n} from \"@graphrefly/pure-ts/core\";\nimport { globToRegExp, matchesAnyPattern } from \"@graphrefly/pure-ts/extra\";\n\ntype ExtraOpts = Omit<NodeOptions<unknown>, \"describeKind\">;\n\nfunction sourceOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\nexport type FSEventType = \"change\" | \"rename\" | \"create\" | \"delete\";\nexport type FSEvent = {\n\ttype: FSEventType;\n\tpath: string;\n\troot: string;\n\trelative_path: string;\n\tsrc_path?: string;\n\tdest_path?: string;\n\ttimestamp_ns: number;\n};\n\nexport type FromFSWatchOptions = ExtraOpts & {\n\trecursive?: boolean;\n\tdebounce?: number;\n\tinclude?: string[];\n\texclude?: string[];\n};\n\n/**\n * Watches filesystem paths and emits debounced change events.\n *\n * Uses `fs.watch` only (no polling fallback). Teardown closes all watchers.\n *\n * @category extra\n */\nexport function fromFSWatch(paths: string | string[], opts?: FromFSWatchOptions): Node<FSEvent> {\n\tconst list = Array.isArray(paths) ? paths : [paths];\n\tif (list.length === 0) {\n\t\tthrow new RangeError(\"fromFSWatch expects at least one path\");\n\t}\n\tconst { recursive = true, debounce = 100, include, exclude, ...rest } = opts ?? {};\n\tconst includePatterns = include?.map(globToRegExp) ?? [];\n\tconst excludePatterns = (exclude ?? [\"**/node_modules/**\", \"**/.git/**\", \"**/dist/**\"]).map(\n\t\tglobToRegExp,\n\t);\n\treturn node<FSEvent>((_data, a) => {\n\t\tconst pending = new Map<string, FSEvent>();\n\t\tconst watchers: ReturnType<typeof watch>[] = [];\n\t\tlet stopped = false;\n\t\tlet terminalEmitted = false;\n\t\tlet generation = 0;\n\t\tconst closeWatchers = () => {\n\t\t\tfor (const watcher of watchers.splice(0)) watcher.close();\n\t\t};\n\t\tconst emitError = (err: unknown) => {\n\t\t\tif (terminalEmitted) return;\n\t\t\tterminalEmitted = true;\n\t\t\tstopped = true;\n\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t\tpending.clear();\n\t\t\tcloseWatchers();\n\t\t\ta.down([[ERROR, err]]);\n\t\t};\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst flush = (token: number) => {\n\t\t\ttimer = undefined;\n\t\t\tif (stopped || terminalEmitted) return;\n\t\t\tif (pending.size === 0) return;\n\t\t\tconst batchMessages: Message[] = [];\n\t\t\tfor (const evt of pending.values()) batchMessages.push([DATA, evt]);\n\t\t\tpending.clear();\n\t\t\tif (stopped || terminalEmitted || token !== generation) return;\n\t\t\ta.down(batchMessages);\n\t\t};\n\t\ttry {\n\t\t\tfor (const basePath of list) {\n\t\t\t\tconst watcher = watch(\n\t\t\t\t\tbasePath,\n\t\t\t\t\t{ recursive },\n\t\t\t\t\t(eventType: \"rename\" | \"change\", fileName: string | Buffer | null) => {\n\t\t\t\t\t\tif (stopped || terminalEmitted) return;\n\t\t\t\t\t\tif (fileName == null) return;\n\t\t\t\t\t\tconst rel = String(fileName).replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst abs = resolvePath(basePath, String(fileName));\n\t\t\t\t\t\tconst normalized = abs.replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst root = resolvePath(basePath).replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst relForMatch = rel.startsWith(\"./\") ? rel.slice(2) : rel;\n\t\t\t\t\t\tconst included =\n\t\t\t\t\t\t\tincludePatterns.length === 0 ||\n\t\t\t\t\t\t\tmatchesAnyPattern(normalized, includePatterns) ||\n\t\t\t\t\t\t\tmatchesAnyPattern(relForMatch, includePatterns);\n\t\t\t\t\t\tif (!included) return;\n\t\t\t\t\t\tconst excluded =\n\t\t\t\t\t\t\tmatchesAnyPattern(normalized, excludePatterns) ||\n\t\t\t\t\t\t\tmatchesAnyPattern(relForMatch, excludePatterns);\n\t\t\t\t\t\tif (excluded) return;\n\t\t\t\t\t\tlet kind: FSEventType = \"change\";\n\t\t\t\t\t\tif (eventType === \"rename\") {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tkind = existsSync(normalized) ? \"create\" : \"delete\";\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\tkind = \"rename\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpending.set(normalized, {\n\t\t\t\t\t\t\ttype: kind,\n\t\t\t\t\t\t\tpath: normalized,\n\t\t\t\t\t\t\troot,\n\t\t\t\t\t\t\trelative_path: relForMatch,\n\t\t\t\t\t\t\ttimestamp_ns: wallClockNs(),\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\t\t\t\tconst token = generation;\n\t\t\t\t\t\ttimer = setTimeout(() => flush(token), debounce);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\twatcher.on(\"error\", (err) => emitError(err));\n\t\t\t\twatchers.push(watcher);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\temitError(err);\n\t\t}\n\t\treturn {\n\t\t\tonDeactivation: () => {\n\t\t\t\tstopped = true;\n\t\t\t\tgeneration += 1;\n\t\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\t\ttimer = undefined;\n\t\t\t\tcloseWatchers();\n\t\t\t\tpending.clear();\n\t\t\t},\n\t\t};\n\t}, sourceOpts(rest));\n}\n","/**\n * Git hook source — Node-only reactive source that polls a repository's HEAD\n * and emits structured `GitEvent`s on every new commit.\n *\n * Isolated from `./adapters.ts` so that the universal `extra/index` barrel\n * stays browser-safe. Access via `@graphrefly/graphrefly/extra/node`, which\n * re-exports this module.\n *\n * @module\n */\n\nimport { ERROR, type Node, type NodeOptions, node, wallClockNs } from \"@graphrefly/pure-ts/core\";\nimport { fromTimer, globToRegExp, matchesAnyPattern, switchMap } from \"@graphrefly/pure-ts/extra\";\n\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\n\n/** Git hook type for {@link fromGitHook}. */\nexport type GitHookType = \"post-commit\" | \"post-merge\" | \"post-checkout\" | \"post-rewrite\";\n\n/** Structured git event emitted by {@link fromGitHook}. */\nexport type GitEvent = {\n\thook: GitHookType;\n\tcommit: string;\n\tfiles: string[];\n\tmessage: string;\n\tauthor: string;\n\ttimestamp_ns: number;\n};\n\n/** Options for {@link fromGitHook}. */\nexport type FromGitHookOptions = ExtraOpts & {\n\tpollMs?: number;\n\tinclude?: string[];\n\texclude?: string[];\n\t/**\n\t * Maximum consecutive poll errors before terminating the source. Prevents\n\t * error storms when the repository is unavailable (e.g. deleted, corrupt,\n\t * permissions lost). Default: `1` (terminate on first error — preserves\n\t * pre-switchMap back-compat). Raise it (or set `Infinity`) to keep\n\t * retrying indefinitely (legacy behavior).\n\t */\n\tmaxConsecutiveErrors?: number;\n};\n\n/**\n * Git change detection as a reactive source.\n *\n * @category extra\n */\nexport function fromGitHook(repoPath: string, opts?: FromGitHookOptions): Node<GitEvent> {\n\tconst { pollMs = 5000, include, exclude, maxConsecutiveErrors = 1 } = opts ?? {};\n\tconst includePatterns = include?.map(globToRegExp) ?? [];\n\tconst excludePatterns = exclude?.map(globToRegExp) ?? [];\n\tconst { execFileSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\n\tconst gitQuery = (args: string[]): string =>\n\t\texecFileSync(\"git\", args, { cwd: repoPath, encoding: \"utf-8\" }).trim();\n\n\t// Shared across ticks: the previous HEAD we committed to. Undefined on the\n\t// very first poll (we record the initial HEAD without emitting).\n\tlet lastSeen: string | undefined;\n\t// Circuit breaker: consecutive error count. Resets on any successful poll.\n\tlet consecutiveErrors = 0;\n\n\t// `fromTimer | switchMap(sync-git-diff)` — ticks drive the poll, switchMap\n\t// cancels any in-flight inner on next tick. First tick at t=0 records the\n\t// baseline HEAD silently; subsequent ticks emit `GitEvent` on HEAD change.\n\treturn switchMap(fromTimer(0, { period: pollMs }), () =>\n\t\tnode<GitEvent>((_data, a) => {\n\t\t\ttry {\n\t\t\t\tconst head = gitQuery([\"rev-parse\", \"HEAD\"]);\n\t\t\t\tif (!head) {\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn { onDeactivation: () => {} };\n\t\t\t\t}\n\t\t\t\tif (lastSeen === undefined) {\n\t\t\t\t\t// First poll: record baseline; stay idle until next tick\n\t\t\t\t\t// disposes this inner.\n\t\t\t\t\tlastSeen = head;\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn { onDeactivation: () => {} };\n\t\t\t\t}\n\t\t\t\tif (head === lastSeen) {\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn { onDeactivation: () => {} };\n\t\t\t\t}\n\t\t\t\tlet files = gitQuery([\"diff\", \"--name-only\", `${lastSeen}..${head}`])\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.filter(Boolean);\n\t\t\t\tif (includePatterns.length > 0) {\n\t\t\t\t\tfiles = files.filter((f) => matchesAnyPattern(f, includePatterns));\n\t\t\t\t}\n\t\t\t\tif (excludePatterns.length > 0) {\n\t\t\t\t\tfiles = files.filter((f) => !matchesAnyPattern(f, excludePatterns));\n\t\t\t\t}\n\t\t\t\tconst message = gitQuery([\"log\", \"-1\", \"--format=%s\", head]);\n\t\t\t\tconst author = gitQuery([\"log\", \"-1\", \"--format=%an\", head]);\n\t\t\t\ta.emit({\n\t\t\t\t\thook: \"post-commit\" as GitHookType,\n\t\t\t\t\tcommit: head,\n\t\t\t\t\tfiles,\n\t\t\t\t\tmessage,\n\t\t\t\t\tauthor,\n\t\t\t\t\ttimestamp_ns: wallClockNs(),\n\t\t\t\t});\n\t\t\t\tlastSeen = head;\n\t\t\t\tconsecutiveErrors = 0;\n\t\t\t} catch (err) {\n\t\t\t\tconsecutiveErrors += 1;\n\t\t\t\tif (consecutiveErrors >= maxConsecutiveErrors) {\n\t\t\t\t\ta.down([[ERROR, err]]);\n\t\t\t\t}\n\t\t\t\t// else: transient error — next tick will retry; don't spam ERROR.\n\t\t\t}\n\t\t\treturn { onDeactivation: () => {} };\n\t\t}),\n\t);\n}\n","/**\n * Child-process reactive sources — Node-only.\n *\n * Isolated from `./sources.ts` so bundlers targeting the browser can import\n * browser-safe sources without pulling in `node:child_process`.\n *\n * Access via `@graphrefly/graphrefly/extra/node`, which re-exports this module.\n *\n * @module\n */\n\nimport { type SpawnOptions, spawn } from \"node:child_process\";\nimport { COMPLETE, DATA, ERROR, type Messages, type Node, node } from \"@graphrefly/pure-ts/core\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * SpawnEvent — discriminated stream emitted by {@link fromSpawn}.\n *\n * @category extra\n */\nexport type SpawnEvent =\n\t| { kind: \"stdout\"; chunk: Buffer }\n\t| { kind: \"stderr\"; chunk: Buffer }\n\t| { kind: \"exit\"; code: number | null; signal: NodeJS.Signals | null };\n\n/** Options for {@link fromSpawn}. Mirrors `child_process.SpawnOptions`. */\nexport interface FromSpawnOptions {\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\tshell?: boolean | string;\n\t/**\n\t * Optional caller-owned AbortSignal. When fired, the subprocess is sent\n\t * SIGTERM (per `child_process.spawn` signal semantics). The producer's own\n\t * teardown also sends SIGTERM regardless of caller signal — so `switchMap`\n\t * supersede in `actuatorExecutor` cancels in-flight subprocesses without\n\t * the caller wiring extra signals.\n\t */\n\tsignal?: AbortSignal;\n\t/** Extra args forwarded to spawn — e.g. stdio configuration. */\n\tstdio?: \"pipe\" | readonly (\"pipe\" | \"ignore\" | \"inherit\")[];\n}\n\n// ---------------------------------------------------------------------------\n// fromSpawn\n// ---------------------------------------------------------------------------\n\n/**\n * Spawn `cmd args` as a child process and stream stdout/stderr/exit as a\n * single discriminated `SpawnEvent` stream.\n *\n * Lifecycle:\n * - Stdout/stderr chunks emit as `DATA { kind: \"stdout\"|\"stderr\", chunk }`.\n * - Process exit emits one final `DATA { kind: \"exit\", code, signal }` then\n * `COMPLETE`.\n * - Spawn-error (ENOENT, EPERM, …) emits `ERROR`.\n * - Producer teardown sends `SIGTERM` to the subprocess if it is still alive.\n *\n * **Multicast semantics:** `fromSpawn` returns a node backed by a single\n * `producer` activation — the subprocess is spawned once when the first\n * subscriber connects, and all subsequent subscribers share the same event\n * stream. Unsubscribing the last subscriber tears down the subprocess.\n *\n * @example\n * ```ts\n * import { fromSpawn } from \"@graphrefly/graphrefly/extra/node\";\n *\n * const stream = fromSpawn(\"git\", [\"log\", \"--oneline\"]);\n * stream.subscribe((msgs) => {\n * for (const [type, value] of msgs) {\n * if (type === DATA) console.log(value);\n * }\n * });\n * ```\n *\n * @category extra\n */\nexport function fromSpawn(\n\tcmd: string,\n\targs: readonly string[],\n\topts?: FromSpawnOptions,\n): Node<SpawnEvent> {\n\treturn node<SpawnEvent>(\n\t\t(_data, actions) => {\n\t\t\tconst child = spawn(cmd, args as string[], {\n\t\t\t\tcwd: opts?.cwd,\n\t\t\t\tenv: opts?.env,\n\t\t\t\tshell: opts?.shell,\n\t\t\t\tsignal: opts?.signal,\n\t\t\t\tstdio: (opts?.stdio as SpawnOptions[\"stdio\"]) ?? \"pipe\",\n\t\t\t});\n\n\t\t\tlet alive = true;\n\t\t\tlet exitInfo: { code: number | null; signal: NodeJS.Signals | null } | null = null;\n\n\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tactions.down([[DATA, { kind: \"stdout\", chunk }]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tactions.down([[DATA, { kind: \"stderr\", chunk }]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tactions.down([[ERROR, err]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"exit\", (code, signal) => {\n\t\t\t\t// Capture exit info, but defer terminal emission to \"close\" — by which\n\t\t\t\t// time all stdout/stderr \"data\" events have been delivered.\n\t\t\t\tif (exitInfo == null) exitInfo = { code, signal: signal as NodeJS.Signals | null };\n\t\t\t});\n\n\t\t\tchild.on(\"close\", () => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tconst info = exitInfo ?? { code: null, signal: null };\n\t\t\t\tactions.down([\n\t\t\t\t\t[DATA, { kind: \"exit\", code: info.code, signal: info.signal }],\n\t\t\t\t\t[COMPLETE],\n\t\t\t\t] satisfies Messages);\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tonDeactivation: () => {\n\t\t\t\t\tif (alive) {\n\t\t\t\t\t\talive = false;\n\t\t\t\t\t\tchild.stdout?.removeAllListeners();\n\t\t\t\t\t\tchild.stderr?.removeAllListeners();\n\t\t\t\t\t\tchild.removeAllListeners(\"error\");\n\t\t\t\t\t\tchild.removeAllListeners(\"exit\");\n\t\t\t\t\t\tchild.removeAllListeners(\"close\");\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// already dead — ignore\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\t{ name: \"from_spawn\" },\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// runProcess\n// ---------------------------------------------------------------------------\n\n/**\n * Run `cmd args` to completion and emit one DATA with aggregated output.\n *\n * Convenience over {@link fromSpawn} for the \"wait for the process to finish,\n * capture stdout/stderr as strings, get exit code\" case.\n *\n * Aggregation policy: stdout and stderr are concatenated as `Buffer`s and\n * decoded as utf-8 once at exit, so multi-byte sequences split across chunks\n * are handled correctly. Actuators that need byte-exact stdout should use\n * `fromSpawn` directly.\n *\n * @example\n * ```ts\n * import { runProcess } from \"@graphrefly/graphrefly/extra/node\";\n *\n * const result = runProcess(\"git\", [\"rev-parse\", \"HEAD\"]);\n * result.subscribe((msgs) => {\n * for (const [type, value] of msgs) {\n * if (type === DATA) console.log(value.stdout.trim());\n * }\n * });\n * ```\n *\n * @category extra\n */\nexport function runProcess(\n\tcmd: string,\n\targs: readonly string[],\n\topts?: FromSpawnOptions,\n): Node<{\n\tstdout: string;\n\tstderr: string;\n\texitCode: number | null;\n\tsignal: NodeJS.Signals | null;\n}> {\n\ttype Result = {\n\t\tstdout: string;\n\t\tstderr: string;\n\t\texitCode: number | null;\n\t\tsignal: NodeJS.Signals | null;\n\t};\n\treturn node<Result>(\n\t\t(_data, actions) => {\n\t\t\tconst child = spawn(cmd, args as string[], {\n\t\t\t\tcwd: opts?.cwd,\n\t\t\t\tenv: opts?.env,\n\t\t\t\tshell: opts?.shell,\n\t\t\t\tsignal: opts?.signal,\n\t\t\t\tstdio: (opts?.stdio as SpawnOptions[\"stdio\"]) ?? \"pipe\",\n\t\t\t});\n\n\t\t\tlet alive = true;\n\t\t\tconst stdoutChunks: Buffer[] = [];\n\t\t\tconst stderrChunks: Buffer[] = [];\n\t\t\tlet exitInfo: { code: number | null; signal: NodeJS.Signals | null } | null = null;\n\n\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tstdoutChunks.push(chunk);\n\t\t\t});\n\n\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tstderrChunks.push(chunk);\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tactions.down([[ERROR, err]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"exit\", (code, signal) => {\n\t\t\t\t// Capture exit info, but defer terminal emission to \"close\" — by which\n\t\t\t\t// time all stdout/stderr \"data\" events have been delivered.\n\t\t\t\tif (exitInfo == null) exitInfo = { code, signal: signal as NodeJS.Signals | null };\n\t\t\t});\n\n\t\t\tchild.on(\"close\", () => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tconst info = exitInfo ?? { code: null, signal: null };\n\t\t\t\tconst stdout = Buffer.concat(stdoutChunks).toString(\"utf8\");\n\t\t\t\tconst stderr = Buffer.concat(stderrChunks).toString(\"utf8\");\n\t\t\t\tactions.down([\n\t\t\t\t\t[\n\t\t\t\t\t\tDATA,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstdout,\n\t\t\t\t\t\t\tstderr,\n\t\t\t\t\t\t\texitCode: info.code,\n\t\t\t\t\t\t\tsignal: info.signal,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\t[COMPLETE],\n\t\t\t\t] satisfies Messages);\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tonDeactivation: () => {\n\t\t\t\t\tif (alive) {\n\t\t\t\t\t\talive = false;\n\t\t\t\t\t\tchild.stdout?.removeAllListeners();\n\t\t\t\t\t\tchild.stderr?.removeAllListeners();\n\t\t\t\t\t\tchild.removeAllListeners(\"error\");\n\t\t\t\t\t\tchild.removeAllListeners(\"exit\");\n\t\t\t\t\t\tchild.removeAllListeners(\"close\");\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// already dead — ignore\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\t{ name: \"run_process\" },\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACMA,qBAAkC;AAClC,uBAAuC;AACvC,kBAQO;AACP,mBAAgD;AAIhD,SAAS,WAAwB,MAAkC;AAClE,SAAO,EAAE,cAAc,YAAY,GAAG,KAAK;AAC5C;AA2BO,SAAS,YAAY,OAA0B,MAA0C;AAC/F,QAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAClD,MAAI,KAAK,WAAW,GAAG;AACtB,UAAM,IAAI,WAAW,uCAAuC;AAAA,EAC7D;AACA,QAAM,EAAE,YAAY,MAAM,WAAW,KAAK,SAAS,SAAS,GAAG,KAAK,IAAI,QAAQ,CAAC;AACjF,QAAM,kBAAkB,SAAS,IAAI,yBAAY,KAAK,CAAC;AACvD,QAAM,mBAAmB,WAAW,CAAC,sBAAsB,cAAc,YAAY,GAAG;AAAA,IACvF;AAAA,EACD;AACA,aAAO,kBAAc,CAAC,OAAO,MAAM;AAClC,UAAM,UAAU,oBAAI,IAAqB;AACzC,UAAM,WAAuC,CAAC;AAC9C,QAAI,UAAU;AACd,QAAI,kBAAkB;AACtB,QAAI,aAAa;AACjB,UAAM,gBAAgB,MAAM;AAC3B,iBAAW,WAAW,SAAS,OAAO,CAAC,EAAG,SAAQ,MAAM;AAAA,IACzD;AACA,UAAM,YAAY,CAAC,QAAiB;AACnC,UAAI,gBAAiB;AACrB,wBAAkB;AAClB,gBAAU;AACV,UAAI,UAAU,OAAW,cAAa,KAAK;AAC3C,cAAQ;AACR,cAAQ,MAAM;AACd,oBAAc;AACd,QAAE,KAAK,CAAC,CAAC,mBAAO,GAAG,CAAC,CAAC;AAAA,IACtB;AACA,QAAI;AACJ,UAAM,QAAQ,CAAC,UAAkB;AAChC,cAAQ;AACR,UAAI,WAAW,gBAAiB;AAChC,UAAI,QAAQ,SAAS,EAAG;AACxB,YAAM,gBAA2B,CAAC;AAClC,iBAAW,OAAO,QAAQ,OAAO,EAAG,eAAc,KAAK,CAAC,kBAAM,GAAG,CAAC;AAClE,cAAQ,MAAM;AACd,UAAI,WAAW,mBAAmB,UAAU,WAAY;AACxD,QAAE,KAAK,aAAa;AAAA,IACrB;AACA,QAAI;AACH,iBAAW,YAAY,MAAM;AAC5B,cAAM,cAAU;AAAA,UACf;AAAA,UACA,EAAE,UAAU;AAAA,UACZ,CAAC,WAAgC,aAAqC;AACrE,gBAAI,WAAW,gBAAiB;AAChC,gBAAI,YAAY,KAAM;AACtB,kBAAM,MAAM,OAAO,QAAQ,EAAE,WAAW,MAAM,GAAG;AACjD,kBAAM,UAAM,iBAAAA,SAAY,UAAU,OAAO,QAAQ,CAAC;AAClD,kBAAM,aAAa,IAAI,WAAW,MAAM,GAAG;AAC3C,kBAAM,WAAO,iBAAAA,SAAY,QAAQ,EAAE,WAAW,MAAM,GAAG;AACvD,kBAAM,cAAc,IAAI,WAAW,IAAI,IAAI,IAAI,MAAM,CAAC,IAAI;AAC1D,kBAAM,WACL,gBAAgB,WAAW,SAC3B,gCAAkB,YAAY,eAAe,SAC7C,gCAAkB,aAAa,eAAe;AAC/C,gBAAI,CAAC,SAAU;AACf,kBAAM,eACL,gCAAkB,YAAY,eAAe,SAC7C,gCAAkB,aAAa,eAAe;AAC/C,gBAAI,SAAU;AACd,gBAAI,OAAoB;AACxB,gBAAI,cAAc,UAAU;AAC3B,kBAAI;AACH,2BAAO,2BAAW,UAAU,IAAI,WAAW;AAAA,cAC5C,QAAQ;AACP,uBAAO;AAAA,cACR;AAAA,YACD;AACA,oBAAQ,IAAI,YAAY;AAAA,cACvB,MAAM;AAAA,cACN,MAAM;AAAA,cACN;AAAA,cACA,eAAe;AAAA,cACf,kBAAc,yBAAY;AAAA,YAC3B,CAAC;AACD,gBAAI,UAAU,OAAW,cAAa,KAAK;AAC3C,kBAAM,QAAQ;AACd,oBAAQ,WAAW,MAAM,MAAM,KAAK,GAAG,QAAQ;AAAA,UAChD;AAAA,QACD;AACA,gBAAQ,GAAG,SAAS,CAAC,QAAQ,UAAU,GAAG,CAAC;AAC3C,iBAAS,KAAK,OAAO;AAAA,MACtB;AAAA,IACD,SAAS,KAAK;AACb,gBAAU,GAAG;AAAA,IACd;AACA,WAAO;AAAA,MACN,gBAAgB,MAAM;AACrB,kBAAU;AACV,sBAAc;AACd,YAAI,UAAU,OAAW,cAAa,KAAK;AAC3C,gBAAQ;AACR,sBAAc;AACd,gBAAQ,MAAM;AAAA,MACf;AAAA,IACD;AAAA,EACD,GAAG,WAAW,IAAI,CAAC;AACpB;;;AC1IA,IAAAC,eAAsE;AACtE,IAAAC,gBAAsE;AAqC/D,SAAS,YAAY,UAAkB,MAA2C;AACxF,QAAM,EAAE,SAAS,KAAM,SAAS,SAAS,uBAAuB,EAAE,IAAI,QAAQ,CAAC;AAC/E,QAAM,kBAAkB,SAAS,IAAI,0BAAY,KAAK,CAAC;AACvD,QAAM,kBAAkB,SAAS,IAAI,0BAAY,KAAK,CAAC;AACvD,QAAM,EAAE,aAAa,IAAI,QAAQ,eAAoB;AAErD,QAAM,WAAW,CAAC,SACjB,aAAa,OAAO,MAAM,EAAE,KAAK,UAAU,UAAU,QAAQ,CAAC,EAAE,KAAK;AAItE,MAAI;AAEJ,MAAI,oBAAoB;AAKxB,aAAO;AAAA,QAAU,yBAAU,GAAG,EAAE,QAAQ,OAAO,CAAC;AAAA,IAAG,UAClD,mBAAe,CAAC,OAAO,MAAM;AAC5B,UAAI;AACH,cAAM,OAAO,SAAS,CAAC,aAAa,MAAM,CAAC;AAC3C,YAAI,CAAC,MAAM;AACV,8BAAoB;AACpB,iBAAO,EAAE,gBAAgB,MAAM;AAAA,UAAC,EAAE;AAAA,QACnC;AACA,YAAI,aAAa,QAAW;AAG3B,qBAAW;AACX,8BAAoB;AACpB,iBAAO,EAAE,gBAAgB,MAAM;AAAA,UAAC,EAAE;AAAA,QACnC;AACA,YAAI,SAAS,UAAU;AACtB,8BAAoB;AACpB,iBAAO,EAAE,gBAAgB,MAAM;AAAA,UAAC,EAAE;AAAA,QACnC;AACA,YAAI,QAAQ,SAAS,CAAC,QAAQ,eAAe,GAAG,QAAQ,KAAK,IAAI,EAAE,CAAC,EAClE,MAAM,IAAI,EACV,OAAO,OAAO;AAChB,YAAI,gBAAgB,SAAS,GAAG;AAC/B,kBAAQ,MAAM,OAAO,CAAC,UAAM,iCAAkB,GAAG,eAAe,CAAC;AAAA,QAClE;AACA,YAAI,gBAAgB,SAAS,GAAG;AAC/B,kBAAQ,MAAM,OAAO,CAAC,MAAM,KAAC,iCAAkB,GAAG,eAAe,CAAC;AAAA,QACnE;AACA,cAAM,UAAU,SAAS,CAAC,OAAO,MAAM,eAAe,IAAI,CAAC;AAC3D,cAAM,SAAS,SAAS,CAAC,OAAO,MAAM,gBAAgB,IAAI,CAAC;AAC3D,UAAE,KAAK;AAAA,UACN,MAAM;AAAA,UACN,QAAQ;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,UACA,kBAAc,0BAAY;AAAA,QAC3B,CAAC;AACD,mBAAW;AACX,4BAAoB;AAAA,MACrB,SAAS,KAAK;AACb,6BAAqB;AACrB,YAAI,qBAAqB,sBAAsB;AAC9C,YAAE,KAAK,CAAC,CAAC,oBAAO,GAAG,CAAC,CAAC;AAAA,QACtB;AAAA,MAED;AACA,aAAO,EAAE,gBAAgB,MAAM;AAAA,MAAC,EAAE;AAAA,IACnC,CAAC;AAAA,EACF;AACD;;;AC1GA,gCAAyC;AACzC,IAAAC,eAAsE;AAmE/D,SAAS,UACf,KACA,MACA,MACmB;AACnB,aAAO;AAAA,IACN,CAAC,OAAO,YAAY;AACnB,YAAM,YAAQ,iCAAM,KAAK,MAAkB;AAAA,QAC1C,KAAK,MAAM;AAAA,QACX,KAAK,MAAM;AAAA,QACX,OAAO,MAAM;AAAA,QACb,QAAQ,MAAM;AAAA,QACd,OAAQ,MAAM,SAAmC;AAAA,MAClD,CAAC;AAED,UAAI,QAAQ;AACZ,UAAI,WAA0E;AAE9E,YAAM,QAAQ,GAAG,QAAQ,CAAC,UAAkB;AAC3C,YAAI,CAAC,MAAO;AACZ,gBAAQ,KAAK,CAAC,CAAC,mBAAM,EAAE,MAAM,UAAU,MAAM,CAAC,CAAC,CAAoB;AAAA,MACpE,CAAC;AAED,YAAM,QAAQ,GAAG,QAAQ,CAAC,UAAkB;AAC3C,YAAI,CAAC,MAAO;AACZ,gBAAQ,KAAK,CAAC,CAAC,mBAAM,EAAE,MAAM,UAAU,MAAM,CAAC,CAAC,CAAoB;AAAA,MACpE,CAAC;AAED,YAAM,GAAG,SAAS,CAAC,QAAQ;AAC1B,YAAI,CAAC,MAAO;AACZ,gBAAQ;AACR,gBAAQ,KAAK,CAAC,CAAC,oBAAO,GAAG,CAAC,CAAoB;AAAA,MAC/C,CAAC;AAED,YAAM,GAAG,QAAQ,CAAC,MAAM,WAAW;AAGlC,YAAI,YAAY,KAAM,YAAW,EAAE,MAAM,OAAwC;AAAA,MAClF,CAAC;AAED,YAAM,GAAG,SAAS,MAAM;AACvB,YAAI,CAAC,MAAO;AACZ,gBAAQ;AACR,cAAM,OAAO,YAAY,EAAE,MAAM,MAAM,QAAQ,KAAK;AACpD,gBAAQ,KAAK;AAAA,UACZ,CAAC,mBAAM,EAAE,MAAM,QAAQ,MAAM,KAAK,MAAM,QAAQ,KAAK,OAAO,CAAC;AAAA,UAC7D,CAAC,qBAAQ;AAAA,QACV,CAAoB;AAAA,MACrB,CAAC;AAED,aAAO;AAAA,QACN,gBAAgB,MAAM;AACrB,cAAI,OAAO;AACV,oBAAQ;AACR,kBAAM,QAAQ,mBAAmB;AACjC,kBAAM,QAAQ,mBAAmB;AACjC,kBAAM,mBAAmB,OAAO;AAChC,kBAAM,mBAAmB,MAAM;AAC/B,kBAAM,mBAAmB,OAAO;AAChC,gBAAI;AACH,oBAAM,KAAK,SAAS;AAAA,YACrB,QAAQ;AAAA,YAER;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA,EAAE,MAAM,aAAa;AAAA,EACtB;AACD;AA+BO,SAAS,WACf,KACA,MACA,MAME;AAOF,aAAO;AAAA,IACN,CAAC,OAAO,YAAY;AACnB,YAAM,YAAQ,iCAAM,KAAK,MAAkB;AAAA,QAC1C,KAAK,MAAM;AAAA,QACX,KAAK,MAAM;AAAA,QACX,OAAO,MAAM;AAAA,QACb,QAAQ,MAAM;AAAA,QACd,OAAQ,MAAM,SAAmC;AAAA,MAClD,CAAC;AAED,UAAI,QAAQ;AACZ,YAAM,eAAyB,CAAC;AAChC,YAAM,eAAyB,CAAC;AAChC,UAAI,WAA0E;AAE9E,YAAM,QAAQ,GAAG,QAAQ,CAAC,UAAkB;AAC3C,YAAI,CAAC,MAAO;AACZ,qBAAa,KAAK,KAAK;AAAA,MACxB,CAAC;AAED,YAAM,QAAQ,GAAG,QAAQ,CAAC,UAAkB;AAC3C,YAAI,CAAC,MAAO;AACZ,qBAAa,KAAK,KAAK;AAAA,MACxB,CAAC;AAED,YAAM,GAAG,SAAS,CAAC,QAAQ;AAC1B,YAAI,CAAC,MAAO;AACZ,gBAAQ;AACR,gBAAQ,KAAK,CAAC,CAAC,oBAAO,GAAG,CAAC,CAAoB;AAAA,MAC/C,CAAC;AAED,YAAM,GAAG,QAAQ,CAAC,MAAM,WAAW;AAGlC,YAAI,YAAY,KAAM,YAAW,EAAE,MAAM,OAAwC;AAAA,MAClF,CAAC;AAED,YAAM,GAAG,SAAS,MAAM;AACvB,YAAI,CAAC,MAAO;AACZ,gBAAQ;AACR,cAAM,OAAO,YAAY,EAAE,MAAM,MAAM,QAAQ,KAAK;AACpD,cAAM,SAAS,OAAO,OAAO,YAAY,EAAE,SAAS,MAAM;AAC1D,cAAM,SAAS,OAAO,OAAO,YAAY,EAAE,SAAS,MAAM;AAC1D,gBAAQ,KAAK;AAAA,UACZ;AAAA,YACC;AAAA,YACA;AAAA,cACC;AAAA,cACA;AAAA,cACA,UAAU,KAAK;AAAA,cACf,QAAQ,KAAK;AAAA,YACd;AAAA,UACD;AAAA,UACA,CAAC,qBAAQ;AAAA,QACV,CAAoB;AAAA,MACrB,CAAC;AAED,aAAO;AAAA,QACN,gBAAgB,MAAM;AACrB,cAAI,OAAO;AACV,oBAAQ;AACR,kBAAM,QAAQ,mBAAmB;AACjC,kBAAM,QAAQ,mBAAmB;AACjC,kBAAM,mBAAmB,OAAO;AAChC,kBAAM,mBAAmB,MAAM;AAC/B,kBAAM,mBAAmB,OAAO;AAChC,gBAAI;AACH,oBAAM,KAAK,SAAS;AAAA,YACrB,QAAQ;AAAA,YAER;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA,EAAE,MAAM,cAAc;AAAA,EACvB;AACD;","names":["resolvePath","import_core","import_extra","import_core"]}
@@ -98,13 +98,15 @@ function fromFSWatch(paths, opts) {
98
98
  } catch (err) {
99
99
  emitError(err);
100
100
  }
101
- return () => {
102
- stopped = true;
103
- generation += 1;
104
- if (timer !== void 0) clearTimeout(timer);
105
- timer = void 0;
106
- closeWatchers();
107
- pending.clear();
101
+ return {
102
+ onDeactivation: () => {
103
+ stopped = true;
104
+ generation += 1;
105
+ if (timer !== void 0) clearTimeout(timer);
106
+ timer = void 0;
107
+ closeWatchers();
108
+ pending.clear();
109
+ }
108
110
  };
109
111
  }, sourceOpts(rest));
110
112
  }
@@ -127,19 +129,19 @@ function fromGitHook(repoPath, opts) {
127
129
  const head = gitQuery(["rev-parse", "HEAD"]);
128
130
  if (!head) {
129
131
  consecutiveErrors = 0;
130
- return () => {
131
- };
132
+ return { onDeactivation: () => {
133
+ } };
132
134
  }
133
135
  if (lastSeen === void 0) {
134
136
  lastSeen = head;
135
137
  consecutiveErrors = 0;
136
- return () => {
137
- };
138
+ return { onDeactivation: () => {
139
+ } };
138
140
  }
139
141
  if (head === lastSeen) {
140
142
  consecutiveErrors = 0;
141
- return () => {
142
- };
143
+ return { onDeactivation: () => {
144
+ } };
143
145
  }
144
146
  let files = gitQuery(["diff", "--name-only", `${lastSeen}..${head}`]).split("\n").filter(Boolean);
145
147
  if (includePatterns.length > 0) {
@@ -166,8 +168,8 @@ function fromGitHook(repoPath, opts) {
166
168
  a.down([[ERROR2, err]]);
167
169
  }
168
170
  }
169
- return () => {
170
- };
171
+ return { onDeactivation: () => {
172
+ } };
171
173
  })
172
174
  );
173
175
  }
@@ -212,17 +214,19 @@ function fromSpawn(cmd, args, opts) {
212
214
  [COMPLETE]
213
215
  ]);
214
216
  });
215
- return () => {
216
- if (alive) {
217
- alive = false;
218
- child.stdout?.removeAllListeners();
219
- child.stderr?.removeAllListeners();
220
- child.removeAllListeners("error");
221
- child.removeAllListeners("exit");
222
- child.removeAllListeners("close");
223
- try {
224
- child.kill("SIGTERM");
225
- } catch {
217
+ return {
218
+ onDeactivation: () => {
219
+ if (alive) {
220
+ alive = false;
221
+ child.stdout?.removeAllListeners();
222
+ child.stderr?.removeAllListeners();
223
+ child.removeAllListeners("error");
224
+ child.removeAllListeners("exit");
225
+ child.removeAllListeners("close");
226
+ try {
227
+ child.kill("SIGTERM");
228
+ } catch {
229
+ }
226
230
  }
227
231
  }
228
232
  };
@@ -279,17 +283,19 @@ function runProcess(cmd, args, opts) {
279
283
  [COMPLETE]
280
284
  ]);
281
285
  });
282
- return () => {
283
- if (alive) {
284
- alive = false;
285
- child.stdout?.removeAllListeners();
286
- child.stderr?.removeAllListeners();
287
- child.removeAllListeners("error");
288
- child.removeAllListeners("exit");
289
- child.removeAllListeners("close");
290
- try {
291
- child.kill("SIGTERM");
292
- } catch {
286
+ return {
287
+ onDeactivation: () => {
288
+ if (alive) {
289
+ alive = false;
290
+ child.stdout?.removeAllListeners();
291
+ child.stderr?.removeAllListeners();
292
+ child.removeAllListeners("error");
293
+ child.removeAllListeners("exit");
294
+ child.removeAllListeners("close");
295
+ try {
296
+ child.kill("SIGTERM");
297
+ } catch {
298
+ }
293
299
  }
294
300
  }
295
301
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/base/sources/node/fs-root.ts","../../../../src/base/sources/node/git-hook.ts","../../../../src/base/sources/node/process.ts"],"sourcesContent":["/**\n * Filesystem-watching source. Isolated from `./sources.ts` so bundlers\n * targeting the browser can import browser-safe sources (`fromTimer`,\n * `fromRaf`, etc.) without pulling in `node:fs`/`node:path`.\n */\n\nimport { existsSync, watch } from \"node:fs\";\nimport { resolve as resolvePath } from \"node:path\";\nimport {\n\tDATA,\n\tERROR,\n\ttype Message,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n\twallClockNs,\n} from \"@graphrefly/pure-ts/core\";\nimport { globToRegExp, matchesAnyPattern } from \"@graphrefly/pure-ts/extra\";\n\ntype ExtraOpts = Omit<NodeOptions<unknown>, \"describeKind\">;\n\nfunction sourceOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\nexport type FSEventType = \"change\" | \"rename\" | \"create\" | \"delete\";\nexport type FSEvent = {\n\ttype: FSEventType;\n\tpath: string;\n\troot: string;\n\trelative_path: string;\n\tsrc_path?: string;\n\tdest_path?: string;\n\ttimestamp_ns: number;\n};\n\nexport type FromFSWatchOptions = ExtraOpts & {\n\trecursive?: boolean;\n\tdebounce?: number;\n\tinclude?: string[];\n\texclude?: string[];\n};\n\n/**\n * Watches filesystem paths and emits debounced change events.\n *\n * Uses `fs.watch` only (no polling fallback). Teardown closes all watchers.\n *\n * @category extra\n */\nexport function fromFSWatch(paths: string | string[], opts?: FromFSWatchOptions): Node<FSEvent> {\n\tconst list = Array.isArray(paths) ? paths : [paths];\n\tif (list.length === 0) {\n\t\tthrow new RangeError(\"fromFSWatch expects at least one path\");\n\t}\n\tconst { recursive = true, debounce = 100, include, exclude, ...rest } = opts ?? {};\n\tconst includePatterns = include?.map(globToRegExp) ?? [];\n\tconst excludePatterns = (exclude ?? [\"**/node_modules/**\", \"**/.git/**\", \"**/dist/**\"]).map(\n\t\tglobToRegExp,\n\t);\n\treturn node<FSEvent>((_data, a) => {\n\t\tconst pending = new Map<string, FSEvent>();\n\t\tconst watchers: ReturnType<typeof watch>[] = [];\n\t\tlet stopped = false;\n\t\tlet terminalEmitted = false;\n\t\tlet generation = 0;\n\t\tconst closeWatchers = () => {\n\t\t\tfor (const watcher of watchers.splice(0)) watcher.close();\n\t\t};\n\t\tconst emitError = (err: unknown) => {\n\t\t\tif (terminalEmitted) return;\n\t\t\tterminalEmitted = true;\n\t\t\tstopped = true;\n\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t\tpending.clear();\n\t\t\tcloseWatchers();\n\t\t\ta.down([[ERROR, err]]);\n\t\t};\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst flush = (token: number) => {\n\t\t\ttimer = undefined;\n\t\t\tif (stopped || terminalEmitted) return;\n\t\t\tif (pending.size === 0) return;\n\t\t\tconst batchMessages: Message[] = [];\n\t\t\tfor (const evt of pending.values()) batchMessages.push([DATA, evt]);\n\t\t\tpending.clear();\n\t\t\tif (stopped || terminalEmitted || token !== generation) return;\n\t\t\ta.down(batchMessages);\n\t\t};\n\t\ttry {\n\t\t\tfor (const basePath of list) {\n\t\t\t\tconst watcher = watch(\n\t\t\t\t\tbasePath,\n\t\t\t\t\t{ recursive },\n\t\t\t\t\t(eventType: \"rename\" | \"change\", fileName: string | Buffer | null) => {\n\t\t\t\t\t\tif (stopped || terminalEmitted) return;\n\t\t\t\t\t\tif (fileName == null) return;\n\t\t\t\t\t\tconst rel = String(fileName).replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst abs = resolvePath(basePath, String(fileName));\n\t\t\t\t\t\tconst normalized = abs.replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst root = resolvePath(basePath).replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst relForMatch = rel.startsWith(\"./\") ? rel.slice(2) : rel;\n\t\t\t\t\t\tconst included =\n\t\t\t\t\t\t\tincludePatterns.length === 0 ||\n\t\t\t\t\t\t\tmatchesAnyPattern(normalized, includePatterns) ||\n\t\t\t\t\t\t\tmatchesAnyPattern(relForMatch, includePatterns);\n\t\t\t\t\t\tif (!included) return;\n\t\t\t\t\t\tconst excluded =\n\t\t\t\t\t\t\tmatchesAnyPattern(normalized, excludePatterns) ||\n\t\t\t\t\t\t\tmatchesAnyPattern(relForMatch, excludePatterns);\n\t\t\t\t\t\tif (excluded) return;\n\t\t\t\t\t\tlet kind: FSEventType = \"change\";\n\t\t\t\t\t\tif (eventType === \"rename\") {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tkind = existsSync(normalized) ? \"create\" : \"delete\";\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\tkind = \"rename\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpending.set(normalized, {\n\t\t\t\t\t\t\ttype: kind,\n\t\t\t\t\t\t\tpath: normalized,\n\t\t\t\t\t\t\troot,\n\t\t\t\t\t\t\trelative_path: relForMatch,\n\t\t\t\t\t\t\ttimestamp_ns: wallClockNs(),\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\t\t\t\tconst token = generation;\n\t\t\t\t\t\ttimer = setTimeout(() => flush(token), debounce);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\twatcher.on(\"error\", (err) => emitError(err));\n\t\t\t\twatchers.push(watcher);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\temitError(err);\n\t\t}\n\t\treturn () => {\n\t\t\tstopped = true;\n\t\t\tgeneration += 1;\n\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t\tcloseWatchers();\n\t\t\tpending.clear();\n\t\t};\n\t}, sourceOpts(rest));\n}\n","/**\n * Git hook source — Node-only reactive source that polls a repository's HEAD\n * and emits structured `GitEvent`s on every new commit.\n *\n * Isolated from `./adapters.ts` so that the universal `extra/index` barrel\n * stays browser-safe. Access via `@graphrefly/graphrefly/extra/node`, which\n * re-exports this module.\n *\n * @module\n */\n\nimport { ERROR, type Node, type NodeOptions, node, wallClockNs } from \"@graphrefly/pure-ts/core\";\nimport { fromTimer, globToRegExp, matchesAnyPattern, switchMap } from \"@graphrefly/pure-ts/extra\";\n\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\n\n/** Git hook type for {@link fromGitHook}. */\nexport type GitHookType = \"post-commit\" | \"post-merge\" | \"post-checkout\" | \"post-rewrite\";\n\n/** Structured git event emitted by {@link fromGitHook}. */\nexport type GitEvent = {\n\thook: GitHookType;\n\tcommit: string;\n\tfiles: string[];\n\tmessage: string;\n\tauthor: string;\n\ttimestamp_ns: number;\n};\n\n/** Options for {@link fromGitHook}. */\nexport type FromGitHookOptions = ExtraOpts & {\n\tpollMs?: number;\n\tinclude?: string[];\n\texclude?: string[];\n\t/**\n\t * Maximum consecutive poll errors before terminating the source. Prevents\n\t * error storms when the repository is unavailable (e.g. deleted, corrupt,\n\t * permissions lost). Default: `1` (terminate on first error — preserves\n\t * pre-switchMap back-compat). Raise it (or set `Infinity`) to keep\n\t * retrying indefinitely (legacy behavior).\n\t */\n\tmaxConsecutiveErrors?: number;\n};\n\n/**\n * Git change detection as a reactive source.\n *\n * @category extra\n */\nexport function fromGitHook(repoPath: string, opts?: FromGitHookOptions): Node<GitEvent> {\n\tconst { pollMs = 5000, include, exclude, maxConsecutiveErrors = 1 } = opts ?? {};\n\tconst includePatterns = include?.map(globToRegExp) ?? [];\n\tconst excludePatterns = exclude?.map(globToRegExp) ?? [];\n\tconst { execFileSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\n\tconst gitQuery = (args: string[]): string =>\n\t\texecFileSync(\"git\", args, { cwd: repoPath, encoding: \"utf-8\" }).trim();\n\n\t// Shared across ticks: the previous HEAD we committed to. Undefined on the\n\t// very first poll (we record the initial HEAD without emitting).\n\tlet lastSeen: string | undefined;\n\t// Circuit breaker: consecutive error count. Resets on any successful poll.\n\tlet consecutiveErrors = 0;\n\n\t// `fromTimer | switchMap(sync-git-diff)` — ticks drive the poll, switchMap\n\t// cancels any in-flight inner on next tick. First tick at t=0 records the\n\t// baseline HEAD silently; subsequent ticks emit `GitEvent` on HEAD change.\n\treturn switchMap(fromTimer(0, { period: pollMs }), () =>\n\t\tnode<GitEvent>((_data, a) => {\n\t\t\ttry {\n\t\t\t\tconst head = gitQuery([\"rev-parse\", \"HEAD\"]);\n\t\t\t\tif (!head) {\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn () => {};\n\t\t\t\t}\n\t\t\t\tif (lastSeen === undefined) {\n\t\t\t\t\t// First poll: record baseline; stay idle until next tick\n\t\t\t\t\t// disposes this inner.\n\t\t\t\t\tlastSeen = head;\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn () => {};\n\t\t\t\t}\n\t\t\t\tif (head === lastSeen) {\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn () => {};\n\t\t\t\t}\n\t\t\t\tlet files = gitQuery([\"diff\", \"--name-only\", `${lastSeen}..${head}`])\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.filter(Boolean);\n\t\t\t\tif (includePatterns.length > 0) {\n\t\t\t\t\tfiles = files.filter((f) => matchesAnyPattern(f, includePatterns));\n\t\t\t\t}\n\t\t\t\tif (excludePatterns.length > 0) {\n\t\t\t\t\tfiles = files.filter((f) => !matchesAnyPattern(f, excludePatterns));\n\t\t\t\t}\n\t\t\t\tconst message = gitQuery([\"log\", \"-1\", \"--format=%s\", head]);\n\t\t\t\tconst author = gitQuery([\"log\", \"-1\", \"--format=%an\", head]);\n\t\t\t\ta.emit({\n\t\t\t\t\thook: \"post-commit\" as GitHookType,\n\t\t\t\t\tcommit: head,\n\t\t\t\t\tfiles,\n\t\t\t\t\tmessage,\n\t\t\t\t\tauthor,\n\t\t\t\t\ttimestamp_ns: wallClockNs(),\n\t\t\t\t});\n\t\t\t\tlastSeen = head;\n\t\t\t\tconsecutiveErrors = 0;\n\t\t\t} catch (err) {\n\t\t\t\tconsecutiveErrors += 1;\n\t\t\t\tif (consecutiveErrors >= maxConsecutiveErrors) {\n\t\t\t\t\ta.down([[ERROR, err]]);\n\t\t\t\t}\n\t\t\t\t// else: transient error — next tick will retry; don't spam ERROR.\n\t\t\t}\n\t\t\treturn () => {};\n\t\t}),\n\t);\n}\n","/**\n * Child-process reactive sources — Node-only.\n *\n * Isolated from `./sources.ts` so bundlers targeting the browser can import\n * browser-safe sources without pulling in `node:child_process`.\n *\n * Access via `@graphrefly/graphrefly/extra/node`, which re-exports this module.\n *\n * @module\n */\n\nimport { type SpawnOptions, spawn } from \"node:child_process\";\nimport { COMPLETE, DATA, ERROR, type Messages, type Node, node } from \"@graphrefly/pure-ts/core\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * SpawnEvent — discriminated stream emitted by {@link fromSpawn}.\n *\n * @category extra\n */\nexport type SpawnEvent =\n\t| { kind: \"stdout\"; chunk: Buffer }\n\t| { kind: \"stderr\"; chunk: Buffer }\n\t| { kind: \"exit\"; code: number | null; signal: NodeJS.Signals | null };\n\n/** Options for {@link fromSpawn}. Mirrors `child_process.SpawnOptions`. */\nexport interface FromSpawnOptions {\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\tshell?: boolean | string;\n\t/**\n\t * Optional caller-owned AbortSignal. When fired, the subprocess is sent\n\t * SIGTERM (per `child_process.spawn` signal semantics). The producer's own\n\t * teardown also sends SIGTERM regardless of caller signal — so `switchMap`\n\t * supersede in `actuatorExecutor` cancels in-flight subprocesses without\n\t * the caller wiring extra signals.\n\t */\n\tsignal?: AbortSignal;\n\t/** Extra args forwarded to spawn — e.g. stdio configuration. */\n\tstdio?: \"pipe\" | readonly (\"pipe\" | \"ignore\" | \"inherit\")[];\n}\n\n// ---------------------------------------------------------------------------\n// fromSpawn\n// ---------------------------------------------------------------------------\n\n/**\n * Spawn `cmd args` as a child process and stream stdout/stderr/exit as a\n * single discriminated `SpawnEvent` stream.\n *\n * Lifecycle:\n * - Stdout/stderr chunks emit as `DATA { kind: \"stdout\"|\"stderr\", chunk }`.\n * - Process exit emits one final `DATA { kind: \"exit\", code, signal }` then\n * `COMPLETE`.\n * - Spawn-error (ENOENT, EPERM, …) emits `ERROR`.\n * - Producer teardown sends `SIGTERM` to the subprocess if it is still alive.\n *\n * **Multicast semantics:** `fromSpawn` returns a node backed by a single\n * `producer` activation — the subprocess is spawned once when the first\n * subscriber connects, and all subsequent subscribers share the same event\n * stream. Unsubscribing the last subscriber tears down the subprocess.\n *\n * @example\n * ```ts\n * import { fromSpawn } from \"@graphrefly/graphrefly/extra/node\";\n *\n * const stream = fromSpawn(\"git\", [\"log\", \"--oneline\"]);\n * stream.subscribe((msgs) => {\n * for (const [type, value] of msgs) {\n * if (type === DATA) console.log(value);\n * }\n * });\n * ```\n *\n * @category extra\n */\nexport function fromSpawn(\n\tcmd: string,\n\targs: readonly string[],\n\topts?: FromSpawnOptions,\n): Node<SpawnEvent> {\n\treturn node<SpawnEvent>(\n\t\t(_data, actions) => {\n\t\t\tconst child = spawn(cmd, args as string[], {\n\t\t\t\tcwd: opts?.cwd,\n\t\t\t\tenv: opts?.env,\n\t\t\t\tshell: opts?.shell,\n\t\t\t\tsignal: opts?.signal,\n\t\t\t\tstdio: (opts?.stdio as SpawnOptions[\"stdio\"]) ?? \"pipe\",\n\t\t\t});\n\n\t\t\tlet alive = true;\n\t\t\tlet exitInfo: { code: number | null; signal: NodeJS.Signals | null } | null = null;\n\n\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tactions.down([[DATA, { kind: \"stdout\", chunk }]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tactions.down([[DATA, { kind: \"stderr\", chunk }]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tactions.down([[ERROR, err]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"exit\", (code, signal) => {\n\t\t\t\t// Capture exit info, but defer terminal emission to \"close\" — by which\n\t\t\t\t// time all stdout/stderr \"data\" events have been delivered.\n\t\t\t\tif (exitInfo == null) exitInfo = { code, signal: signal as NodeJS.Signals | null };\n\t\t\t});\n\n\t\t\tchild.on(\"close\", () => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tconst info = exitInfo ?? { code: null, signal: null };\n\t\t\t\tactions.down([\n\t\t\t\t\t[DATA, { kind: \"exit\", code: info.code, signal: info.signal }],\n\t\t\t\t\t[COMPLETE],\n\t\t\t\t] satisfies Messages);\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tif (alive) {\n\t\t\t\t\talive = false;\n\t\t\t\t\tchild.stdout?.removeAllListeners();\n\t\t\t\t\tchild.stderr?.removeAllListeners();\n\t\t\t\t\tchild.removeAllListeners(\"error\");\n\t\t\t\t\tchild.removeAllListeners(\"exit\");\n\t\t\t\t\tchild.removeAllListeners(\"close\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// already dead — ignore\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\t{ name: \"from_spawn\" },\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// runProcess\n// ---------------------------------------------------------------------------\n\n/**\n * Run `cmd args` to completion and emit one DATA with aggregated output.\n *\n * Convenience over {@link fromSpawn} for the \"wait for the process to finish,\n * capture stdout/stderr as strings, get exit code\" case.\n *\n * Aggregation policy: stdout and stderr are concatenated as `Buffer`s and\n * decoded as utf-8 once at exit, so multi-byte sequences split across chunks\n * are handled correctly. Actuators that need byte-exact stdout should use\n * `fromSpawn` directly.\n *\n * @example\n * ```ts\n * import { runProcess } from \"@graphrefly/graphrefly/extra/node\";\n *\n * const result = runProcess(\"git\", [\"rev-parse\", \"HEAD\"]);\n * result.subscribe((msgs) => {\n * for (const [type, value] of msgs) {\n * if (type === DATA) console.log(value.stdout.trim());\n * }\n * });\n * ```\n *\n * @category extra\n */\nexport function runProcess(\n\tcmd: string,\n\targs: readonly string[],\n\topts?: FromSpawnOptions,\n): Node<{\n\tstdout: string;\n\tstderr: string;\n\texitCode: number | null;\n\tsignal: NodeJS.Signals | null;\n}> {\n\ttype Result = {\n\t\tstdout: string;\n\t\tstderr: string;\n\t\texitCode: number | null;\n\t\tsignal: NodeJS.Signals | null;\n\t};\n\treturn node<Result>(\n\t\t(_data, actions) => {\n\t\t\tconst child = spawn(cmd, args as string[], {\n\t\t\t\tcwd: opts?.cwd,\n\t\t\t\tenv: opts?.env,\n\t\t\t\tshell: opts?.shell,\n\t\t\t\tsignal: opts?.signal,\n\t\t\t\tstdio: (opts?.stdio as SpawnOptions[\"stdio\"]) ?? \"pipe\",\n\t\t\t});\n\n\t\t\tlet alive = true;\n\t\t\tconst stdoutChunks: Buffer[] = [];\n\t\t\tconst stderrChunks: Buffer[] = [];\n\t\t\tlet exitInfo: { code: number | null; signal: NodeJS.Signals | null } | null = null;\n\n\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tstdoutChunks.push(chunk);\n\t\t\t});\n\n\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tstderrChunks.push(chunk);\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tactions.down([[ERROR, err]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"exit\", (code, signal) => {\n\t\t\t\t// Capture exit info, but defer terminal emission to \"close\" — by which\n\t\t\t\t// time all stdout/stderr \"data\" events have been delivered.\n\t\t\t\tif (exitInfo == null) exitInfo = { code, signal: signal as NodeJS.Signals | null };\n\t\t\t});\n\n\t\t\tchild.on(\"close\", () => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tconst info = exitInfo ?? { code: null, signal: null };\n\t\t\t\tconst stdout = Buffer.concat(stdoutChunks).toString(\"utf8\");\n\t\t\t\tconst stderr = Buffer.concat(stderrChunks).toString(\"utf8\");\n\t\t\t\tactions.down([\n\t\t\t\t\t[\n\t\t\t\t\t\tDATA,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstdout,\n\t\t\t\t\t\t\tstderr,\n\t\t\t\t\t\t\texitCode: info.code,\n\t\t\t\t\t\t\tsignal: info.signal,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\t[COMPLETE],\n\t\t\t\t] satisfies Messages);\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tif (alive) {\n\t\t\t\t\talive = false;\n\t\t\t\t\tchild.stdout?.removeAllListeners();\n\t\t\t\t\tchild.stderr?.removeAllListeners();\n\t\t\t\t\tchild.removeAllListeners(\"error\");\n\t\t\t\t\tchild.removeAllListeners(\"exit\");\n\t\t\t\t\tchild.removeAllListeners(\"close\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// already dead — ignore\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\t{ name: \"run_process\" },\n\t);\n}\n"],"mappings":"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","names":["ERROR","node","wallClockNs","globToRegExp","matchesAnyPattern","DATA","ERROR","node"]}
1
+ {"version":3,"sources":["../../../../src/base/sources/node/fs-root.ts","../../../../src/base/sources/node/git-hook.ts","../../../../src/base/sources/node/process.ts"],"sourcesContent":["/**\n * Filesystem-watching source. Isolated from `./sources.ts` so bundlers\n * targeting the browser can import browser-safe sources (`fromTimer`,\n * `fromRaf`, etc.) without pulling in `node:fs`/`node:path`.\n */\n\nimport { existsSync, watch } from \"node:fs\";\nimport { resolve as resolvePath } from \"node:path\";\nimport {\n\tDATA,\n\tERROR,\n\ttype Message,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n\twallClockNs,\n} from \"@graphrefly/pure-ts/core\";\nimport { globToRegExp, matchesAnyPattern } from \"@graphrefly/pure-ts/extra\";\n\ntype ExtraOpts = Omit<NodeOptions<unknown>, \"describeKind\">;\n\nfunction sourceOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"producer\", ...opts } as NodeOptions<T>;\n}\n\nexport type FSEventType = \"change\" | \"rename\" | \"create\" | \"delete\";\nexport type FSEvent = {\n\ttype: FSEventType;\n\tpath: string;\n\troot: string;\n\trelative_path: string;\n\tsrc_path?: string;\n\tdest_path?: string;\n\ttimestamp_ns: number;\n};\n\nexport type FromFSWatchOptions = ExtraOpts & {\n\trecursive?: boolean;\n\tdebounce?: number;\n\tinclude?: string[];\n\texclude?: string[];\n};\n\n/**\n * Watches filesystem paths and emits debounced change events.\n *\n * Uses `fs.watch` only (no polling fallback). Teardown closes all watchers.\n *\n * @category extra\n */\nexport function fromFSWatch(paths: string | string[], opts?: FromFSWatchOptions): Node<FSEvent> {\n\tconst list = Array.isArray(paths) ? paths : [paths];\n\tif (list.length === 0) {\n\t\tthrow new RangeError(\"fromFSWatch expects at least one path\");\n\t}\n\tconst { recursive = true, debounce = 100, include, exclude, ...rest } = opts ?? {};\n\tconst includePatterns = include?.map(globToRegExp) ?? [];\n\tconst excludePatterns = (exclude ?? [\"**/node_modules/**\", \"**/.git/**\", \"**/dist/**\"]).map(\n\t\tglobToRegExp,\n\t);\n\treturn node<FSEvent>((_data, a) => {\n\t\tconst pending = new Map<string, FSEvent>();\n\t\tconst watchers: ReturnType<typeof watch>[] = [];\n\t\tlet stopped = false;\n\t\tlet terminalEmitted = false;\n\t\tlet generation = 0;\n\t\tconst closeWatchers = () => {\n\t\t\tfor (const watcher of watchers.splice(0)) watcher.close();\n\t\t};\n\t\tconst emitError = (err: unknown) => {\n\t\t\tif (terminalEmitted) return;\n\t\t\tterminalEmitted = true;\n\t\t\tstopped = true;\n\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t\tpending.clear();\n\t\t\tcloseWatchers();\n\t\t\ta.down([[ERROR, err]]);\n\t\t};\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst flush = (token: number) => {\n\t\t\ttimer = undefined;\n\t\t\tif (stopped || terminalEmitted) return;\n\t\t\tif (pending.size === 0) return;\n\t\t\tconst batchMessages: Message[] = [];\n\t\t\tfor (const evt of pending.values()) batchMessages.push([DATA, evt]);\n\t\t\tpending.clear();\n\t\t\tif (stopped || terminalEmitted || token !== generation) return;\n\t\t\ta.down(batchMessages);\n\t\t};\n\t\ttry {\n\t\t\tfor (const basePath of list) {\n\t\t\t\tconst watcher = watch(\n\t\t\t\t\tbasePath,\n\t\t\t\t\t{ recursive },\n\t\t\t\t\t(eventType: \"rename\" | \"change\", fileName: string | Buffer | null) => {\n\t\t\t\t\t\tif (stopped || terminalEmitted) return;\n\t\t\t\t\t\tif (fileName == null) return;\n\t\t\t\t\t\tconst rel = String(fileName).replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst abs = resolvePath(basePath, String(fileName));\n\t\t\t\t\t\tconst normalized = abs.replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst root = resolvePath(basePath).replaceAll(\"\\\\\", \"/\");\n\t\t\t\t\t\tconst relForMatch = rel.startsWith(\"./\") ? rel.slice(2) : rel;\n\t\t\t\t\t\tconst included =\n\t\t\t\t\t\t\tincludePatterns.length === 0 ||\n\t\t\t\t\t\t\tmatchesAnyPattern(normalized, includePatterns) ||\n\t\t\t\t\t\t\tmatchesAnyPattern(relForMatch, includePatterns);\n\t\t\t\t\t\tif (!included) return;\n\t\t\t\t\t\tconst excluded =\n\t\t\t\t\t\t\tmatchesAnyPattern(normalized, excludePatterns) ||\n\t\t\t\t\t\t\tmatchesAnyPattern(relForMatch, excludePatterns);\n\t\t\t\t\t\tif (excluded) return;\n\t\t\t\t\t\tlet kind: FSEventType = \"change\";\n\t\t\t\t\t\tif (eventType === \"rename\") {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tkind = existsSync(normalized) ? \"create\" : \"delete\";\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\tkind = \"rename\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpending.set(normalized, {\n\t\t\t\t\t\t\ttype: kind,\n\t\t\t\t\t\t\tpath: normalized,\n\t\t\t\t\t\t\troot,\n\t\t\t\t\t\t\trelative_path: relForMatch,\n\t\t\t\t\t\t\ttimestamp_ns: wallClockNs(),\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\t\t\t\tconst token = generation;\n\t\t\t\t\t\ttimer = setTimeout(() => flush(token), debounce);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\twatcher.on(\"error\", (err) => emitError(err));\n\t\t\t\twatchers.push(watcher);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\temitError(err);\n\t\t}\n\t\treturn {\n\t\t\tonDeactivation: () => {\n\t\t\t\tstopped = true;\n\t\t\t\tgeneration += 1;\n\t\t\t\tif (timer !== undefined) clearTimeout(timer);\n\t\t\t\ttimer = undefined;\n\t\t\t\tcloseWatchers();\n\t\t\t\tpending.clear();\n\t\t\t},\n\t\t};\n\t}, sourceOpts(rest));\n}\n","/**\n * Git hook source — Node-only reactive source that polls a repository's HEAD\n * and emits structured `GitEvent`s on every new commit.\n *\n * Isolated from `./adapters.ts` so that the universal `extra/index` barrel\n * stays browser-safe. Access via `@graphrefly/graphrefly/extra/node`, which\n * re-exports this module.\n *\n * @module\n */\n\nimport { ERROR, type Node, type NodeOptions, node, wallClockNs } from \"@graphrefly/pure-ts/core\";\nimport { fromTimer, globToRegExp, matchesAnyPattern, switchMap } from \"@graphrefly/pure-ts/extra\";\n\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\n\n/** Git hook type for {@link fromGitHook}. */\nexport type GitHookType = \"post-commit\" | \"post-merge\" | \"post-checkout\" | \"post-rewrite\";\n\n/** Structured git event emitted by {@link fromGitHook}. */\nexport type GitEvent = {\n\thook: GitHookType;\n\tcommit: string;\n\tfiles: string[];\n\tmessage: string;\n\tauthor: string;\n\ttimestamp_ns: number;\n};\n\n/** Options for {@link fromGitHook}. */\nexport type FromGitHookOptions = ExtraOpts & {\n\tpollMs?: number;\n\tinclude?: string[];\n\texclude?: string[];\n\t/**\n\t * Maximum consecutive poll errors before terminating the source. Prevents\n\t * error storms when the repository is unavailable (e.g. deleted, corrupt,\n\t * permissions lost). Default: `1` (terminate on first error — preserves\n\t * pre-switchMap back-compat). Raise it (or set `Infinity`) to keep\n\t * retrying indefinitely (legacy behavior).\n\t */\n\tmaxConsecutiveErrors?: number;\n};\n\n/**\n * Git change detection as a reactive source.\n *\n * @category extra\n */\nexport function fromGitHook(repoPath: string, opts?: FromGitHookOptions): Node<GitEvent> {\n\tconst { pollMs = 5000, include, exclude, maxConsecutiveErrors = 1 } = opts ?? {};\n\tconst includePatterns = include?.map(globToRegExp) ?? [];\n\tconst excludePatterns = exclude?.map(globToRegExp) ?? [];\n\tconst { execFileSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\n\tconst gitQuery = (args: string[]): string =>\n\t\texecFileSync(\"git\", args, { cwd: repoPath, encoding: \"utf-8\" }).trim();\n\n\t// Shared across ticks: the previous HEAD we committed to. Undefined on the\n\t// very first poll (we record the initial HEAD without emitting).\n\tlet lastSeen: string | undefined;\n\t// Circuit breaker: consecutive error count. Resets on any successful poll.\n\tlet consecutiveErrors = 0;\n\n\t// `fromTimer | switchMap(sync-git-diff)` — ticks drive the poll, switchMap\n\t// cancels any in-flight inner on next tick. First tick at t=0 records the\n\t// baseline HEAD silently; subsequent ticks emit `GitEvent` on HEAD change.\n\treturn switchMap(fromTimer(0, { period: pollMs }), () =>\n\t\tnode<GitEvent>((_data, a) => {\n\t\t\ttry {\n\t\t\t\tconst head = gitQuery([\"rev-parse\", \"HEAD\"]);\n\t\t\t\tif (!head) {\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn { onDeactivation: () => {} };\n\t\t\t\t}\n\t\t\t\tif (lastSeen === undefined) {\n\t\t\t\t\t// First poll: record baseline; stay idle until next tick\n\t\t\t\t\t// disposes this inner.\n\t\t\t\t\tlastSeen = head;\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn { onDeactivation: () => {} };\n\t\t\t\t}\n\t\t\t\tif (head === lastSeen) {\n\t\t\t\t\tconsecutiveErrors = 0;\n\t\t\t\t\treturn { onDeactivation: () => {} };\n\t\t\t\t}\n\t\t\t\tlet files = gitQuery([\"diff\", \"--name-only\", `${lastSeen}..${head}`])\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.filter(Boolean);\n\t\t\t\tif (includePatterns.length > 0) {\n\t\t\t\t\tfiles = files.filter((f) => matchesAnyPattern(f, includePatterns));\n\t\t\t\t}\n\t\t\t\tif (excludePatterns.length > 0) {\n\t\t\t\t\tfiles = files.filter((f) => !matchesAnyPattern(f, excludePatterns));\n\t\t\t\t}\n\t\t\t\tconst message = gitQuery([\"log\", \"-1\", \"--format=%s\", head]);\n\t\t\t\tconst author = gitQuery([\"log\", \"-1\", \"--format=%an\", head]);\n\t\t\t\ta.emit({\n\t\t\t\t\thook: \"post-commit\" as GitHookType,\n\t\t\t\t\tcommit: head,\n\t\t\t\t\tfiles,\n\t\t\t\t\tmessage,\n\t\t\t\t\tauthor,\n\t\t\t\t\ttimestamp_ns: wallClockNs(),\n\t\t\t\t});\n\t\t\t\tlastSeen = head;\n\t\t\t\tconsecutiveErrors = 0;\n\t\t\t} catch (err) {\n\t\t\t\tconsecutiveErrors += 1;\n\t\t\t\tif (consecutiveErrors >= maxConsecutiveErrors) {\n\t\t\t\t\ta.down([[ERROR, err]]);\n\t\t\t\t}\n\t\t\t\t// else: transient error — next tick will retry; don't spam ERROR.\n\t\t\t}\n\t\t\treturn { onDeactivation: () => {} };\n\t\t}),\n\t);\n}\n","/**\n * Child-process reactive sources — Node-only.\n *\n * Isolated from `./sources.ts` so bundlers targeting the browser can import\n * browser-safe sources without pulling in `node:child_process`.\n *\n * Access via `@graphrefly/graphrefly/extra/node`, which re-exports this module.\n *\n * @module\n */\n\nimport { type SpawnOptions, spawn } from \"node:child_process\";\nimport { COMPLETE, DATA, ERROR, type Messages, type Node, node } from \"@graphrefly/pure-ts/core\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * SpawnEvent — discriminated stream emitted by {@link fromSpawn}.\n *\n * @category extra\n */\nexport type SpawnEvent =\n\t| { kind: \"stdout\"; chunk: Buffer }\n\t| { kind: \"stderr\"; chunk: Buffer }\n\t| { kind: \"exit\"; code: number | null; signal: NodeJS.Signals | null };\n\n/** Options for {@link fromSpawn}. Mirrors `child_process.SpawnOptions`. */\nexport interface FromSpawnOptions {\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\tshell?: boolean | string;\n\t/**\n\t * Optional caller-owned AbortSignal. When fired, the subprocess is sent\n\t * SIGTERM (per `child_process.spawn` signal semantics). The producer's own\n\t * teardown also sends SIGTERM regardless of caller signal — so `switchMap`\n\t * supersede in `actuatorExecutor` cancels in-flight subprocesses without\n\t * the caller wiring extra signals.\n\t */\n\tsignal?: AbortSignal;\n\t/** Extra args forwarded to spawn — e.g. stdio configuration. */\n\tstdio?: \"pipe\" | readonly (\"pipe\" | \"ignore\" | \"inherit\")[];\n}\n\n// ---------------------------------------------------------------------------\n// fromSpawn\n// ---------------------------------------------------------------------------\n\n/**\n * Spawn `cmd args` as a child process and stream stdout/stderr/exit as a\n * single discriminated `SpawnEvent` stream.\n *\n * Lifecycle:\n * - Stdout/stderr chunks emit as `DATA { kind: \"stdout\"|\"stderr\", chunk }`.\n * - Process exit emits one final `DATA { kind: \"exit\", code, signal }` then\n * `COMPLETE`.\n * - Spawn-error (ENOENT, EPERM, …) emits `ERROR`.\n * - Producer teardown sends `SIGTERM` to the subprocess if it is still alive.\n *\n * **Multicast semantics:** `fromSpawn` returns a node backed by a single\n * `producer` activation — the subprocess is spawned once when the first\n * subscriber connects, and all subsequent subscribers share the same event\n * stream. Unsubscribing the last subscriber tears down the subprocess.\n *\n * @example\n * ```ts\n * import { fromSpawn } from \"@graphrefly/graphrefly/extra/node\";\n *\n * const stream = fromSpawn(\"git\", [\"log\", \"--oneline\"]);\n * stream.subscribe((msgs) => {\n * for (const [type, value] of msgs) {\n * if (type === DATA) console.log(value);\n * }\n * });\n * ```\n *\n * @category extra\n */\nexport function fromSpawn(\n\tcmd: string,\n\targs: readonly string[],\n\topts?: FromSpawnOptions,\n): Node<SpawnEvent> {\n\treturn node<SpawnEvent>(\n\t\t(_data, actions) => {\n\t\t\tconst child = spawn(cmd, args as string[], {\n\t\t\t\tcwd: opts?.cwd,\n\t\t\t\tenv: opts?.env,\n\t\t\t\tshell: opts?.shell,\n\t\t\t\tsignal: opts?.signal,\n\t\t\t\tstdio: (opts?.stdio as SpawnOptions[\"stdio\"]) ?? \"pipe\",\n\t\t\t});\n\n\t\t\tlet alive = true;\n\t\t\tlet exitInfo: { code: number | null; signal: NodeJS.Signals | null } | null = null;\n\n\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tactions.down([[DATA, { kind: \"stdout\", chunk }]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tactions.down([[DATA, { kind: \"stderr\", chunk }]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tactions.down([[ERROR, err]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"exit\", (code, signal) => {\n\t\t\t\t// Capture exit info, but defer terminal emission to \"close\" — by which\n\t\t\t\t// time all stdout/stderr \"data\" events have been delivered.\n\t\t\t\tif (exitInfo == null) exitInfo = { code, signal: signal as NodeJS.Signals | null };\n\t\t\t});\n\n\t\t\tchild.on(\"close\", () => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tconst info = exitInfo ?? { code: null, signal: null };\n\t\t\t\tactions.down([\n\t\t\t\t\t[DATA, { kind: \"exit\", code: info.code, signal: info.signal }],\n\t\t\t\t\t[COMPLETE],\n\t\t\t\t] satisfies Messages);\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tonDeactivation: () => {\n\t\t\t\t\tif (alive) {\n\t\t\t\t\t\talive = false;\n\t\t\t\t\t\tchild.stdout?.removeAllListeners();\n\t\t\t\t\t\tchild.stderr?.removeAllListeners();\n\t\t\t\t\t\tchild.removeAllListeners(\"error\");\n\t\t\t\t\t\tchild.removeAllListeners(\"exit\");\n\t\t\t\t\t\tchild.removeAllListeners(\"close\");\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// already dead — ignore\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\t{ name: \"from_spawn\" },\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// runProcess\n// ---------------------------------------------------------------------------\n\n/**\n * Run `cmd args` to completion and emit one DATA with aggregated output.\n *\n * Convenience over {@link fromSpawn} for the \"wait for the process to finish,\n * capture stdout/stderr as strings, get exit code\" case.\n *\n * Aggregation policy: stdout and stderr are concatenated as `Buffer`s and\n * decoded as utf-8 once at exit, so multi-byte sequences split across chunks\n * are handled correctly. Actuators that need byte-exact stdout should use\n * `fromSpawn` directly.\n *\n * @example\n * ```ts\n * import { runProcess } from \"@graphrefly/graphrefly/extra/node\";\n *\n * const result = runProcess(\"git\", [\"rev-parse\", \"HEAD\"]);\n * result.subscribe((msgs) => {\n * for (const [type, value] of msgs) {\n * if (type === DATA) console.log(value.stdout.trim());\n * }\n * });\n * ```\n *\n * @category extra\n */\nexport function runProcess(\n\tcmd: string,\n\targs: readonly string[],\n\topts?: FromSpawnOptions,\n): Node<{\n\tstdout: string;\n\tstderr: string;\n\texitCode: number | null;\n\tsignal: NodeJS.Signals | null;\n}> {\n\ttype Result = {\n\t\tstdout: string;\n\t\tstderr: string;\n\t\texitCode: number | null;\n\t\tsignal: NodeJS.Signals | null;\n\t};\n\treturn node<Result>(\n\t\t(_data, actions) => {\n\t\t\tconst child = spawn(cmd, args as string[], {\n\t\t\t\tcwd: opts?.cwd,\n\t\t\t\tenv: opts?.env,\n\t\t\t\tshell: opts?.shell,\n\t\t\t\tsignal: opts?.signal,\n\t\t\t\tstdio: (opts?.stdio as SpawnOptions[\"stdio\"]) ?? \"pipe\",\n\t\t\t});\n\n\t\t\tlet alive = true;\n\t\t\tconst stdoutChunks: Buffer[] = [];\n\t\t\tconst stderrChunks: Buffer[] = [];\n\t\t\tlet exitInfo: { code: number | null; signal: NodeJS.Signals | null } | null = null;\n\n\t\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tstdoutChunks.push(chunk);\n\t\t\t});\n\n\t\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\tstderrChunks.push(chunk);\n\t\t\t});\n\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tactions.down([[ERROR, err]] satisfies Messages);\n\t\t\t});\n\n\t\t\tchild.on(\"exit\", (code, signal) => {\n\t\t\t\t// Capture exit info, but defer terminal emission to \"close\" — by which\n\t\t\t\t// time all stdout/stderr \"data\" events have been delivered.\n\t\t\t\tif (exitInfo == null) exitInfo = { code, signal: signal as NodeJS.Signals | null };\n\t\t\t});\n\n\t\t\tchild.on(\"close\", () => {\n\t\t\t\tif (!alive) return;\n\t\t\t\talive = false;\n\t\t\t\tconst info = exitInfo ?? { code: null, signal: null };\n\t\t\t\tconst stdout = Buffer.concat(stdoutChunks).toString(\"utf8\");\n\t\t\t\tconst stderr = Buffer.concat(stderrChunks).toString(\"utf8\");\n\t\t\t\tactions.down([\n\t\t\t\t\t[\n\t\t\t\t\t\tDATA,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstdout,\n\t\t\t\t\t\t\tstderr,\n\t\t\t\t\t\t\texitCode: info.code,\n\t\t\t\t\t\t\tsignal: info.signal,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\t[COMPLETE],\n\t\t\t\t] satisfies Messages);\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tonDeactivation: () => {\n\t\t\t\t\tif (alive) {\n\t\t\t\t\t\talive = false;\n\t\t\t\t\t\tchild.stdout?.removeAllListeners();\n\t\t\t\t\t\tchild.stderr?.removeAllListeners();\n\t\t\t\t\t\tchild.removeAllListeners(\"error\");\n\t\t\t\t\t\tchild.removeAllListeners(\"exit\");\n\t\t\t\t\t\tchild.removeAllListeners(\"close\");\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// already dead — ignore\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\t{ name: \"run_process\" },\n\t);\n}\n"],"mappings":"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","names":["ERROR","node","wallClockNs","globToRegExp","matchesAnyPattern","DATA","ERROR","node"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  fromCron
3
- } from "./chunk-7EGRP2VX.js";
3
+ } from "./chunk-7BULJTL6.js";
4
4
  import {
5
5
  cqrs
6
6
  } from "./chunk-NY2PYHNC.js";
@@ -1076,4 +1076,4 @@ export {
1076
1076
  toObservable2 as toObservable,
1077
1077
  nestjs_exports
1078
1078
  };
1079
- //# sourceMappingURL=chunk-VLAGJZSL.js.map
1079
+ //# sourceMappingURL=chunk-3O3NKZJW.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  forEach
3
- } from "./chunk-DKNHAICT.js";
3
+ } from "./chunk-5GVURVIG.js";
4
4
 
5
5
  // src/base/composition/materialize.ts
6
6
  import {
@@ -125,11 +125,13 @@ function materialize(key, factories, parent, opts) {
125
125
  }
126
126
  }
127
127
  });
128
- return () => {
129
- terminated = true;
130
- keyUnsub();
131
- facUnsub();
132
- unmountCurrent();
128
+ return {
129
+ onDeactivation: () => {
130
+ terminated = true;
131
+ keyUnsub();
132
+ facUnsub();
133
+ unmountCurrent();
134
+ }
133
135
  };
134
136
  },
135
137
  {
@@ -311,4 +313,4 @@ export {
311
313
  pubsub,
312
314
  verifiable
313
315
  };
314
- //# sourceMappingURL=chunk-YJ4U2D2C.js.map
316
+ //# sourceMappingURL=chunk-446I4EGD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/base/composition/materialize.ts","../src/base/composition/pubsub.ts","../src/base/composition/verifiable.ts"],"sourcesContent":["/**\n * Phase 13.C — `selector` + `materialize` composers (DS-13.C / G2 lock C).\n *\n * Two paired primitives for dynamic-mount routing:\n *\n * - {@link selector} — projects an input value to a routing key, deduped.\n * Equivalent to `map + distinctUntilChanged`, but the dedup is the\n * semantic point: the key changes ONLY when the routed-to slot should\n * change. Fires `materialize` re-mounts efficiently.\n *\n * - {@link materialize} — given a reactive `key` and a reactive map of\n * `key → factory` thunks, mounts the matching factory's Graph under\n * `parent` at a stable slot name. When `key` changes, unmounts the old\n * slot and mounts the new factory's output. When `factories` mutates but\n * `key` stays the same, the current slot continues to run on the OLD\n * factory (\"current sessions complete on old factory; new sessions use\n * new factory\" — full hot-swap correctness deferred to G10, parked).\n *\n * Reusable beyond the agent layer:\n * - `harnessLoop` strategy routing — the strategy node IS a `selector`.\n * - `pipelineGraph` dynamic stage selection.\n * - `refineLoop` strategy swap.\n * - Phase 13.I `spawnable()` mounts agent slots via `materialize`.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\tfactoryTag,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n\tRESOLVED,\n} from \"@graphrefly/pure-ts/core\";\nimport type { Graph } from \"@graphrefly/pure-ts/graph\";\n\n/** Options for operator nodes: NodeOptions without `describeKind` (set internally). */\nexport type ExtraOpts = Omit<NodeOptions<unknown>, \"describeKind\">;\n\nfunction operatorOpts<T = unknown>(opts?: ExtraOpts): NodeOptions<T> {\n\treturn { describeKind: \"derived\", ...opts } as NodeOptions<T>;\n}\n\n// ---------------------------------------------------------------------------\n// selector\n// ---------------------------------------------------------------------------\n\n/** Options for {@link selector}. */\nexport type SelectorOpts<TKey> = ExtraOpts & {\n\t/**\n\t * Equality comparator for the projected key. Defaults to {@link Object.is}.\n\t * Used to suppress re-emits when the input changes but the projected key\n\t * does not — this is the load-bearing behavior that lets downstream\n\t * `materialize` skip unnecessary unmount/remount cycles.\n\t */\n\tequals?: (a: TKey, b: TKey) => boolean;\n};\n\n/**\n * Projects each upstream value to a routing key, deduped on the key. The\n * output node emits a key only when the projected key actually changes —\n * pairs cleanly with {@link materialize}, which re-mounts only on key\n * change.\n *\n * **Differs from `map`:** `map(input, fn)` fires on every upstream wave\n * regardless of output value. `selector(input, fn)` fires only when the\n * projected key CHANGES (under `equals`), so downstream re-mount logic is\n * stable.\n *\n * @param input - Upstream node carrying the value to project.\n * @param fn - Synchronous projection function.\n * @param opts - Optional {@link SelectorOpts}.\n * @returns `Node<TKey>` carrying the latest projected key (deduped).\n *\n * @example\n * ```ts\n * import { selector, materialize, state } from \"@graphrefly/graphrefly-ts\";\n *\n * type Request = { kind: \"research\" | \"summarize\" | \"code\"; payload: unknown };\n * const requestNode = state<Request>({ kind: \"research\", payload: {} });\n *\n * const presetId = selector(requestNode, (req) => req.kind);\n * // presetId is `Node<\"research\" | \"summarize\" | \"code\">`, deduped.\n * // Downstream materialize re-mounts ONLY when the kind axis changes.\n * ```\n *\n * @category extra\n */\nexport function selector<TIn, TKey>(\n\tinput: Node<TIn>,\n\tfn: (input: TIn) => TKey,\n\topts?: SelectorOpts<TKey>,\n): Node<TKey> {\n\tconst equals = opts?.equals ?? Object.is;\n\t// Lock 6.D (Phase 13.6.B): clear prev/hasPrev on deactivation so a\n\t// resubscribable selector doesn't dedupe the next cycle's first\n\t// projected key against a stale prev from the prior cycle.\n\tlet cleanup: { onDeactivation: () => void } | undefined;\n\treturn node<TKey>(\n\t\t[input as Node],\n\t\t(data, a, ctx) => {\n\t\t\tif (cleanup === undefined) {\n\t\t\t\tconst store = ctx.store;\n\t\t\t\tcleanup = {\n\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\tdelete store.prev;\n\t\t\t\t\t\tdelete store.hasPrev;\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst batch0 = data[0];\n\t\t\tif (batch0 == null || batch0.length === 0) {\n\t\t\t\ta.down([[RESOLVED]]);\n\t\t\t\treturn cleanup;\n\t\t\t}\n\t\t\t// A11 (QA fix 2026-05-01): pre-pass — compute every projected\n\t\t\t// key + dedup decision FIRST, surface any user `equals` throw\n\t\t\t// as ERROR before any DATA goes out. The previous in-loop\n\t\t\t// emission interleaved partial DATA with ERROR mid-batch; that\n\t\t\t// left subscribers inconsistent (some had read the early DATA,\n\t\t\t// some treated ERROR as \"discard everything since RESOLVED\")\n\t\t\t// and left `ctx.store.prev` mutated to the last successful key,\n\t\t\t// which made selector \"stuck\" until the next throw-free batch.\n\t\t\tconst toEmit: TKey[] = [];\n\t\t\tlet prev: TKey | undefined = ctx.store.hasPrev ? (ctx.store.prev as TKey) : undefined;\n\t\t\tlet hasPrev = ctx.store.hasPrev;\n\t\t\tfor (const v of batch0 as TIn[]) {\n\t\t\t\tconst key = fn(v);\n\t\t\t\tif (hasPrev) {\n\t\t\t\t\tlet same: boolean;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsame = equals(prev as TKey, key);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t// Pre-pass throw — abandon the whole batch (no DATA emits)\n\t\t\t\t\t\t// and surface ERROR. ctx.store stays at its pre-batch\n\t\t\t\t\t\t// state so the next batch starts from a known-good prev.\n\t\t\t\t\t\ta.down([[ERROR, err]]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (same) continue;\n\t\t\t\t}\n\t\t\t\tprev = key;\n\t\t\t\thasPrev = true;\n\t\t\t\ttoEmit.push(key);\n\t\t\t}\n\t\t\tif (toEmit.length === 0) {\n\t\t\t\ta.down([[RESOLVED]]);\n\t\t\t\treturn cleanup;\n\t\t\t}\n\t\t\tctx.store.prev = prev as TKey;\n\t\t\tctx.store.hasPrev = true;\n\t\t\tfor (const k of toEmit) a.emit(k);\n\t\t\treturn cleanup;\n\t\t},\n\t\t{\n\t\t\t...operatorOpts(opts),\n\t\t\tmeta: { ...factoryTag(\"selector\"), ...(opts?.meta ?? {}) },\n\t\t},\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// materialize\n// ---------------------------------------------------------------------------\n\n/**\n * Factory thunk for a {@link materialize} slot. Called once per mount cycle\n * to mint a fresh `TGraph` instance. The instance is mounted under\n * `parent.mount(slotName, ...)` and unmounted via `parent.remove(slotName)`\n * when `key` next changes.\n *\n * Each invocation MUST return a fresh, never-before-mounted Graph instance —\n * `Graph.mount` rejects re-mounting an instance that is already mounted\n * elsewhere in the tree. Caching factory output is unsafe.\n */\nexport type GraphFactory<TGraph extends Graph> = () => TGraph;\n\n/** Options for {@link materialize}. */\nexport type MaterializeOpts = ExtraOpts & {\n\t/**\n\t * Local mount name on the parent graph. Default `\"materialized\"`.\n\t *\n\t * Two materialize calls on the SAME parent must use distinct `slotName`\n\t * values, otherwise the second mount throws \"mount already exists\".\n\t * For a hub mounting many slots (e.g. {@link spawnable}'s preset\n\t * registry), use `slotName: \\`preset-\\${id}\\`` or similar.\n\t */\n\tslotName?: string;\n};\n\n/**\n * Reactive dynamic mount: mounts the Graph instance for `key` under\n * `parent.mount(slotName, ...)`, and re-mounts when `key` changes.\n *\n * **Lifecycle.** First DATA on `key` triggers a mount: look up\n * `factories.get(key)`, call the factory thunk, mount the result under\n * `parent`. Each subsequent `key` change unmounts the previous slot and\n * mounts the new one. When this materialize node terminates (subscriber\n * teardown, `COMPLETE` from `key`, `Graph.destroy`), the active slot is\n * unmounted via `parent.remove(slotName)`.\n *\n * **Hot-swap policy (G10 deferred).** When `factories` mutates but `key`\n * stays the same, the currently-mounted slot is NOT re-instantiated —\n * \"current sessions complete on old factory; new sessions use new\n * factory.\" Atomic disconnect/reconnect of an in-flight slot to a new\n * factory is parked under G10 (see `optimizations.md` \"G10 atomic\n * registry hot-swap\").\n *\n * **Output.** The returned `Node<TGraph>` emits the currently-mounted\n * Graph reference whenever a mount occurs. Consumers can subscribe to\n * watch slot changes, or read `.cache` for the active mount. SENTINEL\n * (no DATA) when no slot is currently mounted (e.g. `key` has no matching\n * factory).\n *\n * **Spec compliance.**\n * - No polling: mount transitions are reactive on `key` / `factories`.\n * - No raw async: factory invocation is synchronous; if a factory needs\n * async setup, it returns a Graph that handles its own setup internally.\n * - Mount/unmount happens as side-effects inside the reactive `subscribe`\n * handler — sanctioned per spec §5.9 (Graph topology mutations are\n * imperative writes at the system boundary, not in-flight reactive\n * triggers).\n *\n * @param key - Reactive routing key. Re-mounts on each key change (deduped\n * by reference; pair with {@link selector} for projection-based dedup).\n * @param factories - Reactive map of `key → factory thunk`. Factory map\n * mutations don't disturb the active slot until the next key change.\n * @param parent - Graph to mount slots under. The `slotName` (default\n * `\"materialized\"`) must be free on `parent` at construction time.\n * @param opts - Optional {@link MaterializeOpts}.\n * @returns `Node<TGraph>` carrying the active mount.\n *\n * @example\n * ```ts\n * import { materialize, selector, state, Graph } from \"@graphrefly/graphrefly-ts\";\n *\n * const parent = new Graph(\"parent\");\n * const factories = state<ReadonlyMap<string, () => Graph>>(new Map([\n * [\"researcher\", () => new ResearchAgentGraph()],\n * [\"coder\", () => new CoderAgentGraph()],\n * ]));\n * const key = selector(requestNode, (r) => r.kind);\n * const slot = materialize(key, factories, parent, { slotName: \"agent\" });\n * // `slot.cache` is the active agent graph; `parent.node(\"agent::out\")`\n * // resolves into whichever agent is currently mounted.\n * ```\n *\n * @category extra\n */\nexport function materialize<TKey, TGraph extends Graph>(\n\tkey: Node<TKey>,\n\tfactories: Node<ReadonlyMap<TKey, GraphFactory<TGraph>>>,\n\tparent: Graph,\n\topts?: MaterializeOpts,\n): Node<TGraph> {\n\tconst slotName = opts?.slotName ?? \"materialized\";\n\treturn node<TGraph>(\n\t\t(_data, a) => {\n\t\t\tlet currentKey: TKey | undefined;\n\t\t\tlet hasCurrentKey = false;\n\t\t\tlet currentGraph: TGraph | undefined;\n\t\t\tlet latestFactories: ReadonlyMap<TKey, GraphFactory<TGraph>> | undefined;\n\t\t\tlet terminated = false;\n\n\t\t\tfunction unmountCurrent(): void {\n\t\t\t\tif (currentGraph === undefined) return;\n\t\t\t\ttry {\n\t\t\t\t\tparent.remove(slotName);\n\t\t\t\t} catch {\n\t\t\t\t\t// Slot already gone (parent destroyed, or external `remove`).\n\t\t\t\t}\n\t\t\t\tcurrentGraph = undefined;\n\t\t\t}\n\n\t\t\t// Closure mirror for the factories map. Subscribed FIRST so\n\t\t\t// `latestFactories` is populated by the time the first `key` DATA\n\t\t\t// arrives. Same §28 factory-time-seed pattern used elsewhere.\n\t\t\tconst facUnsub = factories.subscribe((msgs) => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\t\tlatestFactories = m[1] as ReadonlyMap<TKey, GraphFactory<TGraph>>;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Primary trigger: key DATA drives mount transitions.\n\t\t\tconst keyUnsub = key.subscribe((msgs) => {\n\t\t\t\tif (terminated) return;\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\t\tconst newKey = m[1] as TKey;\n\t\t\t\t\t\tconst keyChanged = !hasCurrentKey || newKey !== currentKey;\n\t\t\t\t\t\tif (keyChanged) {\n\t\t\t\t\t\t\tunmountCurrent();\n\t\t\t\t\t\t\tif (latestFactories !== undefined) {\n\t\t\t\t\t\t\t\tconst factory = latestFactories.get(newKey);\n\t\t\t\t\t\t\t\tif (factory !== undefined) {\n\t\t\t\t\t\t\t\t\tcurrentGraph = factory();\n\t\t\t\t\t\t\t\t\tparent.mount(slotName, currentGraph);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcurrentKey = newKey;\n\t\t\t\t\t\t\thasCurrentKey = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (currentGraph !== undefined) {\n\t\t\t\t\t\t\ta.emit(currentGraph);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ta.down([[RESOLVED]]);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (m[0] === COMPLETE) {\n\t\t\t\t\t\tterminated = true;\n\t\t\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t\t\t} else if (m[0] === ERROR) {\n\t\t\t\t\t\tterminated = true;\n\t\t\t\t\t\ta.down([m]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tonDeactivation: () => {\n\t\t\t\t\tterminated = true;\n\t\t\t\t\tkeyUnsub();\n\t\t\t\t\tfacUnsub();\n\t\t\t\t\tunmountCurrent();\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\t{\n\t\t\t...operatorOpts(opts),\n\t\t\tmeta: { ...factoryTag(\"materialize\"), slotName, ...(opts?.meta ?? {}) },\n\t\t},\n\t);\n}\n","/**\n * Lazy per-topic state hub (roadmap §3.2) — lightweight last-value broadcasts.\n *\n * Each topic is a sentinel `node<unknown>()` with push-on-subscribe replay of\n * the most recent published value (no push until the first `publish`). For\n * Pulsar-inspired retained message logs,\n * cursor-based subscriptions, and job-queue semantics, use `messagingHub()` in\n * `utils/messaging` — built on `TopicGraph` / `SubscriptionGraph` with\n * retention policies, absolute cursor tracking, and per-subscriber state.\n *\n * Presentation layer (base/composition). Moved from pure-ts during cleave A3\n * (no substrate core/graph dependency on pubsub found).\n */\n\nimport { batch, type Node, node, TEARDOWN, wallClockNs } from \"@graphrefly/pure-ts/core\";\nimport {\n\ttype PubSubChange,\n\ttype PubSubChangePayload,\n\ttype ReactiveLogBundle,\n\treactiveLog,\n} from \"@graphrefly/pure-ts/extra\";\n\n// ── Backend interface ─────────────────────────────────────────────────────\n\n/**\n * Storage contract for {@link pubsub} — registry only.\n *\n * Tracks the set of topic names plus a monotonic `version` counter that\n * advances on topic create/remove. Does NOT own per-topic message storage —\n * per-topic cached last values live in the topic nodes themselves (sentinel\n * until the first publish).\n *\n * For distributed / persistent per-topic storage, use `messagingHub()` in\n * `utils/messaging`, which composes `TopicGraph` under a lazy registry.\n *\n * @category base\n */\nexport interface PubSubBackend {\n\t/** Monotonic counter; advances on topic create/remove. */\n\treadonly version: number;\n\treadonly topicCount: number;\n\thasTopic(name: string): boolean;\n\ttopicNames(): IterableIterator<string>;\n\t/** Records topic creation. Returns `true` if newly added (advances `version`). */\n\tcreateTopic(name: string): boolean;\n\t/** Records topic removal. Returns `true` if it existed (advances `version`). */\n\tremoveTopic(name: string): boolean;\n}\n\n/**\n * Default in-memory registry backend.\n *\n * @category base\n */\nexport class NativePubSubBackend implements PubSubBackend {\n\tprivate _version = 0;\n\tprivate readonly _topics = new Set<string>();\n\n\tget version(): number {\n\t\treturn this._version;\n\t}\n\n\tget topicCount(): number {\n\t\treturn this._topics.size;\n\t}\n\n\thasTopic(name: string): boolean {\n\t\treturn this._topics.has(name);\n\t}\n\n\ttopicNames(): IterableIterator<string> {\n\t\treturn this._topics.values();\n\t}\n\n\tcreateTopic(name: string): boolean {\n\t\tif (this._topics.has(name)) return false;\n\t\tthis._topics.add(name);\n\t\tthis._version += 1;\n\t\treturn true;\n\t}\n\n\tremoveTopic(name: string): boolean {\n\t\tconst had = this._topics.delete(name);\n\t\tif (had) this._version += 1;\n\t\treturn had;\n\t}\n}\n\n// ── Hub ───────────────────────────────────────────────────────────────────\n\nexport type PubSubHubOptions = {\n\t/**\n\t * Storage backend. Defaults to `NativePubSubBackend`. Pluggable for audit /\n\t * monitoring / mirror-to-external-broker use cases.\n\t */\n\tbackend?: PubSubBackend;\n\t/**\n\t * DS-14 / DS14R2 — opt-in delta companion. When set, the hub appends a\n\t * typed {@link PubSubChange} record in the **same batch frame** as the\n\t * topic emission / teardown (same-wave consistency — subscribers reading\n\t * both a topic and `mutationLog` never see torn state).\n\t *\n\t * Records the locked `PubSubChange` verbs that apply to this last-value\n\t * hub: `publish` (per `publish` / `publishMany`) and `remove` (per\n\t * `removeTopic`). The `ack` verb is a cursor concern of `messagingHub()`\n\t * and does not apply here. **Note:** the locked `publish` payload carries\n\t * `value` only (no topic name) — callers needing per-topic delta\n\t * correlation should embed identity in the value or use `messagingHub()`.\n\t *\n\t * `true` = defaults; object form forwards `maxSize` / `name` to the inner\n\t * `reactiveLog`.\n\t */\n\tmutationLog?: true | { maxSize?: number; name?: string };\n};\n\n/**\n * Lazy per-topic state hub. Topics are single-value sentinel nodes\n * with push-on-subscribe replay of the most recent publish.\n *\n * @category base\n */\nexport interface PubSubHub {\n\t/**\n\t * Returns the topic node, creating it on first use.\n\t *\n\t * @param name - Topic key.\n\t * @returns `Node` whose value is the last published payload. Starts in\n\t * sentinel state — no push-on-subscribe until the first publish.\n\t */\n\ttopic(name: string): Node<unknown>;\n\t/** Publishes a value to the topic (lazily creating the topic if missing). */\n\tpublish(name: string, value: unknown): void;\n\t/**\n\t * Bulk publish — single outer batch for all entries. No-op if empty.\n\t *\n\t * **Iterable consumption (F6):** `entries` is consumed once (single-pass).\n\t * Pass an array or `Set` for multi-shot callers. Iteration happens INSIDE\n\t * the batch frame — if the iterator throws mid-way, the batch is discarded\n\t * and NO publishes are visible to subscribers (all-or-nothing within one\n\t * call).\n\t */\n\tpublishMany(entries: Iterable<[string, unknown]>): void;\n\t/** Removes a topic; sends `TEARDOWN` to its node. Returns `true` if it existed. */\n\tremoveTopic(name: string): boolean;\n\t/** Checks topic existence without creating. O(1). */\n\thas(name: string): boolean;\n\t/** Number of topics currently registered. O(1). */\n\treadonly size: number;\n\t/** Iterator over topic names. */\n\ttopicNames(): IterableIterator<string>;\n\t/**\n\t * DS14R2 — present iff `mutationLog` was configured. Append-only log of\n\t * `publish` / `remove` deltas, same-wave-consistent with topic emissions.\n\t */\n\treadonly mutationLog?: ReactiveLogBundle<PubSubChange>;\n}\n\n/**\n * Creates a lazy per-topic state hub.\n *\n * @param options - Optional pluggable `backend` (defaults to `NativePubSubBackend`).\n * @returns Hub with lazy `topic()` / `publish()` / `publishMany()` / `removeTopic()` /\n * `has()` / `size` / `topicNames()`.\n *\n * @remarks\n * **Scope:** Each topic is a sentinel node — retains only the last published\n * value (no push-on-subscribe before the first publish). For Pulsar-inspired\n * retention + cursor reading, use `messagingHub()` in `utils/messaging`.\n *\n * **`removeTopic`:** Sends `TEARDOWN` to the topic node; all subscribers receive\n * the TEARDOWN message. Subsequent `publish(name, value)` silently recreates the\n * topic with a fresh node — existing subscribers to the old node do NOT reconnect.\n *\n * @example\n * ```ts\n * import { pubsub } from \"@graphrefly/graphrefly\";\n *\n * const hub = pubsub();\n * const t = hub.topic(\"events\");\n * t.subscribe((msgs) => console.log(msgs));\n * hub.publish(\"events\", { ok: true });\n * hub.publishMany([[\"events\", 1], [\"status\", \"ready\"]]);\n * ```\n *\n * @category base\n */\nexport function pubsub(options: PubSubHubOptions = {}): PubSubHub {\n\tconst { backend: userBackend, mutationLog: mutLogOpt } = options;\n\tconst backend: PubSubBackend = userBackend ?? new NativePubSubBackend();\n\tconst nodes = new Map<string, Node<unknown>>();\n\n\t// ── DS14R2 — mutation log companion ──────────────────────────────────────\n\tconst mutLog: ReactiveLogBundle<PubSubChange> | undefined = mutLogOpt\n\t\t? reactiveLog<PubSubChange>(undefined, {\n\t\t\t\tname: mutLogOpt === true ? \"pubsub.mutationLog\" : (mutLogOpt.name ?? \"pubsub.mutationLog\"),\n\t\t\t\tmaxSize: mutLogOpt === true ? undefined : mutLogOpt.maxSize,\n\t\t\t})\n\t\t: undefined;\n\tlet mutVersion = 0;\n\tfunction recordChange(change: PubSubChangePayload): void {\n\t\tif (!mutLog) return;\n\t\tmutLog.append({\n\t\t\tstructure: \"pubsub\",\n\t\t\tversion: ++mutVersion,\n\t\t\tt_ns: wallClockNs(),\n\t\t\tlifecycle: \"data\",\n\t\t\tchange,\n\t\t});\n\t}\n\n\tfunction ensureTopic(name: string): Node<unknown> {\n\t\tlet n = nodes.get(name);\n\t\tif (n === undefined) {\n\t\t\tn = node<unknown>({ describeKind: \"state\" });\n\t\t\tnodes.set(name, n);\n\t\t\tbackend.createTopic(name);\n\t\t}\n\t\treturn n;\n\t}\n\n\treturn {\n\t\ttopic(name: string): Node<unknown> {\n\t\t\treturn ensureTopic(name);\n\t\t},\n\n\t\tpublish(name: string, value: unknown): void {\n\t\t\tif (!mutLog) {\n\t\t\t\tensureTopic(name).emit(value);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Same-wave: topic emit + change record in one batch frame.\n\t\t\tbatch(() => {\n\t\t\t\tensureTopic(name).emit(value);\n\t\t\t\trecordChange({ kind: \"publish\", value });\n\t\t\t});\n\t\t},\n\n\t\tpublishMany(entries: Iterable<[string, unknown]>): void {\n\t\t\tbatch(() => {\n\t\t\t\tfor (const [name, value] of entries) {\n\t\t\t\t\tensureTopic(name).emit(value);\n\t\t\t\t\trecordChange({ kind: \"publish\", value });\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tremoveTopic(name: string): boolean {\n\t\t\tconst n = nodes.get(name);\n\t\t\tif (n === undefined) return false;\n\t\t\tnodes.delete(name);\n\t\t\tbackend.removeTopic(name);\n\t\t\tif (!mutLog) {\n\t\t\t\tn.down([[TEARDOWN]]);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbatch(() => {\n\t\t\t\t// QA P3: record BEFORE TEARDOWN. A subscriber wired to both a\n\t\t\t\t// topic and `mutationLog` that self-detaches on TEARDOWN would\n\t\t\t\t// otherwise miss the `remove` delta (same-wave consistency).\n\t\t\t\trecordChange({ kind: \"remove\", name });\n\t\t\t\tn.down([[TEARDOWN]]);\n\t\t\t});\n\t\t\treturn true;\n\t\t},\n\n\t\thas(name: string): boolean {\n\t\t\treturn backend.hasTopic(name);\n\t\t},\n\n\t\tget size(): number {\n\t\t\treturn backend.topicCount;\n\t\t},\n\n\t\ttopicNames(): IterableIterator<string> {\n\t\t\treturn backend.topicNames();\n\t\t},\n\n\t\tmutationLog: mutLog,\n\t};\n}\n","/**\n * Composite data patterns (roadmap §3.2b).\n *\n * These helpers compose existing primitives (`node`, `switchMap`, `reactiveMap`,\n * `dynamicNode`, `fromAny`) without introducing new protocol semantics.\n */\n\nimport {\n\tbatch,\n\tDATA,\n\tfactoryTag,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n} from \"@graphrefly/pure-ts/core\";\nimport {\n\tfromAny,\n\tmerge,\n\ttype NodeInput,\n\tswitchMap,\n\twithLatestFrom,\n} from \"@graphrefly/pure-ts/extra\";\nimport { forEach } from \"../sources/async.js\";\n\n// Re-export distill from its canonical module (co-located here pre-split;\n// moved to distill.ts to avoid duplicate-export conflict at the barrel level).\nexport {\n\ttype DistillBundle,\n\ttype DistillOptions,\n\tdistill,\n\ttype Extraction,\n} from \"./distill.js\";\n\n/**\n * Verification payload shape is intentionally user-defined.\n */\nexport type VerifyValue = unknown;\n\nexport type VerifiableOptions<TVerify = VerifyValue> = Omit<\n\tNodeOptions,\n\t\"describeKind\" | \"initial\"\n> & {\n\t/** Reactive re-verification trigger. */\n\ttrigger?: NodeInput<unknown>;\n\t/** Re-run verification whenever `source` settles. */\n\tautoVerify?: boolean;\n\t/** Initial verification companion value. */\n\tinitialVerified?: TVerify | null;\n};\n\nexport type VerifiableBundle<T, TVerify = VerifyValue> = {\n\t/** Coerced source node. */\n\tnode: Node<T>;\n\t/** Latest verification result (`null` before first verification). */\n\tverified: Node<TVerify | null>;\n\t/** Effective trigger node used for verification, if any. */\n\ttrigger: Node<unknown> | null;\n};\n\n/**\n * Composes a value node with a reactive verification companion.\n *\n * Uses `switchMap` so newer triggers cancel stale in-flight verification work.\n */\nexport function verifiable<T, TVerify = VerifyValue>(\n\tsource: NodeInput<T>,\n\tverifyFn: (value: T) => NodeInput<TVerify>,\n\topts?: VerifiableOptions<TVerify>,\n): VerifiableBundle<T, TVerify> {\n\tconst sourceNode = fromAny(source);\n\tconst hasSourceVersioning = sourceNode.v != null;\n\tconst verified = node<TVerify | null>([], {\n\t\tinitial: opts?.initialVerified ?? null,\n\t\tmeta: {\n\t\t\t...factoryTag(\"verifiable\"),\n\t\t\t...(hasSourceVersioning ? { sourceVersion: null } : {}),\n\t\t},\n\t});\n\tconst hasTrigger = opts?.trigger !== undefined && opts.trigger !== null;\n\n\tlet triggerNode: Node<unknown> | null = null;\n\tif (hasTrigger && opts?.autoVerify) {\n\t\ttriggerNode = merge(fromAny(opts.trigger) as Node<unknown>, sourceNode as Node<unknown>);\n\t} else if (hasTrigger) {\n\t\ttriggerNode = fromAny(opts.trigger);\n\t} else if (opts?.autoVerify) {\n\t\ttriggerNode = sourceNode as Node<unknown>;\n\t}\n\n\tif (triggerNode !== null) {\n\t\t// Two patterns depending on trigger shape:\n\t\t// - autoVerify-only (triggerNode === sourceNode): the projected\n\t\t// switchMap value IS the source DATA, pass it directly.\n\t\t// - explicit trigger: `withLatestFrom(trigger, source)` pairs each\n\t\t// trigger emission with the latest source value. Phase 10.5\n\t\t// (`withLatestFrom` flipped to `partial: false`) fixed the W1\n\t\t// initial-pair drop — both deps settle before fn fires, so the\n\t\t// first trigger correctly pairs with the seeded source cache.\n\t\t// Replaces the §28 closure-mirror that was canonical pre-10.5.\n\t\tlet verifyStream: Node<TVerify>;\n\t\tif (triggerNode === (sourceNode as Node<unknown>)) {\n\t\t\tverifyStream = switchMap(sourceNode, (src) => verifyFn(src as T));\n\t\t} else {\n\t\t\tconst paired = withLatestFrom(triggerNode, sourceNode);\n\t\t\tverifyStream = switchMap(paired, ([, source]) => verifyFn(source as T));\n\t\t}\n\t\tforEach(verifyStream, (value) => {\n\t\t\tbatch(() => {\n\t\t\t\tverified.down([[DATA, value]]);\n\t\t\t\t// V0 backfill: stamp which source version was verified (§6.0b).\n\t\t\t\tif (hasSourceVersioning) {\n\t\t\t\t\tconst sv = sourceNode.v;\n\t\t\t\t\tif (sv != null) {\n\t\t\t\t\t\tverified.meta.sourceVersion.down([[DATA, { id: sv.id, version: sv.version }]]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\treturn { node: sourceNode, verified, trigger: triggerNode };\n}\n"],"mappings":"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","names":["node","batch","DATA","factoryTag","node","node","factoryTag","source","batch","DATA"]}
@@ -43,9 +43,11 @@ function defer(thunk, opts) {
43
43
  const safe = err === void 0 ? new Error("defer: thunk threw undefined") : err;
44
44
  a.down([[ERROR, safe]]);
45
45
  }
46
- return () => {
47
- stopped = true;
48
- unsub?.();
46
+ return {
47
+ onDeactivation: () => {
48
+ stopped = true;
49
+ unsub?.();
50
+ }
49
51
  };
50
52
  }, merged);
51
53
  }
@@ -101,8 +103,10 @@ function toArray(source, opts) {
101
103
  }
102
104
  function share(source, opts) {
103
105
  return node(
104
- (_data, a) => source.subscribe((msgs) => {
105
- a.down(msgs);
106
+ (_data, a) => ({
107
+ onDeactivation: source.subscribe((msgs) => {
108
+ a.down(msgs);
109
+ })
106
110
  }),
107
111
  { ...sourceOpts(opts), initial: source.cache }
108
112
  );
@@ -110,8 +114,10 @@ function share(source, opts) {
110
114
  function replay(source, bufferSize, opts) {
111
115
  if (bufferSize < 1) throw new RangeError("replay expects bufferSize >= 1");
112
116
  return node(
113
- (_data, a) => source.subscribe((msgs) => {
114
- a.down(msgs);
117
+ (_data, a) => ({
118
+ onDeactivation: source.subscribe((msgs) => {
119
+ a.down(msgs);
120
+ })
115
121
  }),
116
122
  { ...sourceOpts(opts), initial: source.cache, replayBuffer: bufferSize }
117
123
  );
@@ -130,4 +136,4 @@ export {
130
136
  cached,
131
137
  shareReplay
132
138
  };
133
- //# sourceMappingURL=chunk-DKNHAICT.js.map
139
+ //# sourceMappingURL=chunk-5GVURVIG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/base/sources/async.ts"],"sourcesContent":["/**\n * Async sources, sinks, and multicast — presentation layer.\n *\n * `fromPromise`, `fromAsyncIter`, `fromAny` are substrate primitives; they are\n * re-exported here from `@graphrefly/pure-ts` for ergonomic single-import use.\n * This file owns the presentation-only async utilities: `defer`, `forEach`,\n * `toArray`, `share`, `replay`, `cached`, `shareReplay`.\n *\n * `singleFromAny` and `singleNodeFromAny` (keyed singleflight) live in\n * `base/composition/single-from-any.ts`.\n */\n\nimport {\n\tCOMPLETE,\n\tDATA,\n\tERROR,\n\ttype Node,\n\ttype NodeOptions,\n\tnode,\n\tRESOLVED,\n\tSTART,\n} from \"@graphrefly/pure-ts/core\";\nimport { type AsyncSourceOpts, type NodeInput, sourceOpts } from \"@graphrefly/pure-ts/extra\";\n\n/** Options for presentation-layer async operators: NodeOptions without `describeKind`. */\ntype ExtraOpts = Omit<NodeOptions, \"describeKind\">;\n\n// Import fromAny from substrate — used internally by defer. The three async\n// substrate sources (fromAny, fromAsyncIter, fromPromise) are already\n// re-exported from @graphrefly/pure-ts; do NOT re-export here to avoid\n// duplicate-export conflicts at the root barrel level.\nimport { fromAny } from \"@graphrefly/pure-ts/extra\";\n\n/**\n * Lazily constructs a {@link Node} from a thunk that runs at **activation\n * time** (first subscriber after a teardown to zero sinks), not factory time.\n *\n * **Resubscribable by default.** Diverges from `fromPromise` / `fromIter` /\n * `fromAsyncIter` (which are single-shot — second subscriber sees the cached\n * terminal value). `defer`'s contract matches RxJS `defer`: every fresh\n * activation cycle re-runs the thunk. To opt out and get one-shot semantics,\n * pass `{ resubscribable: false }`.\n *\n * **Sharing across overlapping subscribers.** The thunk only re-runs on a\n * fresh activation cycle (zero → one sink). Overlapping subscribers share\n * the single activation; the thunk does NOT re-run for each subscriber. If\n * the thunk returns an existing `Node`, that Node is shared across activations\n * — `defer` will subscribe to it on each activation but does not isolate state\n * across subscribers. For per-subscriber isolation, the thunk must construct\n * a fresh source (`state(...)`, `fromPromise(fetch(...))`, etc.) on each call.\n *\n * **Use cases:**\n * - Lazy upstream construction (avoid eager evaluation of expensive factories\n * at module load — the thunk runs only when something subscribes).\n * - Per-activation resource construction (open a connection / file handle on\n * subscribe, when paired with full teardown between sessions).\n * - Bridging non-Node inputs (Promise, AsyncIterable, Iterable, scalar) into\n * the graph behind a lazy boundary.\n *\n * The thunk's return value is bridged via {@link fromAny}. Errors thrown by\n * the thunk surface as a single `[[ERROR, err]]` on the output (with `err`\n * coerced to a non-`undefined` value to satisfy spec §1.3 — bare `throw` and\n * `throw undefined` are wrapped in a `defer: thunk threw undefined` Error).\n *\n * Upstream messages are forwarded transparently (DIRTY / DATA / RESOLVED /\n * COMPLETE / ERROR / INVALIDATE / PAUSE / RESUME / TEARDOWN), preserving\n * batch boundaries. The producer's own `START` handshake is delivered to\n * subscribers automatically; the upstream's `START` is filtered.\n *\n * @param thunk - Called on each activation; returns the upstream input.\n * @param opts - Forwarded to `fromAny` (e.g. `signal` for async inputs).\n * `signal` is only consumed by `fromAny` for async input shapes (Promise,\n * AsyncIterable); it does NOT abort a Node-input or scalar-input defer.\n * @returns `Node<T>` — lazy upstream-on-activation.\n *\n * @example\n * ```ts\n * import { defer } from \"@graphrefly/graphrefly-ts\";\n *\n * // Lazy fetch — runs on the first activation, NOT at factory time.\n * // Each fresh activation cycle (after teardown) re-runs the thunk →\n * // a new fetch. Overlapping subscribers share the single activation.\n * const live = defer(() => fetch(\"/api/feed\").then((r) => r.json()));\n * ```\n *\n * @category extra\n */\nexport function defer<T>(thunk: () => NodeInput<T>, opts?: AsyncSourceOpts): Node<T> {\n\t// A4: strip `signal` before forwarding to NodeOptions — sibling sources\n\t// (fromTimer / fromPromise / fromAsyncIter) destructure first; signal\n\t// continues to flow into fromAny(input, opts) for async input shapes.\n\tconst { signal: _sig, ...nodeOpts } = (opts ?? {}) as AsyncSourceOpts;\n\tconst sOpts = sourceOpts<T>(nodeOpts);\n\tconst merged = sOpts.resubscribable === undefined ? { ...sOpts, resubscribable: true } : sOpts;\n\treturn node<T>((_data, a) => {\n\t\tlet unsub: (() => void) | undefined;\n\t\tlet stopped = false;\n\t\ttry {\n\t\t\tconst input = thunk();\n\t\t\t// `iter: true` preserves defer's RxJS-aligned per-element\n\t\t\t// streaming for sync iterable thunk returns (post DS-13.5\n\t\t\t// fromAny default flip; defer's documented contract is\n\t\t\t// \"forwards iterable values\" per-element).\n\t\t\tconst src = fromAny(input, { ...opts, iter: true });\n\t\t\tunsub = src.subscribe((msgs) => {\n\t\t\t\tif (stopped) return;\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tconst t = m[0];\n\t\t\t\t\tif (t === START) continue; // producer's own START is delivered separately\n\t\t\t\t\tif (t === DATA) {\n\t\t\t\t\t\ta.emit(m[1] as T);\n\t\t\t\t\t} else if (t === COMPLETE) {\n\t\t\t\t\t\tstopped = true;\n\t\t\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t\t\t\tbreak; // A2: don't forward post-terminal messages in the same batch\n\t\t\t\t\t} else if (t === ERROR) {\n\t\t\t\t\t\tstopped = true;\n\t\t\t\t\t\ta.down([[ERROR, m[1]]]);\n\t\t\t\t\t\tbreak; // A2\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Forward DIRTY / RESOLVED / INVALIDATE / PAUSE / RESUME /\n\t\t\t\t\t\t// TEARDOWN, plus any unknown types (spec §1.3.6 forward-compat).\n\t\t\t\t\t\ta.down([m]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (err) {\n\t\t\t// A5: spec §1.3 — ERROR payload must not be undefined. Wrap a\n\t\t\t// `throw` or `throw undefined` so dispatch doesn't reject the emit.\n\t\t\tconst safe = err === undefined ? new Error(\"defer: thunk threw undefined\") : err;\n\t\t\ta.down([[ERROR, safe]]);\n\t\t}\n\t\treturn {\n\t\t\tonDeactivation: () => {\n\t\t\t\tstopped = true;\n\t\t\t\tunsub?.();\n\t\t\t},\n\t\t};\n\t}, merged);\n}\n\n/**\n * Subscribes immediately and runs `fn` for each upstream `DATA`; returns unsubscribe.\n *\n * @param source - Upstream node.\n * @param fn - Side effect per value.\n * @param opts - Effect node options.\n * @returns Unsubscribe function (idempotent).\n *\n * @example\n * ```ts\n * import { forEach, state } from \"@graphrefly/graphrefly-ts\";\n *\n * const u = forEach(state(1), (v) => console.log(v));\n * u();\n * ```\n *\n * @category extra\n */\nexport function forEach<T>(source: Node<T>, fn: (value: T) => void, opts?: ExtraOpts): () => void {\n\tconst inner = node(\n\t\t[source as Node],\n\t\t(data, _actions) => {\n\t\t\tconst batch0 = data[0];\n\t\t\tif (batch0 != null && batch0.length > 0) {\n\t\t\t\tfor (const v of batch0) fn(v as T);\n\t\t\t}\n\t\t},\n\t\t{ describeKind: \"effect\", ...opts } as NodeOptions,\n\t);\n\treturn inner.subscribe(() => {});\n}\n\n/**\n * Buffers every `DATA`; on upstream `COMPLETE` emits one `DATA` with the full array then `COMPLETE`.\n *\n * @param source - Upstream node.\n * @param opts - Optional node options (derived describe kind).\n * @returns `Node<T[]>` — single array emission before completion.\n *\n * @example\n * ```ts\n * import { of, toArray } from \"@graphrefly/graphrefly-ts\";\n *\n * toArray(of(1, 2, 3));\n * ```\n *\n * @category extra\n */\nexport function toArray<T>(source: Node<T>, opts?: ExtraOpts): Node<T[]> {\n\t// Lock 6.D (Phase 13.6.B): clear the accumulator buffer on\n\t// deactivation so a resubscribable toArray restarts with an empty\n\t// array on the next cycle — pre-flip this came for free via\n\t// `_deactivate`'s store wipe.\n\tlet cleanup: { onDeactivation: () => void } | undefined;\n\treturn node<T[]>(\n\t\t[source as Node],\n\t\t(data, actions, ctx) => {\n\t\t\tif (cleanup === undefined) {\n\t\t\t\tconst store = ctx.store;\n\t\t\t\tcleanup = {\n\t\t\t\t\tonDeactivation: () => {\n\t\t\t\t\t\tdelete store.buf;\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (!ctx.store.buf) ctx.store.buf = [];\n\t\t\tconst buf = ctx.store.buf as T[];\n\t\t\t// Accumulate DATA first — must happen before the COMPLETE check so\n\t\t\t// that a same-wave DATA+COMPLETE batch (e.g. fromTimer one-shot,\n\t\t\t// fromIter last item) is included in the emitted array.\n\t\t\tconst batch0 = data[0];\n\t\t\tif (batch0 != null && batch0.length > 0) {\n\t\t\t\tfor (const v of batch0) buf.push(v as T);\n\t\t\t}\n\t\t\t// COMPLETE: emit accumulated array then complete.\n\t\t\t// ERROR: autoError propagates; do NOT emit the partial buffer.\n\t\t\tif (ctx.terminalDeps[0] === true) {\n\t\t\t\tactions.emit([...buf]);\n\t\t\t\tactions.down([[COMPLETE]]);\n\t\t\t\treturn cleanup;\n\t\t\t}\n\t\t\t// RESOLVED wave: propagate RESOLVED. Covers first-wave case; after first\n\t\t\t// call the pre-fn skip handles this automatically.\n\t\t\tif (batch0 == null || batch0.length === 0) {\n\t\t\t\tactions.down([[RESOLVED]]);\n\t\t\t}\n\t\t\treturn cleanup;\n\t\t},\n\t\t{\n\t\t\tdescribeKind: \"derived\",\n\t\t\tcompleteWhenDepsComplete: false,\n\t\t\t...opts,\n\t\t} as NodeOptions<T[]>,\n\t);\n}\n\n/**\n * Multicasts upstream: one subscription to `source` while this wrapper has subscribers (via {@link producer}).\n *\n * @param source - Upstream node to share.\n * @param opts - Producer options; `initial` seeds from `source.cache` when set by factory.\n * @returns `Node<T>` — hot ref-counted bridge.\n *\n * @example\n * ```ts\n * import { share, state } from \"@graphrefly/graphrefly-ts\";\n *\n * share(state(0));\n * ```\n *\n * @category extra\n */\nexport function share<T>(source: Node<T>, opts?: ExtraOpts): Node<T> {\n\treturn node<T>(\n\t\t(_data, a) => ({\n\t\t\tonDeactivation: source.subscribe((msgs) => {\n\t\t\t\ta.down(msgs);\n\t\t\t}),\n\t\t}),\n\t\t{ ...sourceOpts<T>(opts), initial: source.cache },\n\t);\n}\n\n/**\n * Like {@link share} with a bounded replay buffer: new subscribers receive the last `bufferSize`\n * `DATA` payloads (as separate batches) before live updates.\n *\n * @param source - Upstream node.\n * @param bufferSize - Maximum past values to replay (≥ 1).\n * @param opts - Producer options.\n * @returns `Node<T>` — multicast with replay on subscribe.\n *\n * @example\n * ```ts\n * import { replay, state } from \"@graphrefly/graphrefly-ts\";\n *\n * replay(state(0), 3);\n * ```\n *\n * @category extra\n */\nexport function replay<T>(source: Node<T>, bufferSize: number, opts?: ExtraOpts): Node<T> {\n\tif (bufferSize < 1) throw new RangeError(\"replay expects bufferSize >= 1\");\n\t// Spec §2.5 / Lock 6.G: the built-in `replayBuffer` NodeOption retains the\n\t// last-N outgoing DATA and `defaultOnSubscribe` delivers them to a late\n\t// subscriber INSTEAD of the cache-DATA push — so there is no double-deliver\n\t// of the most-recent value. Supersedes the old `wrapSubscribeHook` +\n\t// manual-buffer pattern (which flushed the buffer AND then push-on-\n\t// subscribed the cache, double-delivering the last value).\n\treturn node<T>(\n\t\t(_data, a) => ({\n\t\t\tonDeactivation: source.subscribe((msgs) => {\n\t\t\t\ta.down(msgs);\n\t\t\t}),\n\t\t}),\n\t\t{ ...sourceOpts<T>(opts), initial: source.cache, replayBuffer: bufferSize },\n\t);\n}\n\n/**\n * {@link replay} with `bufferSize === 1` — replays the latest `DATA` to new subscribers.\n *\n * @param source - Upstream node.\n * @param opts - Producer options.\n * @returns `Node<T>` — share + last-value replay.\n *\n * @example\n * ```ts\n * import { cached, state } from \"@graphrefly/graphrefly-ts\";\n *\n * cached(state(0));\n * ```\n *\n * @category extra\n */\nexport function cached<T>(source: Node<T>, opts?: ExtraOpts): Node<T> {\n\treturn replay(source, 1, opts);\n}\n\n// ——————————————————————————————————————————————————————————————\n// RxJS-compatible aliases\n// ——————————————————————————————————————————————————————————————\n\n/**\n * RxJS-named alias for {@link replay} — multicast with a replay buffer of size `bufferSize`.\n *\n * @param source - Upstream node.\n * @param bufferSize - Replay depth (≥ 1).\n * @param opts - Producer options.\n * @returns Same behavior as `replay`.\n *\n * @example\n * ```ts\n * import { shareReplay, state } from \"@graphrefly/graphrefly-ts\";\n *\n * shareReplay(state(0), 5);\n * ```\n *\n * @category extra\n */\nexport const shareReplay = replay;\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  feedback
3
- } from "./chunk-IHTWQEDR.js";
3
+ } from "./chunk-QFE5BQH7.js";
4
4
 
5
5
  // src/utils/graphspec/index.ts
6
6
  import { node } from "@graphrefly/pure-ts/core";
@@ -1062,4 +1062,4 @@ export {
1062
1062
  llmCompose,
1063
1063
  llmRefine
1064
1064
  };
1065
- //# sourceMappingURL=chunk-2OB3CEJS.js.map
1065
+ //# sourceMappingURL=chunk-6MRSX3YK.js.map