@graphrefly/graphrefly 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/README.md +8 -0
  2. package/dist/{chunk-QOWVNWOC.js → chunk-3ZWCKRHX.js} +27 -25
  3. package/dist/{chunk-QOWVNWOC.js.map → chunk-3ZWCKRHX.js.map} +1 -1
  4. package/dist/chunk-6LDQFTYS.js +102 -0
  5. package/dist/chunk-6LDQFTYS.js.map +1 -0
  6. package/dist/{chunk-IAHGTNOZ.js → chunk-AMCG74RZ.js} +193 -24
  7. package/dist/chunk-AMCG74RZ.js.map +1 -0
  8. package/dist/{chunk-L2GLW2U7.js → chunk-BVZYTZ5H.js} +9 -103
  9. package/dist/chunk-BVZYTZ5H.js.map +1 -0
  10. package/dist/chunk-FQMKGR6L.js +330 -0
  11. package/dist/chunk-FQMKGR6L.js.map +1 -0
  12. package/dist/chunk-HXZEYDUR.js +94 -0
  13. package/dist/chunk-HXZEYDUR.js.map +1 -0
  14. package/dist/{chunk-EVR6UFUV.js → chunk-IZYUSJC7.js} +16 -14
  15. package/dist/{chunk-EVR6UFUV.js.map → chunk-IZYUSJC7.js.map} +1 -1
  16. package/dist/chunk-J22W6HV3.js +107 -0
  17. package/dist/chunk-J22W6HV3.js.map +1 -0
  18. package/dist/{chunk-HWPIFSW2.js → chunk-JSCT3CR4.js} +6 -4
  19. package/dist/{chunk-HWPIFSW2.js.map → chunk-JSCT3CR4.js.map} +1 -1
  20. package/dist/chunk-JYXEWPH4.js +62 -0
  21. package/dist/chunk-JYXEWPH4.js.map +1 -0
  22. package/dist/{chunk-TKE3JGOH.js → chunk-LCE3GF5P.js} +5 -692
  23. package/dist/chunk-LCE3GF5P.js.map +1 -0
  24. package/dist/chunk-MJ2NKQQL.js +119 -0
  25. package/dist/chunk-MJ2NKQQL.js.map +1 -0
  26. package/dist/chunk-N6UR7YVY.js +198 -0
  27. package/dist/chunk-N6UR7YVY.js.map +1 -0
  28. package/dist/chunk-OHISZPOJ.js +97 -0
  29. package/dist/chunk-OHISZPOJ.js.map +1 -0
  30. package/dist/{chunk-5DJTTKX3.js → chunk-PHOUUNK7.js} +74 -111
  31. package/dist/chunk-PHOUUNK7.js.map +1 -0
  32. package/dist/{chunk-PY4XCDLR.js → chunk-RB6QPHJ7.js} +8 -6
  33. package/dist/{chunk-PY4XCDLR.js.map → chunk-RB6QPHJ7.js.map} +1 -1
  34. package/dist/chunk-SN4YWWYO.js +171 -0
  35. package/dist/chunk-SN4YWWYO.js.map +1 -0
  36. package/dist/chunk-SX52TAR4.js +110 -0
  37. package/dist/chunk-SX52TAR4.js.map +1 -0
  38. package/dist/{chunk-XOFWRC73.js → chunk-THTWHNU4.js} +319 -24
  39. package/dist/chunk-THTWHNU4.js.map +1 -0
  40. package/dist/{chunk-H4RVA4VE.js → chunk-VYPWMZ6H.js} +2 -2
  41. package/dist/chunk-XGPU467M.js +136 -0
  42. package/dist/chunk-XGPU467M.js.map +1 -0
  43. package/dist/chunk-ZQMEI34O.js +713 -0
  44. package/dist/chunk-ZQMEI34O.js.map +1 -0
  45. package/dist/compat/index.cjs +7656 -0
  46. package/dist/compat/index.cjs.map +1 -0
  47. package/dist/compat/index.d.cts +18 -0
  48. package/dist/compat/index.d.ts +18 -0
  49. package/dist/compat/index.js +49 -0
  50. package/dist/compat/index.js.map +1 -0
  51. package/dist/compat/jotai/index.cjs +2048 -0
  52. package/dist/compat/jotai/index.cjs.map +1 -0
  53. package/dist/compat/jotai/index.d.cts +2 -0
  54. package/dist/compat/jotai/index.d.ts +2 -0
  55. package/dist/compat/jotai/index.js +9 -0
  56. package/dist/compat/jotai/index.js.map +1 -0
  57. package/dist/compat/nanostores/index.cjs +2175 -0
  58. package/dist/compat/nanostores/index.cjs.map +1 -0
  59. package/dist/compat/nanostores/index.d.cts +2 -0
  60. package/dist/compat/nanostores/index.d.ts +2 -0
  61. package/dist/compat/nanostores/index.js +23 -0
  62. package/dist/compat/nanostores/index.js.map +1 -0
  63. package/dist/compat/nestjs/index.cjs +350 -16
  64. package/dist/compat/nestjs/index.cjs.map +1 -1
  65. package/dist/compat/nestjs/index.d.cts +6 -6
  66. package/dist/compat/nestjs/index.d.ts +6 -6
  67. package/dist/compat/nestjs/index.js +10 -9
  68. package/dist/compat/react/index.cjs +141 -0
  69. package/dist/compat/react/index.cjs.map +1 -0
  70. package/dist/compat/react/index.d.cts +2 -0
  71. package/dist/compat/react/index.d.ts +2 -0
  72. package/dist/compat/react/index.js +12 -0
  73. package/dist/compat/react/index.js.map +1 -0
  74. package/dist/compat/solid/index.cjs +128 -0
  75. package/dist/compat/solid/index.cjs.map +1 -0
  76. package/dist/compat/solid/index.d.cts +2 -0
  77. package/dist/compat/solid/index.d.ts +2 -0
  78. package/dist/compat/solid/index.js +12 -0
  79. package/dist/compat/solid/index.js.map +1 -0
  80. package/dist/compat/svelte/index.cjs +131 -0
  81. package/dist/compat/svelte/index.cjs.map +1 -0
  82. package/dist/compat/svelte/index.d.cts +2 -0
  83. package/dist/compat/svelte/index.d.ts +2 -0
  84. package/dist/compat/svelte/index.js +12 -0
  85. package/dist/compat/svelte/index.js.map +1 -0
  86. package/dist/compat/vue/index.cjs +146 -0
  87. package/dist/compat/vue/index.cjs.map +1 -0
  88. package/dist/compat/vue/index.d.cts +3 -0
  89. package/dist/compat/vue/index.d.ts +3 -0
  90. package/dist/compat/vue/index.js +12 -0
  91. package/dist/compat/vue/index.js.map +1 -0
  92. package/dist/compat/zustand/index.cjs +4931 -0
  93. package/dist/compat/zustand/index.cjs.map +1 -0
  94. package/dist/compat/zustand/index.d.cts +5 -0
  95. package/dist/compat/zustand/index.d.ts +5 -0
  96. package/dist/compat/zustand/index.js +12 -0
  97. package/dist/compat/zustand/index.js.map +1 -0
  98. package/dist/core/index.cjs +53 -4
  99. package/dist/core/index.cjs.map +1 -1
  100. package/dist/core/index.d.cts +3 -3
  101. package/dist/core/index.d.ts +3 -3
  102. package/dist/core/index.js +26 -24
  103. package/dist/demo-shell-26p5fVxn.d.cts +102 -0
  104. package/dist/demo-shell-DEp-nMTl.d.ts +102 -0
  105. package/dist/extra/index.cjs +222 -110
  106. package/dist/extra/index.cjs.map +1 -1
  107. package/dist/extra/index.d.cts +5 -4
  108. package/dist/extra/index.d.ts +5 -4
  109. package/dist/extra/index.js +6 -5
  110. package/dist/extra/sources.cjs +2486 -0
  111. package/dist/extra/sources.cjs.map +1 -0
  112. package/dist/extra/sources.d.cts +465 -0
  113. package/dist/extra/sources.d.ts +465 -0
  114. package/dist/extra/sources.js +57 -0
  115. package/dist/extra/sources.js.map +1 -0
  116. package/dist/graph/index.cjs +408 -14
  117. package/dist/graph/index.cjs.map +1 -1
  118. package/dist/graph/index.d.cts +5 -5
  119. package/dist/graph/index.d.ts +5 -5
  120. package/dist/graph/index.js +13 -5
  121. package/dist/{graph-B6NFqv3z.d.ts → graph-6tZ5jEzr.d.cts} +195 -4
  122. package/dist/{graph-D-3JIQme.d.cts → graph-DQ69XU0g.d.ts} +195 -4
  123. package/dist/index-B4MP_8V_.d.cts +37 -0
  124. package/dist/index-BEfE8H_G.d.cts +121 -0
  125. package/dist/{index-OXImXMq6.d.ts → index-BW1z3BN9.d.ts} +18 -196
  126. package/dist/index-BYOHF0zP.d.ts +34 -0
  127. package/dist/index-B_IP40nB.d.cts +36 -0
  128. package/dist/index-Bd_fwmLf.d.cts +45 -0
  129. package/dist/{index-Ds23Wvou.d.ts → index-BeIdBfcb.d.cts} +120 -573
  130. package/dist/index-BjI6ty9z.d.ts +121 -0
  131. package/dist/index-Bxb5ZYc9.d.cts +34 -0
  132. package/dist/{index-BJB7t9gg.d.cts → index-C0ZXMaXO.d.cts} +2 -2
  133. package/dist/{index-DKE1EATr.d.cts → index-C8mdwMXc.d.cts} +18 -196
  134. package/dist/index-CDAjUFIv.d.ts +36 -0
  135. package/dist/index-CPgZ5wRl.d.ts +44 -0
  136. package/dist/{index-AMWewNDe.d.cts → index-CUwyr1Kk.d.cts} +33 -4
  137. package/dist/index-CUyrtuOf.d.cts +127 -0
  138. package/dist/{index-C-TXEa7C.d.ts → index-CY2TljO4.d.ts} +2 -2
  139. package/dist/index-CmnuOibw.d.ts +37 -0
  140. package/dist/{index-DiobMNwE.d.ts → index-CuYwdKO-.d.ts} +3 -3
  141. package/dist/index-DFhjO4Gg.d.cts +44 -0
  142. package/dist/{index-Ch0IpIO0.d.cts → index-DdD5MVDL.d.ts} +120 -573
  143. package/dist/index-DrISNAOm.d.ts +45 -0
  144. package/dist/index-QBpffFW-.d.cts +86 -0
  145. package/dist/{index-J7Kc0oIQ.d.cts → index-_oMEWlDq.d.cts} +3 -3
  146. package/dist/{index-CYkjxu3s.d.ts → index-eJ6T_qGM.d.ts} +33 -4
  147. package/dist/index-qldRdbQw.d.ts +86 -0
  148. package/dist/index-xdGjv0nO.d.ts +127 -0
  149. package/dist/index.cjs +1780 -176
  150. package/dist/index.cjs.map +1 -1
  151. package/dist/index.d.cts +1007 -648
  152. package/dist/index.d.ts +1007 -648
  153. package/dist/index.js +1202 -1172
  154. package/dist/index.js.map +1 -1
  155. package/dist/{meta-CnkLA_43.d.ts → meta-BGqSZ7mt.d.ts} +1 -1
  156. package/dist/{meta-DWbkoq1s.d.cts → meta-C0-8XW6Q.d.cts} +1 -1
  157. package/dist/{node-B-f-Lu-k.d.cts → node-C_IBuvX2.d.cts} +26 -1
  158. package/dist/{node-B-f-Lu-k.d.ts → node-C_IBuvX2.d.ts} +26 -1
  159. package/dist/{observable-DBnrwcar.d.cts → observable-Crr1jgzx.d.cts} +1 -1
  160. package/dist/{observable-uP-wy_uK.d.ts → observable-DCk45RH5.d.ts} +1 -1
  161. package/dist/patterns/demo-shell.cjs +5604 -0
  162. package/dist/patterns/demo-shell.cjs.map +1 -0
  163. package/dist/patterns/demo-shell.d.cts +6 -0
  164. package/dist/patterns/demo-shell.d.ts +6 -0
  165. package/dist/patterns/demo-shell.js +15 -0
  166. package/dist/patterns/demo-shell.js.map +1 -0
  167. package/dist/patterns/reactive-layout/index.cjs +355 -13
  168. package/dist/patterns/reactive-layout/index.cjs.map +1 -1
  169. package/dist/patterns/reactive-layout/index.d.cts +6 -5
  170. package/dist/patterns/reactive-layout/index.d.ts +6 -5
  171. package/dist/patterns/reactive-layout/index.js +15 -12
  172. package/dist/reactive-layout-BaOQefHu.d.cts +183 -0
  173. package/dist/reactive-layout-D9gejYXE.d.ts +183 -0
  174. package/dist/{storage-BuTdpCI1.d.cts → storage-BMycWEh2.d.ts} +9 -1
  175. package/dist/{storage-F2X1U1x0.d.ts → storage-DiqWHzVI.d.cts} +9 -1
  176. package/package.json +32 -2
  177. package/dist/chunk-5DJTTKX3.js.map +0 -1
  178. package/dist/chunk-IAHGTNOZ.js.map +0 -1
  179. package/dist/chunk-L2GLW2U7.js.map +0 -1
  180. package/dist/chunk-MW4VAKAO.js +0 -47
  181. package/dist/chunk-MW4VAKAO.js.map +0 -1
  182. package/dist/chunk-TKE3JGOH.js.map +0 -1
  183. package/dist/chunk-XOFWRC73.js.map +0 -1
  184. /package/dist/{chunk-H4RVA4VE.js.map → chunk-VYPWMZ6H.js.map} +0 -0
@@ -1,14 +1,16 @@
1
1
  import {
2
- COMPLETE,
3
- DATA,
4
- DIRTY,
5
- ERROR,
6
- RESOLVED,
7
2
  node,
8
3
  producer,
9
4
  state,
10
5
  wallClockNs
11
- } from "./chunk-5DJTTKX3.js";
6
+ } from "./chunk-PHOUUNK7.js";
7
+ import {
8
+ COMPLETE,
9
+ DATA,
10
+ DIRTY,
11
+ ERROR,
12
+ RESOLVED
13
+ } from "./chunk-SX52TAR4.js";
12
14
 
13
15
  // src/extra/cron.ts
14
16
  function parseField(field, min, max) {
@@ -54,8 +56,6 @@ function matchesCron(schedule, date) {
54
56
  }
55
57
 
56
58
  // src/extra/sources.ts
57
- import { existsSync, watch } from "fs";
58
- import { resolve as resolvePath } from "path";
59
59
  function sourceOpts(opts) {
60
60
  return { describeKind: "producer", ...opts };
61
61
  }
@@ -249,99 +249,6 @@ function fromEvent(target, type, opts) {
249
249
  return () => target.removeEventListener(type, handler, options);
250
250
  }, sourceOpts(rest));
251
251
  }
252
- function fromFSWatch(paths, opts) {
253
- const list = Array.isArray(paths) ? paths : [paths];
254
- if (list.length === 0) {
255
- throw new RangeError("fromFSWatch expects at least one path");
256
- }
257
- const { recursive = true, debounce = 100, include, exclude, ...rest } = opts ?? {};
258
- const includePatterns = include?.map(globToRegExp) ?? [];
259
- const excludePatterns = (exclude ?? ["**/node_modules/**", "**/.git/**", "**/dist/**"]).map(
260
- globToRegExp
261
- );
262
- return producer((a) => {
263
- const pending = /* @__PURE__ */ new Map();
264
- const watchers = [];
265
- let stopped = false;
266
- let terminalEmitted = false;
267
- let generation = 0;
268
- const closeWatchers = () => {
269
- for (const watcher of watchers.splice(0)) watcher.close();
270
- };
271
- const emitError = (err) => {
272
- if (terminalEmitted) return;
273
- terminalEmitted = true;
274
- stopped = true;
275
- if (timer !== void 0) clearTimeout(timer);
276
- timer = void 0;
277
- pending.clear();
278
- closeWatchers();
279
- a.down([[ERROR, err]]);
280
- };
281
- let timer;
282
- const flush = (token) => {
283
- timer = void 0;
284
- if (stopped || terminalEmitted) return;
285
- if (pending.size === 0) return;
286
- const batchMessages = [];
287
- for (const evt of pending.values()) batchMessages.push([DATA, evt]);
288
- pending.clear();
289
- if (stopped || terminalEmitted || token !== generation) return;
290
- a.down(batchMessages);
291
- };
292
- try {
293
- for (const basePath of list) {
294
- const watcher = watch(
295
- basePath,
296
- { recursive },
297
- (eventType, fileName) => {
298
- if (stopped || terminalEmitted) return;
299
- if (fileName == null) return;
300
- const rel = String(fileName).replaceAll("\\", "/");
301
- const abs = resolvePath(basePath, String(fileName));
302
- const normalized = abs.replaceAll("\\", "/");
303
- const root = resolvePath(basePath).replaceAll("\\", "/");
304
- const relForMatch = rel.startsWith("./") ? rel.slice(2) : rel;
305
- const included = includePatterns.length === 0 || matchesAnyPattern(normalized, includePatterns) || matchesAnyPattern(relForMatch, includePatterns);
306
- if (!included) return;
307
- const excluded = matchesAnyPattern(normalized, excludePatterns) || matchesAnyPattern(relForMatch, excludePatterns);
308
- if (excluded) return;
309
- let kind = "change";
310
- if (eventType === "rename") {
311
- try {
312
- kind = existsSync(normalized) ? "create" : "delete";
313
- } catch {
314
- kind = "rename";
315
- }
316
- }
317
- pending.set(normalized, {
318
- type: kind,
319
- path: normalized,
320
- root,
321
- relative_path: relForMatch,
322
- timestamp_ns: wallClockNs()
323
- });
324
- if (timer !== void 0) clearTimeout(timer);
325
- const token = generation;
326
- timer = setTimeout(() => flush(token), debounce);
327
- }
328
- );
329
- watcher.on("error", (err) => emitError(err));
330
- watchers.push(watcher);
331
- }
332
- } catch (err) {
333
- emitError(err);
334
- }
335
- return () => {
336
- stopped = true;
337
- generation += 1;
338
- if (timer !== void 0) clearTimeout(timer);
339
- timer = void 0;
340
- closeWatchers();
341
- pending.clear();
342
- };
343
- }, sourceOpts(rest));
344
- }
345
252
  function fromIter(iterable, opts) {
346
253
  return producer((a) => {
347
254
  let cancelled = false;
@@ -670,7 +577,6 @@ export {
670
577
  fromRaf,
671
578
  fromCron,
672
579
  fromEvent,
673
- fromFSWatch,
674
580
  fromIter,
675
581
  fromPromise,
676
582
  fromAsyncIter,
@@ -690,4 +596,4 @@ export {
690
596
  keepalive,
691
597
  reactiveCounter
692
598
  };
693
- //# sourceMappingURL=chunk-L2GLW2U7.js.map
599
+ //# sourceMappingURL=chunk-BVZYTZ5H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/extra/cron.ts","../src/extra/sources.ts"],"sourcesContent":["/**\n * Minimal 5-field cron parser and matcher (minute hour day-of-month month day-of-week).\n * Ported from callbag-recharge `extra/cron.ts` for `fromCron` (roadmap §2.3).\n */\nexport interface CronSchedule {\n\tminutes: Set<number>;\n\thours: Set<number>;\n\tdaysOfMonth: Set<number>;\n\tmonths: Set<number>;\n\tdaysOfWeek: Set<number>;\n}\n\nfunction parseField(field: string, min: number, max: number): Set<number> {\n\tconst result = new Set<number>();\n\tfor (const part of field.split(\",\")) {\n\t\tconst [range, stepStr] = part.split(\"/\");\n\t\tconst step = stepStr ? Number.parseInt(stepStr, 10) : 1;\n\t\tif (Number.isNaN(step) || step < 1) throw new Error(`Invalid cron step: ${part}`);\n\t\tlet start: number;\n\t\tlet end: number;\n\t\tif (range === \"*\") {\n\t\t\tstart = min;\n\t\t\tend = max;\n\t\t} else if (range.includes(\"-\")) {\n\t\t\tconst [a, b] = range.split(\"-\");\n\t\t\tstart = Number.parseInt(a, 10);\n\t\t\tend = Number.parseInt(b, 10);\n\t\t} else {\n\t\t\tstart = Number.parseInt(range, 10);\n\t\t\tend = start;\n\t\t}\n\t\tif (Number.isNaN(start) || Number.isNaN(end)) throw new Error(`Invalid cron field: ${field}`);\n\t\tif (start < min || end > max)\n\t\t\tthrow new Error(`Cron field out of range: ${field} (${min}-${max})`);\n\t\tif (start > end) throw new Error(`Invalid cron range: ${start}-${end} in ${field}`);\n\t\tfor (let i = start; i <= end; i += step) result.add(i);\n\t}\n\treturn result;\n}\n\n/**\n * Parses a standard 5-field cron expression into a {@link CronSchedule}.\n *\n * Supports `*`, ranges (`1-5`), steps (`*\\/5`, `0-30/10`), and comma-separated\n * lists. Fields are: minute (0–59), hour (0–23), day-of-month (1–31),\n * month (1–12), day-of-week (0–6, Sunday = 0).\n *\n * @param expr - Five-field whitespace-separated cron string (e.g. `\"0 9 * * 1-5\"`).\n * @returns Parsed {@link CronSchedule} with one `Set<number>` per field.\n * @throws Error when the expression does not have exactly 5 fields, contains\n * out-of-range values, or uses an invalid step.\n *\n * @example\n * ```ts\n * import { parseCron } from \"@graphrefly/graphrefly-ts\";\n *\n * const sched = parseCron(\"0 9 * * 1-5\"); // weekdays at 09:00\n * sched.hours; // Set { 9 }\n * sched.daysOfWeek; // Set { 1, 2, 3, 4, 5 }\n * ```\n */\nexport function parseCron(expr: string): CronSchedule {\n\tconst parts = expr.trim().split(/\\s+/);\n\tif (parts.length !== 5) throw new Error(`Invalid cron: expected 5 fields, got ${parts.length}`);\n\treturn {\n\t\tminutes: parseField(parts[0], 0, 59),\n\t\thours: parseField(parts[1], 0, 23),\n\t\tdaysOfMonth: parseField(parts[2], 1, 31),\n\t\tmonths: parseField(parts[3], 1, 12),\n\t\tdaysOfWeek: parseField(parts[4], 0, 6),\n\t};\n}\n\n/**\n * Returns `true` if `date` satisfies every field of `schedule`.\n *\n * @param schedule - Parsed schedule from {@link parseCron}.\n * @param date - Moment to test (local time via `getMinutes`, `getHours`, etc.).\n * @returns `true` when all five cron fields match the given date.\n *\n * @example\n * ```ts\n * import { parseCron, matchesCron } from \"@graphrefly/graphrefly-ts\";\n *\n * const sched = parseCron(\"30 8 * * 1\"); // Mondays at 08:30\n * const monday = new Date(\"2026-03-30T08:30:00\"); // a Monday\n * matchesCron(sched, monday); // true\n * ```\n */\nexport function matchesCron(schedule: CronSchedule, date: Date): boolean {\n\treturn (\n\t\tschedule.minutes.has(date.getMinutes()) &&\n\t\tschedule.hours.has(date.getHours()) &&\n\t\tschedule.daysOfMonth.has(date.getDate()) &&\n\t\tschedule.months.has(date.getMonth() + 1) &&\n\t\tschedule.daysOfWeek.has(date.getDay())\n\t);\n}\n","/**\n * Core reactive sources, sinks, and utilities (roadmap §2.3).\n *\n * Each API returns a {@link Node} built with {@link node}, {@link producer},\n * {@link derived}, or {@link effect} — no second protocol.\n *\n * Protocol/system/ingest adapters (fromHTTP, fromWebSocket, fromKafka, etc.)\n * live in {@link ./adapters.ts}.\n */\n\nimport { wallClockNs } from \"../core/clock.js\";\nimport { COMPLETE, DATA, DIRTY, ERROR, RESOLVED } from \"../core/messages.js\";\nimport { type Node, type NodeOptions, type NodeSink, node } from \"../core/node.js\";\nimport { producer, state } from \"../core/sugar.js\";\nimport { type CronSchedule, matchesCron, parseCron } from \"./cron.js\";\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\n/** Options for {@link fromTimer} / {@link fromPromise} / {@link fromAsyncIter}. */\nexport type AsyncSourceOpts = ExtraOpts & { signal?: AbortSignal };\n\n/**\n * Values accepted by {@link fromAny}.\n *\n * @category extra\n */\nexport type NodeInput<T> = Node<T> | PromiseLike<T> | AsyncIterable<T> | Iterable<T> | T;\n\n/** Options for {@link fromCron}. */\nexport type FromCronOptions = ExtraOpts & {\n\t/** Polling interval in ms. Default `60_000`. */\n\ttickMs?: number;\n\t/** Output format: `\"timestamp_ns\"` (default) emits wall-clock nanoseconds; `\"date\"` emits a `Date` object. */\n\toutput?: \"timestamp_ns\" | \"date\";\n};\n\n/** DOM-style event target (browser or `node:events`). */\nexport type EventTargetLike = {\n\taddEventListener(\n\t\ttype: string,\n\t\tlistener: (ev: unknown) => void,\n\t\toptions?: boolean | { capture?: boolean; passive?: boolean; once?: boolean },\n\t): void;\n\tremoveEventListener(\n\t\ttype: string,\n\t\tlistener: (ev: unknown) => void,\n\t\toptions?: boolean | { capture?: boolean; passive?: boolean; once?: boolean },\n\t): void;\n};\n\n/** @internal Shared with adapters.ts and sources-fs.ts for glob matching. */\nexport function escapeRegexChar(ch: string): string {\n\treturn /[\\\\^$+?.()|[\\]{}]/.test(ch) ? `\\\\${ch}` : ch;\n}\n\n/** @internal */\nexport function globToRegExp(glob: string): RegExp {\n\tlet out = \"^\";\n\tfor (let i = 0; i < glob.length; i += 1) {\n\t\tconst ch = glob[i];\n\t\tif (ch === \"*\") {\n\t\t\tconst next = glob[i + 1];\n\t\t\tif (next === \"*\") {\n\t\t\t\tout += \".*\";\n\t\t\t\ti += 1;\n\t\t\t} else {\n\t\t\t\tout += \"[^/]*\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tout += escapeRegexChar(ch);\n\t}\n\tout += \"$\";\n\treturn new RegExp(out);\n}\n\n/** @internal */\nexport function matchesAnyPattern(path: string, patterns: RegExp[]): boolean {\n\tfor (const pattern of patterns) {\n\t\tif (pattern.test(path)) return true;\n\t}\n\treturn false;\n}\n\nfunction wrapSubscribeHook<T>(inner: Node<T>, before: (sink: NodeSink) => void): Node<T> {\n\t// node() passthrough instead of derived([inner], ([v]) => v) — derived uses\n\t// .at(-1) and would drop intermediate values from multi-DATA batches (D1 gap).\n\tconst wrapper = node<T>(\n\t\t[inner as Node],\n\t\t(data, a) => {\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;\n\t\t\t}\n\t\t\tfor (const v of batch0) a.emit(v as T);\n\t\t},\n\t\t{ describeKind: \"derived\", initial: inner.cache as T },\n\t);\n\tconst origSubscribe = wrapper.subscribe.bind(wrapper);\n\t(wrapper as { subscribe: typeof wrapper.subscribe }).subscribe = (sink, actor) => {\n\t\tbefore(sink);\n\t\treturn origSubscribe(sink, actor);\n\t};\n\treturn wrapper;\n}\n\n/**\n * Builds a timer-driven source: one-shot (first tick then `COMPLETE`) or periodic (`0`, `1`, `2`, …).\n *\n * @param ms - Milliseconds before the first emission.\n * @param opts - Producer options plus optional `period` for repeating ticks and optional `signal` (`AbortSignal`) to cancel with `ERROR`.\n * @returns `Node<number>` — tick counter from `0`; teardown clears timers.\n *\n * @example\n * ```ts\n * import { fromTimer } from \"@graphrefly/graphrefly-ts\";\n *\n * fromTimer(250, { period: 1_000 });\n * ```\n *\n * @category extra\n */\nexport function fromTimer(ms: number, opts?: AsyncSourceOpts & { period?: number }): Node<number> {\n\tconst { signal, period, ...rest } = opts ?? {};\n\treturn producer<number>((a) => {\n\t\tlet done = false;\n\t\tlet count = 0;\n\t\tlet t: ReturnType<typeof setTimeout> | undefined;\n\t\tlet iv: ReturnType<typeof setInterval> | undefined;\n\t\tconst cleanup = () => {\n\t\t\tdone = true;\n\t\t\tif (t !== undefined) clearTimeout(t);\n\t\t\tif (iv !== undefined) clearInterval(iv);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t};\n\t\tconst finish = () => {\n\t\t\tif (done) return;\n\t\t\tif (period != null) {\n\t\t\t\ta.emit(count++);\n\t\t\t\tiv = setInterval(() => {\n\t\t\t\t\tif (done) return;\n\t\t\t\t\ta.emit(count++);\n\t\t\t\t}, period);\n\t\t\t} else {\n\t\t\t\t// One-shot: mark done, emit, complete synchronously.\n\t\t\t\t// a.emit() delivers DATA to downstream synchronously before\n\t\t\t\t// COMPLETE arrives — no queueMicrotask needed.\n\t\t\t\tdone = true;\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\ta.emit(count++);\n\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t}\n\t\t};\n\t\tconst onAbort = () => {\n\t\t\tif (done) return;\n\t\t\tcleanup();\n\t\t\ta.down([[ERROR, signal!.reason]]);\n\t\t};\n\t\tif (signal?.aborted) {\n\t\t\tonAbort();\n\t\t\treturn;\n\t\t}\n\t\tt = setTimeout(finish, ms);\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\treturn cleanup;\n\t}, sourceOpts(rest));\n}\n\n/**\n * Animation-frame-driven source. Emits on every `requestAnimationFrame` tick,\n * yielding the frame timestamp (DOMHighResTimeStamp, ms since navigation).\n *\n * Use instead of `fromTimer({ period: 16 })` when animation smoothness matters.\n * In a real browser, `requestAnimationFrame` synchronizes with the display\n * refresh. The source keeps ticking even when the tab is hidden — it\n * transparently switches to `setTimeout` while the tab is backgrounded (so\n * downstream state updates continue) and returns to `requestAnimationFrame`\n * when the tab regains focus.\n *\n * When `requestAnimationFrame` is unavailable (Node test environments, SSR),\n * this falls back to `setTimeout(~16ms)` unconditionally. Abortable via\n * `signal` (emits `ERROR`).\n *\n * @example\n * ```ts\n * import { fromRaf, derived } from \"@graphrefly/graphrefly-ts\";\n *\n * const frame = fromRaf();\n * const bouncingX = derived([frame], ([t]) => 50 + 40 * Math.sin((t as number) * 0.001));\n * ```\n *\n * @category extra\n */\nexport function fromRaf(opts?: AsyncSourceOpts): Node<number> {\n\tconst { signal, ...rest } = opts ?? {};\n\treturn producer<number>((a) => {\n\t\tlet done = false;\n\t\tlet rafId: number | undefined;\n\t\tlet fallbackTimer: ReturnType<typeof setTimeout> | undefined;\n\t\tlet abortListenerAdded = false;\n\t\tlet visibilityListenerAdded = false;\n\n\t\tconst raf: typeof requestAnimationFrame | undefined =\n\t\t\ttypeof requestAnimationFrame === \"function\" ? requestAnimationFrame : undefined;\n\t\tconst caf: typeof cancelAnimationFrame | undefined =\n\t\t\ttypeof cancelAnimationFrame === \"function\" ? cancelAnimationFrame : undefined;\n\t\tconst doc: Document | undefined = typeof document !== \"undefined\" ? document : undefined;\n\n\t\tconst clearPending = () => {\n\t\t\tif (rafId !== undefined && caf) caf(rafId);\n\t\t\tif (fallbackTimer !== undefined) clearTimeout(fallbackTimer);\n\t\t\trafId = undefined;\n\t\t\tfallbackTimer = undefined;\n\t\t};\n\t\tconst cleanup = () => {\n\t\t\tdone = true;\n\t\t\tclearPending();\n\t\t\tif (abortListenerAdded) {\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tabortListenerAdded = false;\n\t\t\t}\n\t\t\tif (visibilityListenerAdded && doc) {\n\t\t\t\tdoc.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t\t\tvisibilityListenerAdded = false;\n\t\t\t}\n\t\t};\n\t\tconst onAbort = () => {\n\t\t\tif (done) return;\n\t\t\tcleanup();\n\t\t\ta.down([[ERROR, signal!.reason]]);\n\t\t};\n\t\tconst tick = (now: number) => {\n\t\t\tif (done) return;\n\t\t\ta.emit(now);\n\t\t\tscheduleNext();\n\t\t};\n\t\tconst scheduleNext = () => {\n\t\t\tif (done) return;\n\t\t\t// Prefer rAF for display-synced ticks when the tab is visible; when\n\t\t\t// hidden, rAF is throttled to ~0 by the browser, so fall back to\n\t\t\t// setTimeout so downstream state continues updating.\n\t\t\tif (raf && (!doc || doc.visibilityState !== \"hidden\")) {\n\t\t\t\trafId = raf(tick);\n\t\t\t} else {\n\t\t\t\tfallbackTimer = setTimeout(() => tick(performance.now()), 16);\n\t\t\t}\n\t\t};\n\t\tconst onVisibilityChange = () => {\n\t\t\tif (done) return;\n\t\t\t// Cancel any pending schedule and re-schedule via the path now\n\t\t\t// appropriate for the current visibility state.\n\t\t\tclearPending();\n\t\t\tscheduleNext();\n\t\t};\n\n\t\tif (signal?.aborted) {\n\t\t\tonAbort();\n\t\t\treturn cleanup;\n\t\t}\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tabortListenerAdded = signal !== undefined;\n\t\tif (doc && raf) {\n\t\t\tdoc.addEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t\tvisibilityListenerAdded = true;\n\t\t}\n\t\tscheduleNext();\n\t\treturn cleanup;\n\t}, sourceOpts(rest));\n}\n\n/**\n * Polls on an interval; when the current minute matches a 5-field cron expression, emits once (see {@link parseCron}).\n *\n * @param expr - Cron string (`min hour dom month dow`).\n * @param opts - Producer options plus `tickMs` (default `60_000`) and `output` (`timestamp_ns` default, or `date` for `Date` values).\n * @returns `Node<number>` (nanosecond timestamp) or `Node<Date>` when `output: \"date\"`.\n *\n * @example\n * ```ts\n * import { fromCron } from \"@graphrefly/graphrefly-ts\";\n *\n * fromCron(\"0 9 * * 1\");\n * ```\n *\n * @category extra\n */\nexport function fromCron(expr: string, opts?: FromCronOptions & { output: \"date\" }): Node<Date>;\nexport function fromCron(expr: string, opts?: FromCronOptions): Node<number>;\nexport function fromCron(expr: string, opts?: FromCronOptions): Node<number | Date> {\n\tconst schedule: CronSchedule = parseCron(expr);\n\tconst { tickMs: tickOpt, output, ...rest } = opts ?? {};\n\tconst tickMs = tickOpt ?? 60_000;\n\tconst emitDate = output === \"date\";\n\treturn producer<number | Date>(\n\t\t(a) => {\n\t\t\tlet lastFiredKey = -1;\n\t\t\tconst check = () => {\n\t\t\t\tconst now = new Date();\n\t\t\t\tconst key =\n\t\t\t\t\tnow.getFullYear() * 100_000_000 +\n\t\t\t\t\t(now.getMonth() + 1) * 1_000_000 +\n\t\t\t\t\tnow.getDate() * 10_000 +\n\t\t\t\t\tnow.getHours() * 100 +\n\t\t\t\t\tnow.getMinutes();\n\t\t\t\tif (key !== lastFiredKey && matchesCron(schedule, now)) {\n\t\t\t\t\tlastFiredKey = key;\n\t\t\t\t\ta.emit(emitDate ? now : wallClockNs());\n\t\t\t\t}\n\t\t\t};\n\t\t\tcheck();\n\t\t\tconst id = setInterval(check, tickMs);\n\t\t\treturn () => clearInterval(id);\n\t\t},\n\t\t{ ...sourceOpts(rest), name: rest.name ?? `cron:${expr}` },\n\t);\n}\n\n/**\n * Wraps a DOM-style `addEventListener` target; each event becomes a `DATA` emission.\n *\n * @param target - Object with `addEventListener` / `removeEventListener`.\n * @param type - Event name (e.g. `\"click\"`).\n * @param opts - Producer options plus listener options (`capture`, `passive`, `once`).\n * @returns `Node<T>` — event payloads; teardown removes the listener.\n *\n * @example\n * ```ts\n * import { fromEvent } from \"@graphrefly/graphrefly-ts\";\n *\n * fromEvent(document.body, \"click\");\n * ```\n *\n * @category extra\n */\nexport function fromEvent<T = unknown>(\n\ttarget: EventTargetLike,\n\ttype: string,\n\topts?: ExtraOpts & { capture?: boolean; passive?: boolean; once?: boolean },\n): Node<T> {\n\tconst { capture, passive, once, ...rest } = opts ?? {};\n\treturn producer<T>((a) => {\n\t\tconst handler = (e: unknown) => {\n\t\t\ta.emit(e as T);\n\t\t};\n\t\tconst options = { capture, passive, once };\n\t\ttarget.addEventListener(type, handler, options);\n\t\treturn () => target.removeEventListener(type, handler, options);\n\t}, sourceOpts(rest));\n}\n\n/**\n * Drains a synchronous iterable; each item is `DATA`, then `COMPLETE`, or `ERROR` if iteration throws.\n *\n * @param iterable - Values to emit in order.\n * @param opts - Optional producer options.\n * @returns `Node<T>` — one emission per element.\n *\n * @example\n * ```ts\n * import { fromIter } from \"@graphrefly/graphrefly-ts\";\n *\n * fromIter([1, 2, 3]);\n * ```\n *\n * @category extra\n */\nexport function fromIter<T>(iterable: Iterable<T>, opts?: ExtraOpts): Node<T> {\n\treturn producer<T>((a) => {\n\t\tlet cancelled = false;\n\t\ttry {\n\t\t\tfor (const x of iterable) {\n\t\t\t\tif (cancelled) return;\n\t\t\t\ta.emit(x);\n\t\t\t}\n\t\t\tif (!cancelled) a.down([[COMPLETE]]);\n\t\t} catch (e) {\n\t\t\tif (!cancelled) a.down([[ERROR, e]]);\n\t\t}\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t}, sourceOpts(opts));\n}\n\nfunction isThenable(x: unknown): x is PromiseLike<unknown> {\n\treturn x != null && typeof (x as PromiseLike<unknown>).then === \"function\";\n}\n\n/**\n * Lifts a Promise (or thenable) to a single-value stream: one `DATA` then `COMPLETE`, or `ERROR` on rejection.\n *\n * @param p - Promise to await.\n * @param opts - Producer options plus optional `signal` for abort → `ERROR` with reason.\n * @returns `Node<T>` — settles once.\n *\n * @example\n * ```ts\n * import { fromPromise } from \"@graphrefly/graphrefly-ts\";\n *\n * fromPromise(Promise.resolve(42));\n * ```\n *\n * @category extra\n */\nexport function fromPromise<T>(p: Promise<T> | PromiseLike<T>, opts?: AsyncSourceOpts): Node<T> {\n\tconst { signal, ...rest } = opts ?? {};\n\treturn producer<T>((a) => {\n\t\tlet settled = false;\n\t\tconst onAbort = () => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\ta.down([[ERROR, signal!.reason]]);\n\t\t};\n\t\tif (signal?.aborted) {\n\t\t\tonAbort();\n\t\t\treturn;\n\t\t}\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tvoid Promise.resolve(p).then(\n\t\t\t(v) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\ta.emit(v as T);\n\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t},\n\t\t\t(e) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\ta.down([[ERROR, e]]);\n\t\t\t},\n\t\t);\n\t\treturn () => {\n\t\t\tsettled = true;\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t};\n\t}, sourceOpts(rest));\n}\n\n/**\n * Reads an async iterable; each `next()` value becomes `DATA`; `COMPLETE` when done; `ERROR` on failure.\n *\n * @param iterable - Async source (`for await` shape).\n * @param opts - Producer options plus optional `signal` to abort the pump.\n * @returns `Node<T>` — async pull stream.\n *\n * @example\n * ```ts\n * import { fromAsyncIter } from \"@graphrefly/graphrefly-ts\";\n *\n * async function* gen() {\n * yield 1;\n * }\n * fromAsyncIter(gen());\n * ```\n *\n * @category extra\n */\nexport function fromAsyncIter<T>(iterable: AsyncIterable<T>, opts?: AsyncSourceOpts): Node<T> {\n\tconst { signal: outerSignal, ...rest } = opts ?? {};\n\treturn producer<T>((a) => {\n\t\tconst ac = new AbortController();\n\t\tconst onOuterAbort = () => ac.abort(outerSignal?.reason);\n\t\tif (outerSignal?.aborted) {\n\t\t\tac.abort(outerSignal.reason);\n\t\t} else {\n\t\t\touterSignal?.addEventListener(\"abort\", onOuterAbort, { once: true });\n\t\t}\n\t\tconst signal = outerSignal ?? ac.signal;\n\t\tlet cancelled = false;\n\t\tconst it = iterable[Symbol.asyncIterator]();\n\t\t// Each pump() call chains directly into the next via Promise.then —\n\t\t// no queueMicrotask needed; Promise resolution already yields to the\n\t\t// microtask queue. COMPLETE is delivered synchronously after the last\n\t\t// value, same as fromIter semantics.\n\t\tconst pump = (): void => {\n\t\t\tif (cancelled || signal.aborted) return;\n\t\t\tvoid Promise.resolve(it.next()).then(\n\t\t\t\t(step) => {\n\t\t\t\t\tif (cancelled || signal.aborted) return;\n\t\t\t\t\tif (step.done) {\n\t\t\t\t\t\ta.down([[COMPLETE]]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\ta.emit(step.value as T);\n\t\t\t\t\tpump();\n\t\t\t\t},\n\t\t\t\t(e) => {\n\t\t\t\t\tif (!cancelled && !signal.aborted) a.down([[ERROR, e]]);\n\t\t\t\t},\n\t\t\t);\n\t\t};\n\t\tpump();\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t\touterSignal?.removeEventListener(\"abort\", onOuterAbort);\n\t\t\tac.abort();\n\t\t\tvoid Promise.resolve(it.return?.()).catch(() => undefined);\n\t\t};\n\t}, sourceOpts(rest));\n}\n\nfunction isNode(x: unknown): x is Node {\n\treturn (\n\t\tx != null &&\n\t\ttypeof x === \"object\" &&\n\t\t\"cache\" in x &&\n\t\ttypeof (x as Node).subscribe === \"function\"\n\t);\n}\n\n/**\n * Coerces a value to a `Node` by shape: existing `Node` passthrough, thenable → {@link fromPromise},\n * async iterable → {@link fromAsyncIter}, sync iterable → {@link fromIter}, else scalar → {@link of}.\n *\n * @param input - Any value to wrap.\n * @param opts - Passed through when a Promise/async path is chosen.\n * @returns `Node` of the inferred element type.\n *\n * @example\n * ```ts\n * import { fromAny, state } from \"@graphrefly/graphrefly-ts\";\n *\n * fromAny(state(1));\n * fromAny(Promise.resolve(2));\n * ```\n *\n * @category extra\n */\nexport function fromAny<T>(input: NodeInput<T>, opts?: AsyncSourceOpts): Node<T> {\n\tif (isNode(input)) {\n\t\treturn input as Node<T>;\n\t}\n\tif (isThenable(input)) {\n\t\treturn fromPromise(input as PromiseLike<T>, opts);\n\t}\n\tif (input !== null && input !== undefined) {\n\t\tconst candidate = input as { [Symbol.asyncIterator]?: unknown; [Symbol.iterator]?: unknown };\n\t\tif (typeof candidate[Symbol.asyncIterator] === \"function\") {\n\t\t\treturn fromAsyncIter(input as AsyncIterable<T>, opts);\n\t\t}\n\t\tif (typeof candidate[Symbol.iterator] === \"function\") {\n\t\t\treturn fromIter(input as Iterable<T>, opts);\n\t\t}\n\t}\n\t// scalar fallback\n\treturn of(input as T);\n}\n\n/**\n * Emits each argument as `DATA` in order, then `COMPLETE` (implemented via {@link fromIter}).\n *\n * @param values - Values to emit.\n * @returns `Node<T>` — finite sequence.\n *\n * @example\n * ```ts\n * import { of } from \"@graphrefly/graphrefly-ts\";\n *\n * of(1, 2, 3);\n * ```\n *\n * @category extra\n */\nexport function of<T>(...values: T[]): Node<T> {\n\treturn fromIter(values, undefined);\n}\n\n/**\n * Completes immediately with no `DATA` (cold `EMPTY` analogue).\n *\n * @param opts - Optional producer options.\n * @returns `Node<T>` — terminal `COMPLETE` only.\n *\n * @example\n * ```ts\n * import { empty } from \"@graphrefly/graphrefly-ts\";\n *\n * empty();\n * ```\n *\n * @category extra\n */\nexport function empty<T = never>(opts?: ExtraOpts): Node<T> {\n\treturn producer<T>((a) => {\n\t\ta.down([[COMPLETE]]);\n\t\treturn undefined;\n\t}, sourceOpts(opts));\n}\n\n/**\n * Never emits and never completes until teardown (cold `NEVER` analogue).\n *\n * @param opts - Optional producer options.\n * @returns `Node<T>` — silent until unsubscribed.\n *\n * @example\n * ```ts\n * import { never } from \"@graphrefly/graphrefly-ts\";\n *\n * never();\n * ```\n *\n * @category extra\n */\nexport function never<T = never>(opts?: ExtraOpts): Node<T> {\n\treturn producer<T>(() => undefined, sourceOpts(opts));\n}\n\n/**\n * Emits `ERROR` as soon as the producer starts (cold error source).\n *\n * @param err - Error payload forwarded as `ERROR` data.\n * @param opts - Optional producer options.\n * @returns `Node<never>` — terminates with `ERROR`.\n *\n * @example\n * ```ts\n * import { throwError } from \"@graphrefly/graphrefly-ts\";\n *\n * throwError(new Error(\"fail\"));\n * ```\n *\n * @category extra\n */\nexport function throwError(err: unknown, opts?: ExtraOpts): Node<never> {\n\treturn producer<never>((a) => {\n\t\ta.down([[ERROR, err]]);\n\t\treturn undefined;\n\t}, sourceOpts(opts));\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\treturn node<T[]>(\n\t\t[source as Node],\n\t\t(data, actions, ctx) => {\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;\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},\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 producer<T>(\n\t\t(a) =>\n\t\t\tsource.subscribe((msgs) => {\n\t\t\t\ta.down(msgs);\n\t\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\tconst buf: T[] = [];\n\tconst inner = producer<T>(\n\t\t(a) =>\n\t\t\tsource.subscribe((msgs) => {\n\t\t\t\tfor (const m of msgs) {\n\t\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\t\tbuf.push(m[1] as T);\n\t\t\t\t\t\tif (buf.length > bufferSize) buf.shift();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ta.down(msgs);\n\t\t\t}),\n\t\t{ ...sourceOpts<T>(opts), initial: source.cache },\n\t);\n\treturn wrapSubscribeHook(inner, (sink) => {\n\t\tfor (const v of buf) {\n\t\t\tsink([[DATA, v]]);\n\t\t}\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 * Converts the first `DATA` on `source` into a Promise; rejects on `ERROR` or `COMPLETE` without data.\n *\n * **Important:** This subscribes and waits for a **future** emission. Data that\n * has already flowed is gone and will not be seen. Call this *before* the upstream\n * emits, or use `source.cache` / `source.status` for already-cached state.\n * See COMPOSITION-GUIDE §2 (subscription ordering).\n *\n * @param source - Node to read once.\n * @returns Promise of the first value.\n *\n * @example\n * ```ts\n * import { firstValueFrom, of } from \"@graphrefly/graphrefly-ts\";\n *\n * await firstValueFrom(of(42));\n * ```\n *\n * @category extra\n */\nexport function firstValueFrom<T>(source: Node<T>): Promise<T> {\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet shouldUnsub = false;\n\t\tlet unsub: (() => void) | undefined;\n\t\tunsub = source.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (settled) return;\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tresolve(m[1] as T);\n\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (m[0] === ERROR) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\treject(m[1]);\n\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\treject(new Error(\"completed without DATA\"));\n\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tif (shouldUnsub) {\n\t\t\tunsub?.();\n\t\t\tunsub = undefined;\n\t\t}\n\t});\n}\n\n/**\n * Wait for the first DATA value from `source` that satisfies `predicate`.\n *\n * Subscribes directly and resolves on the first DATA value where\n * `predicate` returns true. Reactive, no polling. Use in tests and\n * bridging code where you need a single matching value as a Promise.\n *\n * **Important:** This only captures **future** emissions — data that has\n * already flowed through the node is gone. Call this *before* the upstream\n * emits. For already-cached values, use `source.cache` / `source.status`.\n * See COMPOSITION-GUIDE §2 (subscription ordering).\n *\n * ```ts\n * const val = await firstWhere(strategy.node, snap => snap.size > 0);\n * ```\n *\n * @category extra\n */\nexport function firstWhere<T>(source: Node<T>, predicate: (value: T) => boolean): Promise<T> {\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet shouldUnsub = false;\n\t\tlet unsub: (() => void) | undefined;\n\t\tunsub = source.subscribe((msgs) => {\n\t\t\tfor (const m of msgs) {\n\t\t\t\tif (settled) return;\n\t\t\t\tif (m[0] === DATA) {\n\t\t\t\t\tconst v = m[1] as T;\n\t\t\t\t\tif (predicate(v)) {\n\t\t\t\t\t\tsettled = true;\n\t\t\t\t\t\tresolve(v);\n\t\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\t\tunsub();\n\t\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (m[0] === ERROR) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\treject(m[1]);\n\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (m[0] === COMPLETE) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\treject(new Error(\"completed without matching value\"));\n\t\t\t\t\tif (unsub) {\n\t\t\t\t\t\tunsub();\n\t\t\t\t\t\tunsub = undefined;\n\t\t\t\t\t} else shouldUnsub = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tif (shouldUnsub) {\n\t\t\tunsub?.();\n\t\t\tunsub = undefined;\n\t\t}\n\t});\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\n// ---------------------------------------------------------------------------\n// keepalive\n// ---------------------------------------------------------------------------\n\n/**\n * Activate a compute node's upstream wiring without a real sink.\n *\n * Derived/effect nodes are lazy — they don't compute until at least one\n * subscriber exists (COMPOSITION-GUIDE §5). `keepalive` subscribes with an\n * empty sink so the node stays wired for `.cache` and upstream propagation.\n *\n * Returns the unsubscribe handle. Common usage:\n * `graph.addDisposer(keepalive(node))`.\n *\n * @category extra\n */\nexport function keepalive(n: Node<unknown>): () => void {\n\treturn n.subscribe(() => {});\n}\n\n// ---------------------------------------------------------------------------\n// reactiveCounter\n// ---------------------------------------------------------------------------\n\n/** Bundle returned by {@link reactiveCounter}. */\nexport type ReactiveCounterBundle = {\n\t/** Reactive node holding the current count. */\n\treadonly node: Node<number>;\n\t/** Increment by 1. Returns `false` if cap would be exceeded. */\n\tincrement(): boolean;\n\t/** Current count (synchronous read). */\n\tget(): number;\n\t/** Whether the counter has reached its cap. */\n\tatCap(): boolean;\n};\n\n/**\n * Reactive counter with a cap — the building block for circuit breakers.\n *\n * Wraps a `state(0)` node with `increment()` that respects a maximum.\n * The `node` is subscribable and composable like any reactive node. When\n * the cap is reached, `increment()` returns `false`.\n *\n * ```ts\n * const retries = reactiveCounter(10);\n * retries.increment(); // true — count is now 1\n * retries.node.subscribe(...); // reactive updates\n * retries.atCap(); // false\n * ```\n *\n * @param cap - Maximum value (inclusive). 0 = no increments allowed.\n * @category extra\n */\nexport function reactiveCounter(cap: number): ReactiveCounterBundle {\n\tconst counter = state(0);\n\treturn {\n\t\tnode: counter,\n\t\tincrement() {\n\t\t\tconst current = counter.cache ?? 0;\n\t\t\tif (current >= cap) return false;\n\t\t\tcounter.down([[DIRTY], [DATA, current + 1]]);\n\t\t\treturn true;\n\t\t},\n\t\tget() {\n\t\t\treturn counter.cache ?? 0;\n\t\t},\n\t\tatCap() {\n\t\t\treturn (counter.cache ?? 0) >= cap;\n\t\t},\n\t};\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,330 @@
1
+ import {
2
+ analyzeAndMeasure,
3
+ computeLineBreaks
4
+ } from "./chunk-LCE3GF5P.js";
5
+ import {
6
+ Graph
7
+ } from "./chunk-THTWHNU4.js";
8
+ import {
9
+ describeNode,
10
+ resolveDescribeFields
11
+ } from "./chunk-VYPWMZ6H.js";
12
+ import {
13
+ batch,
14
+ derived,
15
+ effect,
16
+ state
17
+ } from "./chunk-PHOUUNK7.js";
18
+ import {
19
+ __export
20
+ } from "./chunk-SX52TAR4.js";
21
+
22
+ // src/patterns/demo-shell.ts
23
+ var demo_shell_exports = {};
24
+ __export(demo_shell_exports, {
25
+ demoShell: () => demoShell
26
+ });
27
+ function clamp01(v) {
28
+ return Math.max(0, Math.min(1, v));
29
+ }
30
+ function demoShell(opts) {
31
+ const mainRatioInit = clamp01(opts?.mainRatio ?? 0.65);
32
+ const sideSplitInit = clamp01(opts?.sideSplit ?? 0.5);
33
+ const viewportInit = Math.max(0, opts?.viewportWidth ?? 1280);
34
+ const registry = opts?.nodeRegistry ?? /* @__PURE__ */ new Map();
35
+ const adapter = opts?.adapter ?? null;
36
+ const layoutFont = opts?.layoutFont ?? "14px monospace";
37
+ const onHighlight = opts?.onHighlight;
38
+ const g = new Graph("demo-shell");
39
+ const paneMainRatio = state(mainRatioInit, { name: "pane/main-ratio" });
40
+ const paneSideSplit = state(sideSplitInit, { name: "pane/side-split" });
41
+ const paneFullscreen = state(null, {
42
+ name: "pane/fullscreen"
43
+ });
44
+ const viewportWidth = state(viewportInit, { name: "viewport/width" });
45
+ g.add("pane/main-ratio", paneMainRatio);
46
+ g.add("pane/side-split", paneSideSplit);
47
+ g.add("pane/fullscreen", paneFullscreen);
48
+ g.add("viewport/width", viewportWidth);
49
+ const paneMainWidth = derived(
50
+ [paneMainRatio, viewportWidth, paneFullscreen],
51
+ ([ratio, vw, fs]) => {
52
+ const r = ratio;
53
+ const w = vw;
54
+ const fullscreen = fs;
55
+ if (fullscreen === "main") return w;
56
+ if (fullscreen === "graph" || fullscreen === "code") return 0;
57
+ return Math.round(w * r);
58
+ },
59
+ { name: "pane/main-width" }
60
+ );
61
+ const paneSideWidth = derived(
62
+ [paneMainWidth, viewportWidth, paneFullscreen],
63
+ ([main, vw, fs]) => {
64
+ const fullscreen = fs;
65
+ const w = vw;
66
+ if (fullscreen === "main") return 0;
67
+ if (fullscreen === "graph" || fullscreen === "code") return w;
68
+ return w - main;
69
+ },
70
+ { name: "pane/side-width" }
71
+ );
72
+ const paneGraphHeight = derived(
73
+ [paneSideSplit, paneFullscreen],
74
+ ([split, fs]) => {
75
+ const fullscreen = fs;
76
+ if (fullscreen === "graph") return 1;
77
+ if (fullscreen === "code") return 0;
78
+ if (fullscreen === "main") return 0;
79
+ return clamp01(split);
80
+ },
81
+ { name: "pane/graph-height-ratio" }
82
+ );
83
+ const paneCodeHeight = derived(
84
+ [paneGraphHeight, paneFullscreen],
85
+ ([graphH, fs]) => {
86
+ const fullscreen = fs;
87
+ if (fullscreen === "code") return 1;
88
+ if (fullscreen === "graph" || fullscreen === "main") return 0;
89
+ return 1 - graphH;
90
+ },
91
+ { name: "pane/code-height-ratio" }
92
+ );
93
+ g.add("pane/main-width", paneMainWidth);
94
+ g.add("pane/side-width", paneSideWidth);
95
+ g.add("pane/graph-height-ratio", paneGraphHeight);
96
+ g.add("pane/code-height-ratio", paneCodeHeight);
97
+ const demoGraphRef = state(null, {
98
+ name: "demo/graph-ref"
99
+ });
100
+ const demoGraphTick = state(0, { name: "demo/graph-tick" });
101
+ g.add("demo/graph-ref", demoGraphRef);
102
+ g.add("demo/graph-tick", demoGraphTick);
103
+ const graphMermaid = derived(
104
+ [demoGraphRef, demoGraphTick],
105
+ ([ref, _tick]) => {
106
+ const demo = ref;
107
+ if (!demo) return "";
108
+ return demo.describe({ format: "mermaid" });
109
+ },
110
+ { name: "graph/mermaid" }
111
+ );
112
+ const graphDescribe = derived(
113
+ [demoGraphRef, demoGraphTick],
114
+ ([ref, _tick]) => {
115
+ const demo = ref;
116
+ if (!demo) return null;
117
+ const { expand: _, ...snapshot } = demo.describe({ detail: "standard" });
118
+ return snapshot;
119
+ },
120
+ { name: "graph/describe" }
121
+ );
122
+ g.add("graph/mermaid", graphMermaid);
123
+ g.add("graph/describe", graphDescribe);
124
+ const hoverTarget = state(null, { name: "hover/target" });
125
+ g.add("hover/target", hoverTarget);
126
+ const highlightCodeScroll = derived(
127
+ [hoverTarget],
128
+ ([target]) => {
129
+ const t = target;
130
+ if (!t) return null;
131
+ const entry = registry.get(t.id);
132
+ return entry ? entry.codeLine : null;
133
+ },
134
+ { name: "highlight/code-scroll" }
135
+ );
136
+ const highlightVisual = derived(
137
+ [hoverTarget],
138
+ ([target]) => {
139
+ const t = target;
140
+ if (!t) return null;
141
+ const entry = registry.get(t.id);
142
+ return entry ? entry.visualSelector : null;
143
+ },
144
+ { name: "highlight/visual" }
145
+ );
146
+ const highlightGraph = derived(
147
+ [hoverTarget],
148
+ ([target]) => {
149
+ const t = target;
150
+ if (!t) return null;
151
+ return t.id;
152
+ },
153
+ { name: "highlight/graph" }
154
+ );
155
+ g.add("highlight/code-scroll", highlightCodeScroll);
156
+ g.add("highlight/visual", highlightVisual);
157
+ g.add("highlight/graph", highlightGraph);
158
+ if (onHighlight?.codeScroll) {
159
+ const cb = onHighlight.codeScroll;
160
+ const applyCodeScroll = effect([highlightCodeScroll], ([line]) => {
161
+ cb(line);
162
+ });
163
+ g.add("highlight/apply-code-scroll", applyCodeScroll);
164
+ }
165
+ if (onHighlight?.visual) {
166
+ const cb = onHighlight.visual;
167
+ const applyVisual = effect([highlightVisual], ([selector]) => {
168
+ cb(selector);
169
+ });
170
+ g.add("highlight/apply-visual", applyVisual);
171
+ }
172
+ if (onHighlight?.graph) {
173
+ const cb = onHighlight.graph;
174
+ const applyGraph = effect([highlightGraph], ([nodeId]) => {
175
+ cb(nodeId);
176
+ });
177
+ g.add("highlight/apply-graph", applyGraph);
178
+ }
179
+ const inspectSelected = state(null, {
180
+ name: "inspect/selected-node"
181
+ });
182
+ g.add("inspect/selected-node", inspectSelected);
183
+ const standardFields = resolveDescribeFields("standard");
184
+ const inspectNodeDetail = derived(
185
+ [inspectSelected, demoGraphRef, demoGraphTick],
186
+ ([path, ref, _tick]) => {
187
+ const demo = ref;
188
+ const p = path;
189
+ if (!demo || !p) return null;
190
+ try {
191
+ const nd = demo.resolve(p);
192
+ const nodeDesc = describeNode(nd, standardFields);
193
+ return { path: p, ...nodeDesc, value: nd.cache };
194
+ } catch {
195
+ return null;
196
+ }
197
+ },
198
+ { name: "inspect/node-detail" }
199
+ );
200
+ const inspectTraceLog = derived(
201
+ [demoGraphRef, demoGraphTick],
202
+ ([ref, _tick]) => {
203
+ const demo = ref;
204
+ if (!demo) return [];
205
+ return demo.trace();
206
+ },
207
+ { name: "inspect/trace-log" }
208
+ );
209
+ g.add("inspect/node-detail", inspectNodeDetail);
210
+ g.add("inspect/trace-log", inspectTraceLog);
211
+ const metaDebug = state(false, { name: "meta/debug" });
212
+ g.add("meta/debug", metaDebug);
213
+ const metaShellMermaid = derived(
214
+ [metaDebug, demoGraphTick],
215
+ ([debug, _tick]) => {
216
+ if (!debug) return "";
217
+ return g.describe({ format: "mermaid" });
218
+ },
219
+ { name: "meta/shell-mermaid" }
220
+ );
221
+ g.add("meta/shell-mermaid", metaShellMermaid);
222
+ const codeTextNode = state("", { name: "layout/code-text" });
223
+ g.add("layout/code-text", codeTextNode);
224
+ if (adapter) {
225
+ const measureCache = /* @__PURE__ */ new Map();
226
+ const graphLabels = derived(
227
+ [graphDescribe],
228
+ ([desc]) => {
229
+ const d = desc;
230
+ if (!d) return /* @__PURE__ */ new Map();
231
+ const result = /* @__PURE__ */ new Map();
232
+ for (const [name] of Object.entries(d.nodes)) {
233
+ const segments = analyzeAndMeasure(name, layoutFont, adapter, measureCache);
234
+ const lb = computeLineBreaks(segments, Infinity, adapter, layoutFont, measureCache);
235
+ const width = lb.lines.reduce((max, l) => Math.max(max, l.width), 0);
236
+ const height = lb.lineCount * 20;
237
+ result.set(name, { width, height });
238
+ }
239
+ return result;
240
+ },
241
+ {
242
+ name: "layout/graph-labels",
243
+ equals: (a, b) => {
244
+ if (a === b) return true;
245
+ const ma = a;
246
+ const mb = b;
247
+ if (ma.size !== mb.size) return false;
248
+ for (const [k, v] of ma) {
249
+ const bv = mb.get(k);
250
+ if (!bv || bv.width !== v.width || bv.height !== v.height) return false;
251
+ }
252
+ return true;
253
+ }
254
+ }
255
+ );
256
+ const codeLines = derived(
257
+ [codeTextNode, paneSideWidth],
258
+ ([text, sideW]) => {
259
+ const t = text;
260
+ if (!t) return { lineCount: 0, lines: [] };
261
+ const segments = analyzeAndMeasure(t, layoutFont, adapter, measureCache);
262
+ const maxW = sideW - 40;
263
+ return computeLineBreaks(segments, Math.max(100, maxW), adapter, layoutFont, measureCache);
264
+ },
265
+ { name: "layout/code-lines" }
266
+ );
267
+ const sideWidthHint = derived(
268
+ [graphLabels],
269
+ ([labels]) => {
270
+ const m = labels;
271
+ if (m.size === 0) return 200;
272
+ let maxW = 0;
273
+ for (const { width } of m.values()) {
274
+ if (width > maxW) maxW = width;
275
+ }
276
+ return Math.max(200, Math.round(maxW + 80));
277
+ },
278
+ { name: "layout/side-width-hint" }
279
+ );
280
+ g.add("layout/graph-labels", graphLabels);
281
+ g.add("layout/code-lines", codeLines);
282
+ g.add("layout/side-width-hint", sideWidthHint);
283
+ }
284
+ let tickCounter = 0;
285
+ return {
286
+ graph: g,
287
+ setMainRatio(ratio) {
288
+ g.set("pane/main-ratio", clamp01(ratio));
289
+ },
290
+ setSideSplit(ratio) {
291
+ g.set("pane/side-split", clamp01(ratio));
292
+ },
293
+ setFullscreen(pane) {
294
+ g.set("pane/fullscreen", pane);
295
+ },
296
+ setViewportWidth(width) {
297
+ g.set("viewport/width", Math.max(0, width));
298
+ },
299
+ setHoverTarget(target) {
300
+ g.set("hover/target", target);
301
+ },
302
+ setDemoGraph(demo) {
303
+ g.set("demo/graph-ref", demo);
304
+ },
305
+ bumpGraphTick() {
306
+ g.set("demo/graph-tick", ++tickCounter);
307
+ },
308
+ selectNode(path) {
309
+ g.set("inspect/selected-node", path);
310
+ },
311
+ setMetaDebug(on) {
312
+ g.set("meta/debug", on);
313
+ },
314
+ setCodeText(text) {
315
+ g.set("layout/code-text", text);
316
+ },
317
+ batch(fn) {
318
+ batch(fn);
319
+ },
320
+ destroy() {
321
+ g.destroy();
322
+ }
323
+ };
324
+ }
325
+
326
+ export {
327
+ demoShell,
328
+ demo_shell_exports
329
+ };
330
+ //# sourceMappingURL=chunk-FQMKGR6L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/patterns/demo-shell.ts"],"sourcesContent":["/**\n * Three-pane demo shell (roadmap §7.2).\n *\n * A `Graph(\"demo-shell\")` that dogfoods reactive coordination for the\n * main/side split layout with synchronized cross-highlighting.\n *\n * **Zero framework dependency** — framework bindings wrap pane components only.\n * The shell graph is headless and fully testable.\n */\n\nimport { batch } from \"../core/batch.js\";\nimport { describeNode, resolveDescribeFields } from \"../core/meta.js\";\nimport { derived, effect, state } from \"../core/sugar.js\";\nimport { Graph } from \"../graph/graph.js\";\nimport type { MeasurementAdapter } from \"./reactive-layout/reactive-layout.js\";\nimport { analyzeAndMeasure, computeLineBreaks } from \"./reactive-layout/reactive-layout.js\";\n\n// ——————————————————————————————————————————————————————————\n// Types\n// ——————————————————————————————————————————————————————————\n\n/** Identifies which pane is the source of a hover event. */\nexport type HoverPaneType = \"visual\" | \"graph\" | \"code\";\n\n/** Cross-highlighting hover target. `null` means nothing hovered. */\nexport type HoverTarget = { pane: HoverPaneType; id: string } | null;\n\n/** Which pane is full-screened (null = normal layout). */\nexport type FullscreenPane = \"main\" | \"graph\" | \"code\" | null;\n\n/**\n * Cross-referencing registry: maps node paths to code line numbers and\n * visual element selectors. Provided by each demo's `store.ts`.\n */\nexport type NodeRegistry = Map<string, { codeLine: number; visualSelector: string }>;\n\n/** Callbacks for cross-highlighting effect nodes. */\nexport type HighlightCallbacks = {\n\t/** Called when code-scroll highlight target changes. */\n\tcodeScroll?: (line: number | null) => void;\n\t/** Called when visual highlight target changes. */\n\tvisual?: (selector: string | null) => void;\n\t/** Called when graph highlight target changes. */\n\tgraph?: (nodeId: string | null) => void;\n};\n\n/** Label dimensions for graph node sizing. */\nexport type GraphLabelSize = { width: number; height: number };\n\n/** Options for {@link demoShell}. */\nexport type DemoShellOptions = {\n\t/** Initial main/side split ratio (0–1). Default: `0.65`. */\n\tmainRatio?: number;\n\t/** Initial graph/code vertical split in the side pane (0–1). Default: `0.5`. */\n\tsideSplit?: number;\n\t/** Initial viewport width in pixels. Default: `1280`. */\n\tviewportWidth?: number;\n\t/** Cross-referencing registry for hover→code/visual/graph mapping. */\n\tnodeRegistry?: NodeRegistry;\n\t/** Measurement adapter for layout engine integration. When provided, enables layout/* derived nodes. */\n\tadapter?: MeasurementAdapter;\n\t/** Font string for layout measurement. Default: `\"14px monospace\"`. */\n\tlayoutFont?: string;\n\t/** Callbacks for cross-highlighting effect nodes. When provided, creates effect nodes visible in describe(). */\n\tonHighlight?: HighlightCallbacks;\n};\n\n/** Return type of {@link demoShell}. */\nexport type DemoShellHandle = {\n\t/** The demo-shell graph. */\n\tgraph: Graph;\n\n\t// ── Convenience setters (shorthand for graph.set) ──────────\n\tsetMainRatio(ratio: number): void;\n\tsetSideSplit(ratio: number): void;\n\tsetFullscreen(pane: FullscreenPane): void;\n\tsetViewportWidth(width: number): void;\n\tsetHoverTarget(target: HoverTarget): void;\n\tsetDemoGraph(g: Graph | null): void;\n\tbumpGraphTick(): void;\n\tselectNode(path: string | null): void;\n\tsetMetaDebug(on: boolean): void;\n\t/** Set code text for layout/code-lines measurement (requires adapter). */\n\tsetCodeText(text: string): void;\n\t/** Atomic multi-set — wraps core `batch()` for glitch-free updates. */\n\tbatch(fn: () => void): void;\n\tdestroy(): void;\n};\n\n// ——————————————————————————————————————————————————————————\n// Helpers\n// ——————————————————————————————————————————————————————————\n\nfunction clamp01(v: number): number {\n\treturn Math.max(0, Math.min(1, v));\n}\n\n// ——————————————————————————————————————————————————————————\n// Factory\n// ——————————————————————————————————————————————————————————\n\n/**\n * Creates the three-pane demo shell graph (roadmap §7.2).\n *\n * All coordination is reactive — no polling, no imperative triggers.\n * Framework bindings subscribe to named nodes and drive `state` inputs.\n */\nexport function demoShell(opts?: DemoShellOptions): DemoShellHandle {\n\tconst mainRatioInit = clamp01(opts?.mainRatio ?? 0.65);\n\tconst sideSplitInit = clamp01(opts?.sideSplit ?? 0.5);\n\tconst viewportInit = Math.max(0, opts?.viewportWidth ?? 1280);\n\tconst registry = opts?.nodeRegistry ?? new Map();\n\tconst adapter = opts?.adapter ?? null;\n\tconst layoutFont = opts?.layoutFont ?? \"14px monospace\";\n\tconst onHighlight = opts?.onHighlight;\n\n\tconst g = new Graph(\"demo-shell\");\n\n\t// ── Layout state ─────────────────────────────────────\n\tconst paneMainRatio = state(mainRatioInit, { name: \"pane/main-ratio\" });\n\tconst paneSideSplit = state(sideSplitInit, { name: \"pane/side-split\" });\n\tconst paneFullscreen = state<FullscreenPane>(null, {\n\t\tname: \"pane/fullscreen\",\n\t});\n\tconst viewportWidth = state(viewportInit, { name: \"viewport/width\" });\n\n\tg.add(\"pane/main-ratio\", paneMainRatio);\n\tg.add(\"pane/side-split\", paneSideSplit);\n\tg.add(\"pane/fullscreen\", paneFullscreen);\n\tg.add(\"viewport/width\", viewportWidth);\n\n\t// ── Derived pane dimensions ──────────────────────────\n\tconst paneMainWidth = derived(\n\t\t[paneMainRatio, viewportWidth, paneFullscreen],\n\t\t([ratio, vw, fs]) => {\n\t\t\tconst r = ratio as number;\n\t\t\tconst w = vw as number;\n\t\t\tconst fullscreen = fs as FullscreenPane;\n\t\t\tif (fullscreen === \"main\") return w;\n\t\t\tif (fullscreen === \"graph\" || fullscreen === \"code\") return 0;\n\t\t\treturn Math.round(w * r);\n\t\t},\n\t\t{ name: \"pane/main-width\" },\n\t);\n\n\tconst paneSideWidth = derived(\n\t\t[paneMainWidth, viewportWidth, paneFullscreen],\n\t\t([main, vw, fs]) => {\n\t\t\tconst fullscreen = fs as FullscreenPane;\n\t\t\tconst w = vw as number;\n\t\t\tif (fullscreen === \"main\") return 0;\n\t\t\tif (fullscreen === \"graph\" || fullscreen === \"code\") return w;\n\t\t\treturn (w as number) - (main as number);\n\t\t},\n\t\t{ name: \"pane/side-width\" },\n\t);\n\n\tconst paneGraphHeight = derived(\n\t\t[paneSideSplit, paneFullscreen],\n\t\t([split, fs]) => {\n\t\t\tconst fullscreen = fs as FullscreenPane;\n\t\t\tif (fullscreen === \"graph\") return 1;\n\t\t\tif (fullscreen === \"code\") return 0;\n\t\t\tif (fullscreen === \"main\") return 0;\n\t\t\treturn clamp01(split as number);\n\t\t},\n\t\t{ name: \"pane/graph-height-ratio\" },\n\t);\n\n\tconst paneCodeHeight = derived(\n\t\t[paneGraphHeight, paneFullscreen],\n\t\t([graphH, fs]) => {\n\t\t\tconst fullscreen = fs as FullscreenPane;\n\t\t\tif (fullscreen === \"code\") return 1;\n\t\t\tif (fullscreen === \"graph\" || fullscreen === \"main\") return 0;\n\t\t\treturn 1 - (graphH as number);\n\t\t},\n\t\t{ name: \"pane/code-height-ratio\" },\n\t);\n\n\tg.add(\"pane/main-width\", paneMainWidth);\n\tg.add(\"pane/side-width\", paneSideWidth);\n\tg.add(\"pane/graph-height-ratio\", paneGraphHeight);\n\tg.add(\"pane/code-height-ratio\", paneCodeHeight);\n\n\t// ── External graph observation ───────────────────────\n\tconst demoGraphRef = state<Graph | null>(null, {\n\t\tname: \"demo/graph-ref\",\n\t});\n\tconst demoGraphTick = state(0, { name: \"demo/graph-tick\" });\n\n\tg.add(\"demo/graph-ref\", demoGraphRef);\n\tg.add(\"demo/graph-tick\", demoGraphTick);\n\n\tconst graphMermaid = derived(\n\t\t[demoGraphRef, demoGraphTick],\n\t\t([ref, _tick]) => {\n\t\t\tconst demo = ref as Graph | null;\n\t\t\tif (!demo) return \"\";\n\t\t\treturn demo.describe({ format: \"mermaid\" });\n\t\t},\n\t\t{ name: \"graph/mermaid\" },\n\t);\n\n\tconst graphDescribe = derived(\n\t\t[demoGraphRef, demoGraphTick],\n\t\t([ref, _tick]) => {\n\t\t\tconst demo = ref as Graph | null;\n\t\t\tif (!demo) return null;\n\t\t\tconst { expand: _, ...snapshot } = demo.describe({ detail: \"standard\" });\n\t\t\treturn snapshot;\n\t\t},\n\t\t{ name: \"graph/describe\" },\n\t);\n\n\tg.add(\"graph/mermaid\", graphMermaid);\n\tg.add(\"graph/describe\", graphDescribe);\n\n\t// ── Cross-highlighting ───────────────────────────────\n\tconst hoverTarget = state<HoverTarget>(null, { name: \"hover/target\" });\n\tg.add(\"hover/target\", hoverTarget);\n\n\tconst highlightCodeScroll = derived(\n\t\t[hoverTarget],\n\t\t([target]) => {\n\t\t\tconst t = target as HoverTarget;\n\t\t\tif (!t) return null;\n\t\t\tconst entry = registry.get(t.id);\n\t\t\treturn entry ? entry.codeLine : null;\n\t\t},\n\t\t{ name: \"highlight/code-scroll\" },\n\t);\n\n\tconst highlightVisual = derived(\n\t\t[hoverTarget],\n\t\t([target]) => {\n\t\t\tconst t = target as HoverTarget;\n\t\t\tif (!t) return null;\n\t\t\tconst entry = registry.get(t.id);\n\t\t\treturn entry ? entry.visualSelector : null;\n\t\t},\n\t\t{ name: \"highlight/visual\" },\n\t);\n\n\tconst highlightGraph = derived(\n\t\t[hoverTarget],\n\t\t([target]) => {\n\t\t\tconst t = target as HoverTarget;\n\t\t\tif (!t) return null;\n\t\t\treturn t.id;\n\t\t},\n\t\t{ name: \"highlight/graph\" },\n\t);\n\n\tg.add(\"highlight/code-scroll\", highlightCodeScroll);\n\tg.add(\"highlight/visual\", highlightVisual);\n\tg.add(\"highlight/graph\", highlightGraph);\n\n\t// ── Cross-highlighting effect nodes (optional) ─────\n\t// Created when onHighlight callbacks are provided, making the full\n\t// source→derived→effect chain visible in describe()/toMermaid().\n\n\tif (onHighlight?.codeScroll) {\n\t\tconst cb = onHighlight.codeScroll;\n\t\tconst applyCodeScroll = effect([highlightCodeScroll], ([line]) => {\n\t\t\tcb(line as number | null);\n\t\t});\n\t\tg.add(\"highlight/apply-code-scroll\", applyCodeScroll);\n\t}\n\n\tif (onHighlight?.visual) {\n\t\tconst cb = onHighlight.visual;\n\t\tconst applyVisual = effect([highlightVisual], ([selector]) => {\n\t\t\tcb(selector as string | null);\n\t\t});\n\t\tg.add(\"highlight/apply-visual\", applyVisual);\n\t}\n\n\tif (onHighlight?.graph) {\n\t\tconst cb = onHighlight.graph;\n\t\tconst applyGraph = effect([highlightGraph], ([nodeId]) => {\n\t\t\tcb(nodeId as string | null);\n\t\t});\n\t\tg.add(\"highlight/apply-graph\", applyGraph);\n\t}\n\n\t// ── Inspect panel ────────────────────────────────────\n\tconst inspectSelected = state<string | null>(null, {\n\t\tname: \"inspect/selected-node\",\n\t});\n\tg.add(\"inspect/selected-node\", inspectSelected);\n\n\tconst standardFields = resolveDescribeFields(\"standard\");\n\n\tconst inspectNodeDetail = derived(\n\t\t[inspectSelected, demoGraphRef, demoGraphTick],\n\t\t([path, ref, _tick]) => {\n\t\t\tconst demo = ref as Graph | null;\n\t\t\tconst p = path as string | null;\n\t\t\tif (!demo || !p) return null;\n\t\t\ttry {\n\t\t\t\tconst nd = demo.resolve(p);\n\t\t\t\tconst nodeDesc = describeNode(nd, standardFields);\n\t\t\t\treturn { path: p, ...nodeDesc, value: nd.cache };\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\t{ name: \"inspect/node-detail\" },\n\t);\n\n\tconst inspectTraceLog = derived(\n\t\t[demoGraphRef, demoGraphTick],\n\t\t([ref, _tick]) => {\n\t\t\tconst demo = ref as Graph | null;\n\t\t\tif (!demo) return [];\n\t\t\treturn demo.trace();\n\t\t},\n\t\t{ name: \"inspect/trace-log\" },\n\t);\n\n\tg.add(\"inspect/node-detail\", inspectNodeDetail);\n\tg.add(\"inspect/trace-log\", inspectTraceLog);\n\n\t// ── Meta debug toggle ────────────────────────────────\n\tconst metaDebug = state(false, { name: \"meta/debug\" });\n\tg.add(\"meta/debug\", metaDebug);\n\n\tconst metaShellMermaid = derived(\n\t\t[metaDebug, demoGraphTick],\n\t\t([debug, _tick]) => {\n\t\t\tif (!(debug as boolean)) return \"\";\n\t\t\treturn g.describe({ format: \"mermaid\" });\n\t\t},\n\t\t{ name: \"meta/shell-mermaid\" },\n\t);\n\tg.add(\"meta/shell-mermaid\", metaShellMermaid);\n\n\t// ── Layout engine integration (optional, requires adapter) ──\n\tconst codeTextNode = state(\"\", { name: \"layout/code-text\" });\n\tg.add(\"layout/code-text\", codeTextNode);\n\n\tif (adapter) {\n\t\tconst measureCache = new Map<string, Map<string, number>>();\n\n\t\tconst graphLabels = derived(\n\t\t\t[graphDescribe],\n\t\t\t([desc]) => {\n\t\t\t\tconst d = desc as { nodes: Record<string, { type: string }> } | null;\n\t\t\t\tif (!d) return new Map<string, GraphLabelSize>();\n\t\t\t\tconst result = new Map<string, GraphLabelSize>();\n\t\t\t\tfor (const [name] of Object.entries(d.nodes)) {\n\t\t\t\t\tconst segments = analyzeAndMeasure(name, layoutFont, adapter, measureCache);\n\t\t\t\t\tconst lb = computeLineBreaks(segments, Infinity, adapter, layoutFont, measureCache);\n\t\t\t\t\tconst width = lb.lines.reduce((max, l) => Math.max(max, l.width), 0);\n\t\t\t\t\tconst height = lb.lineCount * 20; // line-height approximation\n\t\t\t\t\tresult.set(name, { width, height });\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"layout/graph-labels\",\n\t\t\t\tequals: (a, b) => {\n\t\t\t\t\tif (a === b) return true;\n\t\t\t\t\tconst ma = a as Map<string, GraphLabelSize>;\n\t\t\t\t\tconst mb = b as Map<string, GraphLabelSize>;\n\t\t\t\t\tif (ma.size !== mb.size) return false;\n\t\t\t\t\tfor (const [k, v] of ma) {\n\t\t\t\t\t\tconst bv = mb.get(k);\n\t\t\t\t\t\tif (!bv || bv.width !== v.width || bv.height !== v.height) return false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tconst codeLines = derived(\n\t\t\t[codeTextNode, paneSideWidth],\n\t\t\t([text, sideW]) => {\n\t\t\t\tconst t = text as string;\n\t\t\t\tif (!t) return { lineCount: 0, lines: [] };\n\t\t\t\tconst segments = analyzeAndMeasure(t, layoutFont, adapter, measureCache);\n\t\t\t\tconst maxW = (sideW as number) - 40; // side pane minus padding\n\t\t\t\treturn computeLineBreaks(segments, Math.max(100, maxW), adapter, layoutFont, measureCache);\n\t\t\t},\n\t\t\t{ name: \"layout/code-lines\" },\n\t\t);\n\n\t\tconst sideWidthHint = derived(\n\t\t\t[graphLabels],\n\t\t\t([labels]) => {\n\t\t\t\tconst m = labels as Map<string, GraphLabelSize>;\n\t\t\t\tif (m.size === 0) return 200; // minimum default\n\t\t\t\tlet maxW = 0;\n\t\t\t\tfor (const { width } of m.values()) {\n\t\t\t\t\tif (width > maxW) maxW = width;\n\t\t\t\t}\n\t\t\t\t// widest label + padding (node box chrome + margin)\n\t\t\t\treturn Math.max(200, Math.round(maxW + 80));\n\t\t\t},\n\t\t\t{ name: \"layout/side-width-hint\" },\n\t\t);\n\n\t\tg.add(\"layout/graph-labels\", graphLabels);\n\t\tg.add(\"layout/code-lines\", codeLines);\n\t\tg.add(\"layout/side-width-hint\", sideWidthHint);\n\t}\n\n\t// ── Edges (explicit wiring for describe/toMermaid) ───\n\n\t// ── Handle ───────────────────────────────────────────\n\tlet tickCounter = 0;\n\treturn {\n\t\tgraph: g,\n\t\tsetMainRatio(ratio: number) {\n\t\t\tg.set(\"pane/main-ratio\", clamp01(ratio));\n\t\t},\n\t\tsetSideSplit(ratio: number) {\n\t\t\tg.set(\"pane/side-split\", clamp01(ratio));\n\t\t},\n\t\tsetFullscreen(pane: FullscreenPane) {\n\t\t\tg.set(\"pane/fullscreen\", pane);\n\t\t},\n\t\tsetViewportWidth(width: number) {\n\t\t\tg.set(\"viewport/width\", Math.max(0, width));\n\t\t},\n\t\tsetHoverTarget(target: HoverTarget) {\n\t\t\tg.set(\"hover/target\", target);\n\t\t},\n\t\tsetDemoGraph(demo: Graph | null) {\n\t\t\tg.set(\"demo/graph-ref\", demo);\n\t\t},\n\t\tbumpGraphTick() {\n\t\t\tg.set(\"demo/graph-tick\", ++tickCounter);\n\t\t},\n\t\tselectNode(path: string | null) {\n\t\t\tg.set(\"inspect/selected-node\", path);\n\t\t},\n\t\tsetMetaDebug(on: boolean) {\n\t\t\tg.set(\"meta/debug\", on);\n\t\t},\n\t\tsetCodeText(text: string) {\n\t\t\tg.set(\"layout/code-text\", text);\n\t\t},\n\t\tbatch(fn: () => void) {\n\t\t\tbatch(fn);\n\t\t},\n\t\tdestroy() {\n\t\t\tg.destroy();\n\t\t},\n\t};\n}\n"],"mappings":"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","names":[]}