@immediately-run/sdk 0.57.1 → 0.58.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 (131) hide show
  1. package/dist/agentChatClient.cjs +112 -0
  2. package/dist/agentChatClient.cjs.map +1 -0
  3. package/dist/agentChatClient.d.cts +9 -0
  4. package/dist/agentChatClient.d.ts +9 -0
  5. package/dist/agentChatClient.js +89 -0
  6. package/dist/agentChatClient.js.map +1 -0
  7. package/dist/agentContext.cjs +69 -0
  8. package/dist/agentContext.cjs.map +1 -0
  9. package/dist/agentContext.d.cts +45 -0
  10. package/dist/agentContext.d.ts +45 -0
  11. package/dist/agentContext.js +45 -0
  12. package/dist/agentContext.js.map +1 -0
  13. package/dist/agentLoop.cjs +301 -0
  14. package/dist/agentLoop.cjs.map +1 -0
  15. package/dist/agentLoop.d.cts +267 -0
  16. package/dist/agentLoop.d.ts +267 -0
  17. package/dist/agentLoop.js +271 -0
  18. package/dist/agentLoop.js.map +1 -0
  19. package/dist/agentSteering.cjs +127 -0
  20. package/dist/agentSteering.cjs.map +1 -0
  21. package/dist/agentSteering.d.cts +80 -0
  22. package/dist/agentSteering.d.ts +80 -0
  23. package/dist/agentSteering.js +98 -0
  24. package/dist/agentSteering.js.map +1 -0
  25. package/dist/auth.cjs.map +1 -1
  26. package/dist/auth.d.cts +33 -2
  27. package/dist/auth.d.ts +33 -2
  28. package/dist/auth.js.map +1 -1
  29. package/dist/bundle.cjs +91 -0
  30. package/dist/bundle.cjs.map +1 -0
  31. package/dist/bundle.d.cts +63 -0
  32. package/dist/bundle.d.ts +63 -0
  33. package/dist/bundle.js +62 -0
  34. package/dist/bundle.js.map +1 -0
  35. package/dist/collectHeadings.cjs +59 -0
  36. package/dist/collectHeadings.cjs.map +1 -0
  37. package/dist/collectHeadings.d.cts +16 -0
  38. package/dist/collectHeadings.d.ts +16 -0
  39. package/dist/collectHeadings.js +36 -0
  40. package/dist/collectHeadings.js.map +1 -0
  41. package/dist/corpus.cjs +9 -58
  42. package/dist/corpus.cjs.map +1 -1
  43. package/dist/corpus.d.cts +3 -63
  44. package/dist/corpus.d.ts +3 -63
  45. package/dist/corpus.js +15 -57
  46. package/dist/corpus.js.map +1 -1
  47. package/dist/debug.cjs +115 -0
  48. package/dist/debug.cjs.map +1 -1
  49. package/dist/debug.js +115 -0
  50. package/dist/debug.js.map +1 -1
  51. package/dist/fence.cjs +49 -0
  52. package/dist/fence.cjs.map +1 -0
  53. package/dist/fence.d.cts +4 -0
  54. package/dist/fence.d.ts +4 -0
  55. package/dist/fence.js +26 -0
  56. package/dist/fence.js.map +1 -0
  57. package/dist/formFactor.cjs.map +1 -1
  58. package/dist/formFactor.d.cts +11 -2
  59. package/dist/formFactor.d.ts +11 -2
  60. package/dist/formFactor.js.map +1 -1
  61. package/dist/fs.cjs.map +1 -1
  62. package/dist/fs.d.cts +16 -6
  63. package/dist/fs.d.ts +16 -6
  64. package/dist/fs.js.map +1 -1
  65. package/dist/hooks.cjs.map +1 -1
  66. package/dist/hooks.d.cts +7 -0
  67. package/dist/hooks.d.ts +7 -0
  68. package/dist/hooks.js.map +1 -1
  69. package/dist/index.cjs +21 -1
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.cts +12 -3
  72. package/dist/index.d.ts +12 -3
  73. package/dist/index.js +10 -0
  74. package/dist/index.js.map +1 -1
  75. package/dist/launch.cjs +45 -6
  76. package/dist/launch.cjs.map +1 -1
  77. package/dist/launch.d.cts +3 -0
  78. package/dist/launch.d.ts +3 -0
  79. package/dist/launch.js +45 -6
  80. package/dist/launch.js.map +1 -1
  81. package/dist/llm.cjs.map +1 -1
  82. package/dist/llm.d.cts +20 -3
  83. package/dist/llm.d.ts +20 -3
  84. package/dist/llm.js.map +1 -1
  85. package/dist/metadataQueryTool.cjs +271 -0
  86. package/dist/metadataQueryTool.cjs.map +1 -0
  87. package/dist/metadataQueryTool.d.cts +159 -0
  88. package/dist/metadataQueryTool.d.ts +159 -0
  89. package/dist/metadataQueryTool.js +241 -0
  90. package/dist/metadataQueryTool.js.map +1 -0
  91. package/dist/mounts.cjs.map +1 -1
  92. package/dist/mounts.d.cts +22 -3
  93. package/dist/mounts.d.ts +22 -3
  94. package/dist/mounts.js.map +1 -1
  95. package/dist/onFsChange.cjs +13 -1
  96. package/dist/onFsChange.cjs.map +1 -1
  97. package/dist/onFsChange.d.cts +21 -1
  98. package/dist/onFsChange.d.ts +21 -1
  99. package/dist/onFsChange.js +13 -1
  100. package/dist/onFsChange.js.map +1 -1
  101. package/dist/protocolSchemes.cjs +1 -0
  102. package/dist/protocolSchemes.cjs.map +1 -1
  103. package/dist/protocolSchemes.d.cts +1 -0
  104. package/dist/protocolSchemes.d.ts +1 -0
  105. package/dist/protocolSchemes.js +2 -0
  106. package/dist/protocolSchemes.js.map +1 -1
  107. package/dist/recents.cjs +41 -0
  108. package/dist/recents.cjs.map +1 -0
  109. package/dist/recents.d.cts +23 -0
  110. package/dist/recents.d.ts +23 -0
  111. package/dist/recents.js +17 -0
  112. package/dist/recents.js.map +1 -0
  113. package/dist/tasks.cjs +41 -7
  114. package/dist/tasks.cjs.map +1 -1
  115. package/dist/tasks.d.cts +136 -7
  116. package/dist/tasks.d.ts +136 -7
  117. package/dist/tasks.js +39 -7
  118. package/dist/tasks.js.map +1 -1
  119. package/dist/version.cjs +1 -1
  120. package/dist/version.cjs.map +1 -1
  121. package/dist/version.d.cts +1 -1
  122. package/dist/version.d.ts +1 -1
  123. package/dist/version.js +1 -1
  124. package/dist/version.js.map +1 -1
  125. package/dist/workspace.cjs +53 -0
  126. package/dist/workspace.cjs.map +1 -0
  127. package/dist/workspace.d.cts +58 -0
  128. package/dist/workspace.d.ts +58 -0
  129. package/dist/workspace.js +28 -0
  130. package/dist/workspace.js.map +1 -0
  131. package/package.json +6 -5
@@ -0,0 +1,80 @@
1
+ /** How a steer applies. */
2
+ type SteerMode =
3
+ /** Apply at the next turn boundary — the in-flight turn finishes first. */
4
+ 'queue'
5
+ /** Abort the in-flight model turn now, then apply. Never interrupts a tool call. */
6
+ | 'interrupt';
7
+ interface SteerMessage {
8
+ id: string;
9
+ text: string;
10
+ mode: SteerMode;
11
+ }
12
+ /** Prefix marking a `user` message as a STEER rather than something typed at the
13
+ * start of a run — so a replayed conversation shows the interruption where it
14
+ * happened, instead of reconstructing a history that never ran (scope 5). Mirrors
15
+ * `COMPACTION_MARKER`/`NUDGE_TEXT`, which the transcript renderer already keys on. */
16
+ declare const STEER_MARKER = "\u241F[steer]\n";
17
+ /** As above, for a steer that ABORTED an in-flight turn. */
18
+ declare const STEER_INTERRUPT_MARKER = "\u241F[steer:interrupt]\n";
19
+ /** Text recorded in place of the assistant turn a steer cut short. The turn has to
20
+ * appear in the transcript — dropping it would put two `user` messages back to back
21
+ * and lose the fact that the model was mid-sentence when the user cut in. */
22
+ declare const INTERRUPTED_TURN_TEXT = "(turn interrupted by the user)";
23
+ /** Wrap a steer's text for the wire. */
24
+ declare const steerWireText: (m: SteerMessage) => string;
25
+ /** Recognise a steer on replay; returns the mode + text, or `null`. */
26
+ declare function parseSteer(text: string): {
27
+ mode: SteerMode;
28
+ text: string;
29
+ } | null;
30
+ /** What the loop needs from a steering source (so it can be faked in tests). */
31
+ interface SteerSource {
32
+ /** Remove and return everything pending, in arrival order. */
33
+ drain(): SteerMessage[];
34
+ /** Is anything waiting? Checked before the loop would otherwise END, so a
35
+ * follow-up queued against a finishing run continues it instead. */
36
+ hasPending(): boolean;
37
+ /** Fires when an `interrupt`-mode steer arrives. The loop composes it with the
38
+ * stop signal for the in-flight model call. */
39
+ readonly interrupt: AbortSignal;
40
+ /** Called by the loop once an interrupt has been consumed, so the next turn is
41
+ * not aborted by a stale signal. */
42
+ rearm(): void;
43
+ }
44
+ /**
45
+ * The live steering queue. Owned by the UI (which enqueues and cancels) and read
46
+ * by the loop (which drains at a turn boundary).
47
+ */
48
+ declare class SteerController implements SteerSource {
49
+ private queue;
50
+ private controller;
51
+ private listeners;
52
+ /** Queue a correction. Returns its id so the UI can cancel it while it waits. */
53
+ enqueue(text: string, mode?: SteerMode): SteerMessage | null;
54
+ /** Drop a queued steer that has not been applied yet. */
55
+ cancel(id: string): boolean;
56
+ /** Everything still waiting, for the UI's "queued" affordance. */
57
+ pending(): readonly SteerMessage[];
58
+ hasPending(): boolean;
59
+ drain(): SteerMessage[];
60
+ get interrupt(): AbortSignal;
61
+ rearm(): void;
62
+ /** Subscribe to queue changes (the UI re-renders its queued chips). */
63
+ onChange(listener: (pending: readonly SteerMessage[]) => void): () => void;
64
+ private emit;
65
+ }
66
+ /**
67
+ * Combine abort signals into one that fires when any of them does.
68
+ *
69
+ * `AbortSignal.any` exists in modern engines but not everywhere the sandbox runs,
70
+ * and a silent `undefined` here would mean the stop button quietly stops working —
71
+ * so this is explicit, and returns a disposer the caller uses to drop its listeners
72
+ * (a per-turn signal that stayed subscribed to a run-long controller would leak one
73
+ * listener per turn on a long run).
74
+ */
75
+ declare function anySignal(signals: Array<AbortSignal | undefined>): {
76
+ signal: AbortSignal;
77
+ dispose: () => void;
78
+ };
79
+
80
+ export { INTERRUPTED_TURN_TEXT, STEER_INTERRUPT_MARKER, STEER_MARKER, SteerController, type SteerMessage, type SteerMode, type SteerSource, anySignal, parseSteer, steerWireText };
@@ -0,0 +1,98 @@
1
+ import "./chunk-VHAA22YE.js";
2
+ const STEER_MARKER = "\u241F[steer]\n";
3
+ const STEER_INTERRUPT_MARKER = "\u241F[steer:interrupt]\n";
4
+ const INTERRUPTED_TURN_TEXT = "(turn interrupted by the user)";
5
+ const steerWireText = (m) => (m.mode === "interrupt" ? STEER_INTERRUPT_MARKER : STEER_MARKER) + m.text;
6
+ function parseSteer(text) {
7
+ if (text.startsWith(STEER_INTERRUPT_MARKER)) {
8
+ return { mode: "interrupt", text: text.slice(STEER_INTERRUPT_MARKER.length) };
9
+ }
10
+ if (text.startsWith(STEER_MARKER)) return { mode: "queue", text: text.slice(STEER_MARKER.length) };
11
+ return null;
12
+ }
13
+ let seq = 0;
14
+ const nextId = () => {
15
+ seq += 1;
16
+ return `steer-${Date.now().toString(36)}-${seq}`;
17
+ };
18
+ class SteerController {
19
+ constructor() {
20
+ this.queue = [];
21
+ this.controller = new AbortController();
22
+ this.listeners = /* @__PURE__ */ new Set();
23
+ }
24
+ /** Queue a correction. Returns its id so the UI can cancel it while it waits. */
25
+ enqueue(text, mode = "queue") {
26
+ const trimmed = text.trim();
27
+ if (!trimmed) return null;
28
+ const msg = { id: nextId(), text: trimmed, mode };
29
+ this.queue.push(msg);
30
+ if (mode === "interrupt") this.controller.abort();
31
+ this.emit();
32
+ return msg;
33
+ }
34
+ /** Drop a queued steer that has not been applied yet. */
35
+ cancel(id) {
36
+ const before = this.queue.length;
37
+ this.queue = this.queue.filter((m) => m.id !== id);
38
+ if (this.queue.length === before) return false;
39
+ if (!this.queue.some((m) => m.mode === "interrupt")) this.rearm();
40
+ this.emit();
41
+ return true;
42
+ }
43
+ /** Everything still waiting, for the UI's "queued" affordance. */
44
+ pending() {
45
+ return this.queue;
46
+ }
47
+ hasPending() {
48
+ return this.queue.length > 0;
49
+ }
50
+ drain() {
51
+ const out = this.queue;
52
+ this.queue = [];
53
+ if (out.length) this.emit();
54
+ return out;
55
+ }
56
+ get interrupt() {
57
+ return this.controller.signal;
58
+ }
59
+ rearm() {
60
+ if (this.controller.signal.aborted) this.controller = new AbortController();
61
+ }
62
+ /** Subscribe to queue changes (the UI re-renders its queued chips). */
63
+ onChange(listener) {
64
+ this.listeners.add(listener);
65
+ return () => this.listeners.delete(listener);
66
+ }
67
+ emit() {
68
+ for (const l of this.listeners) l(this.queue);
69
+ }
70
+ }
71
+ function anySignal(signals) {
72
+ const live = signals.filter((s) => !!s);
73
+ const controller = new AbortController();
74
+ const already = live.find((s) => s.aborted);
75
+ if (already) {
76
+ controller.abort(already.reason);
77
+ return { signal: controller.signal, dispose: () => {
78
+ } };
79
+ }
80
+ const onAbort = (e) => controller.abort(e.target.reason);
81
+ for (const s of live) s.addEventListener("abort", onAbort, { once: true });
82
+ return {
83
+ signal: controller.signal,
84
+ dispose: () => {
85
+ for (const s of live) s.removeEventListener("abort", onAbort);
86
+ }
87
+ };
88
+ }
89
+ export {
90
+ INTERRUPTED_TURN_TEXT,
91
+ STEER_INTERRUPT_MARKER,
92
+ STEER_MARKER,
93
+ SteerController,
94
+ anySignal,
95
+ parseSteer,
96
+ steerWireText
97
+ };
98
+ //# sourceMappingURL=agentSteering.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agentSteering.ts"],"sourcesContent":["// Steering + follow-up queue (R3-333 / AHG-T2-3).\n//\n// WHY. R3-224 resolved the hard half — `ChatRequest.signal`, per-message abort in\n// the host that stops the upstream generator and the billing with it, and\n// `runAgent(signal)` behind the stop button. What sits on top of it was never\n// built: the human's vocabulary was still START and KILL. There was no way to say\n// \"yes, but not that file\" or \"stop and do this instead\", or to queue a follow-up\n// while a turn is in flight. Correction meant restart — which discards the\n// transcript's accumulated understanding and pays for it again.\n//\n// This is the app-layer half: a queue the loop drains at a turn boundary, plus an\n// INTERRUPT signal that aborts the in-flight model turn without ending the run.\n//\n// THE TWO VERBS ARE NOT THE SAME.\n// * STOP — `RunAgentOptions.signal`. Ends the run. The transcript so far is kept.\n// * STEER — this controller. Ends the current TURN (or waits for the next\n// boundary), injects the user's correction, and CONTINUES.\n// They are distinguishable in the transcript (`STEER_MARKER`) and in the UI, which\n// exit criterion 3 requires.\n//\n// WHY A STEER NEVER INTERRUPTS TOOL EXECUTION. A turn's `tool_use` blocks must each\n// get a matching `tool_result`, or the next request is malformed and the provider\n// rejects the whole conversation. So the interrupt signal only ever aborts the\n// in-flight MODEL call; a steer that arrives while tools are running is applied at\n// the boundary after the batch completes. That is exit criterion 5, and it is a\n// property of the design rather than a race the loop has to win.\n\n/** How a steer applies. */\nexport type SteerMode =\n /** Apply at the next turn boundary — the in-flight turn finishes first. */\n | 'queue'\n /** Abort the in-flight model turn now, then apply. Never interrupts a tool call. */\n | 'interrupt';\n\nexport interface SteerMessage {\n id: string;\n text: string;\n mode: SteerMode;\n}\n\n/** Prefix marking a `user` message as a STEER rather than something typed at the\n * start of a run — so a replayed conversation shows the interruption where it\n * happened, instead of reconstructing a history that never ran (scope 5). Mirrors\n * `COMPACTION_MARKER`/`NUDGE_TEXT`, which the transcript renderer already keys on. */\nexport const STEER_MARKER = '␟[steer]\\n';\n/** As above, for a steer that ABORTED an in-flight turn. */\nexport const STEER_INTERRUPT_MARKER = '␟[steer:interrupt]\\n';\n\n/** Text recorded in place of the assistant turn a steer cut short. The turn has to\n * appear in the transcript — dropping it would put two `user` messages back to back\n * and lose the fact that the model was mid-sentence when the user cut in. */\nexport const INTERRUPTED_TURN_TEXT = '(turn interrupted by the user)';\n\n/** Wrap a steer's text for the wire. */\nexport const steerWireText = (m: SteerMessage): string =>\n (m.mode === 'interrupt' ? STEER_INTERRUPT_MARKER : STEER_MARKER) + m.text;\n\n/** Recognise a steer on replay; returns the mode + text, or `null`. */\nexport function parseSteer(text: string): { mode: SteerMode; text: string } | null {\n if (text.startsWith(STEER_INTERRUPT_MARKER)) {\n return { mode: 'interrupt', text: text.slice(STEER_INTERRUPT_MARKER.length) };\n }\n if (text.startsWith(STEER_MARKER)) return { mode: 'queue', text: text.slice(STEER_MARKER.length) };\n return null;\n}\n\n/** What the loop needs from a steering source (so it can be faked in tests). */\nexport interface SteerSource {\n /** Remove and return everything pending, in arrival order. */\n drain(): SteerMessage[];\n /** Is anything waiting? Checked before the loop would otherwise END, so a\n * follow-up queued against a finishing run continues it instead. */\n hasPending(): boolean;\n /** Fires when an `interrupt`-mode steer arrives. The loop composes it with the\n * stop signal for the in-flight model call. */\n readonly interrupt: AbortSignal;\n /** Called by the loop once an interrupt has been consumed, so the next turn is\n * not aborted by a stale signal. */\n rearm(): void;\n}\n\nlet seq = 0;\nconst nextId = (): string => {\n // `crypto.randomUUID` is not available in every host the app runs in; the id only\n // has to be unique within one run, so a counter + timestamp is enough.\n seq += 1;\n return `steer-${Date.now().toString(36)}-${seq}`;\n};\n\n/**\n * The live steering queue. Owned by the UI (which enqueues and cancels) and read\n * by the loop (which drains at a turn boundary).\n */\nexport class SteerController implements SteerSource {\n private queue: SteerMessage[] = [];\n private controller = new AbortController();\n private listeners = new Set<(pending: readonly SteerMessage[]) => void>();\n\n /** Queue a correction. Returns its id so the UI can cancel it while it waits. */\n enqueue(text: string, mode: SteerMode = 'queue'): SteerMessage | null {\n const trimmed = text.trim();\n if (!trimmed) return null;\n const msg: SteerMessage = { id: nextId(), text: trimmed, mode };\n this.queue.push(msg);\n if (mode === 'interrupt') this.controller.abort();\n this.emit();\n return msg;\n }\n\n /** Drop a queued steer that has not been applied yet. */\n cancel(id: string): boolean {\n const before = this.queue.length;\n this.queue = this.queue.filter((m) => m.id !== id);\n if (this.queue.length === before) return false;\n // Cancelling the only interrupt leaves a fired signal behind; re-arm so the\n // next turn is not aborted by a correction the user took back.\n if (!this.queue.some((m) => m.mode === 'interrupt')) this.rearm();\n this.emit();\n return true;\n }\n\n /** Everything still waiting, for the UI's \"queued\" affordance. */\n pending(): readonly SteerMessage[] {\n return this.queue;\n }\n\n hasPending(): boolean {\n return this.queue.length > 0;\n }\n\n drain(): SteerMessage[] {\n const out = this.queue;\n this.queue = [];\n if (out.length) this.emit();\n return out;\n }\n\n get interrupt(): AbortSignal {\n return this.controller.signal;\n }\n\n rearm(): void {\n if (this.controller.signal.aborted) this.controller = new AbortController();\n }\n\n /** Subscribe to queue changes (the UI re-renders its queued chips). */\n onChange(listener: (pending: readonly SteerMessage[]) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private emit(): void {\n for (const l of this.listeners) l(this.queue);\n }\n}\n\n/**\n * Combine abort signals into one that fires when any of them does.\n *\n * `AbortSignal.any` exists in modern engines but not everywhere the sandbox runs,\n * and a silent `undefined` here would mean the stop button quietly stops working —\n * so this is explicit, and returns a disposer the caller uses to drop its listeners\n * (a per-turn signal that stayed subscribed to a run-long controller would leak one\n * listener per turn on a long run).\n */\nexport function anySignal(signals: Array<AbortSignal | undefined>): {\n signal: AbortSignal;\n dispose: () => void;\n} {\n const live = signals.filter((s): s is AbortSignal => !!s);\n const controller = new AbortController();\n const already = live.find((s) => s.aborted);\n if (already) {\n controller.abort(already.reason);\n return { signal: controller.signal, dispose: () => {} };\n }\n const onAbort = (e: Event): void => controller.abort((e.target as AbortSignal).reason);\n for (const s of live) s.addEventListener('abort', onAbort, { once: true });\n return {\n signal: controller.signal,\n dispose: () => {\n for (const s of live) s.removeEventListener('abort', onAbort);\n },\n };\n}\n"],"mappings":";AA4CO,MAAM,eAAe;AAErB,MAAM,yBAAyB;AAK/B,MAAM,wBAAwB;AAG9B,MAAM,gBAAgB,CAAC,OAC3B,EAAE,SAAS,cAAc,yBAAyB,gBAAgB,EAAE;AAGhE,SAAS,WAAW,MAAwD;AACjF,MAAI,KAAK,WAAW,sBAAsB,GAAG;AAC3C,WAAO,EAAE,MAAM,aAAa,MAAM,KAAK,MAAM,uBAAuB,MAAM,EAAE;AAAA,EAC9E;AACA,MAAI,KAAK,WAAW,YAAY,EAAG,QAAO,EAAE,MAAM,SAAS,MAAM,KAAK,MAAM,aAAa,MAAM,EAAE;AACjG,SAAO;AACT;AAiBA,IAAI,MAAM;AACV,MAAM,SAAS,MAAc;AAG3B,SAAO;AACP,SAAO,SAAS,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,GAAG;AAChD;AAMO,MAAM,gBAAuC;AAAA,EAA7C;AACL,SAAQ,QAAwB,CAAC;AACjC,SAAQ,aAAa,IAAI,gBAAgB;AACzC,SAAQ,YAAY,oBAAI,IAAgD;AAAA;AAAA;AAAA,EAGxE,QAAQ,MAAc,OAAkB,SAA8B;AACpE,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,MAAoB,EAAE,IAAI,OAAO,GAAG,MAAM,SAAS,KAAK;AAC9D,SAAK,MAAM,KAAK,GAAG;AACnB,QAAI,SAAS,YAAa,MAAK,WAAW,MAAM;AAChD,SAAK,KAAK;AACV,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,OAAO,IAAqB;AAC1B,UAAM,SAAS,KAAK,MAAM;AAC1B,SAAK,QAAQ,KAAK,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AACjD,QAAI,KAAK,MAAM,WAAW,OAAQ,QAAO;AAGzC,QAAI,CAAC,KAAK,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW,EAAG,MAAK,MAAM;AAChE,SAAK,KAAK;AACV,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UAAmC;AACjC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,aAAsB;AACpB,WAAO,KAAK,MAAM,SAAS;AAAA,EAC7B;AAAA,EAEA,QAAwB;AACtB,UAAM,MAAM,KAAK;AACjB,SAAK,QAAQ,CAAC;AACd,QAAI,IAAI,OAAQ,MAAK,KAAK;AAC1B,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,YAAyB;AAC3B,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,WAAW,OAAO,QAAS,MAAK,aAAa,IAAI,gBAAgB;AAAA,EAC5E;AAAA;AAAA,EAGA,SAAS,UAAkE;AACzE,SAAK,UAAU,IAAI,QAAQ;AAC3B,WAAO,MAAM,KAAK,UAAU,OAAO,QAAQ;AAAA,EAC7C;AAAA,EAEQ,OAAa;AACnB,eAAW,KAAK,KAAK,UAAW,GAAE,KAAK,KAAK;AAAA,EAC9C;AACF;AAWO,SAAS,UAAU,SAGxB;AACA,QAAM,OAAO,QAAQ,OAAO,CAAC,MAAwB,CAAC,CAAC,CAAC;AACxD,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,UAAU,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO;AAC1C,MAAI,SAAS;AACX,eAAW,MAAM,QAAQ,MAAM;AAC/B,WAAO,EAAE,QAAQ,WAAW,QAAQ,SAAS,MAAM;AAAA,IAAC,EAAE;AAAA,EACxD;AACA,QAAM,UAAU,CAAC,MAAmB,WAAW,MAAO,EAAE,OAAuB,MAAM;AACrF,aAAW,KAAK,KAAM,GAAE,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACzE,SAAO;AAAA,IACL,QAAQ,WAAW;AAAA,IACnB,SAAS,MAAM;AACb,iBAAW,KAAK,KAAM,GAAE,oBAAoB,SAAS,OAAO;AAAA,IAC9D;AAAA,EACF;AACF;","names":[]}
package/dist/auth.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\nimport { AUTH_STATE, REQUEST_AUTH_STATE } from './generated/protocol';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\n/** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\n/** The user's login / account state: a `status` plus the `user` when signed in. */\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType: AUTH_STATE,\n requestType: REQUEST_AUTH_STATE,\n initial: { status: 'unknown', user: null },\n parse: (msg) => (isAuthState(msg.state) ? (msg.state as AuthState) : undefined),\n});\n\n/**\n * Returns the current login / account state. Poll this whenever you need a\n * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.\n */\nexport const getAuthState = (): AuthState => channel.get();\n\n/**\n * Subscribe to login / logout changes. The listener is invoked immediately with\n * the current state, then again on every change. Returns an unsubscribe fn.\n */\nexport const onAuthChange = (listener: (state: AuthState) => void): (() => void) => channel.onChange(listener);\n\n/**\n * React hook returning the current login / account state, re-rendering on\n * login / logout.\n */\nexport const useAuth = (): AuthState => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAkC;AAClC,sBAA+C;AAuB/C,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,WAAW,aAAa,EAAE,WAAW,eAAe,EAAE,WAAW,kBACnE,EAAE,SAAS,QAAS,OAAO,EAAE,SAAS,YAAY,OAAQ,EAAE,KAAqB,UAAU;AAEhG;AAIA,MAAM,cAAU,sCAA6B;AAAA,EAC3C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,EAAE,QAAQ,WAAW,MAAM,KAAK;AAAA,EACzC,OAAO,CAAC,QAAS,YAAY,IAAI,KAAK,IAAK,IAAI,QAAsB;AACvE,CAAC;AAMM,MAAM,eAAe,MAAiB,QAAQ,IAAI;AAMlD,MAAM,eAAe,CAAC,aAAuD,QAAQ,SAAS,QAAQ;AAMtG,MAAM,UAAU,MAAiB,QAAQ,IAAI;","names":[]}
1
+ {"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\nimport { AUTH_STATE, REQUEST_AUTH_STATE } from './generated/protocol';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\n/**\n * The signed-in immediately.run user, as seen by the sandbox (no token, ever).\n *\n * Identity is gated by the `auth:identity` capability (elevated): the host\n * redacts `user` to `null` for any frame not holding it, while the baseline\n * `auth:status` still reports whether *a* session exists. A stage app therefore\n * sees `{ status: 'signed-in', user: null }` unless it declares `auth:identity`\n * under `immediately.run.capabilities` in its `package.json` and the user\n * consents once (a durable per-(app, user) grant). First-party / elevated\n * frames receive it via their region binding.\n */\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\n/**\n * The user's login / account state: a `status` plus the `user` when signed in.\n *\n * `user` is `null` for any frame not granted `auth:identity` — even when\n * `status` is `'signed-in'`. See {@link SandboxUser} for how an app earns it.\n */\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType: AUTH_STATE,\n requestType: REQUEST_AUTH_STATE,\n initial: { status: 'unknown', user: null },\n parse: (msg) => (isAuthState(msg.state) ? (msg.state as AuthState) : undefined),\n});\n\n/**\n * Returns the current login / account state. Poll this whenever you need a\n * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.\n *\n * `user` is `null` unless this frame holds `auth:identity` (see\n * {@link SandboxUser}); `status` alone is baseline and always available.\n *\n * Off-host (plain `vite dev` — no host to report a session) `status` stays\n * `'unknown'` FOREVER — it never settles to `'signed-out'`. Code that waits for a\n * settled status before proceeding (e.g. attributing authorship) hangs locally:\n * gate such flows on `status === 'signed-in'` combined with a timeout or an\n * explicit local fallback, never on \"status is no longer 'unknown'\".\n */\nexport const getAuthState = (): AuthState => channel.get();\n\n/**\n * Subscribe to login / logout changes. The listener is invoked immediately with\n * the current state, then again on every change. Returns an unsubscribe fn.\n */\nexport const onAuthChange = (listener: (state: AuthState) => void): (() => void) => channel.onChange(listener);\n\n/**\n * React hook returning the current login / account state, re-rendering on\n * login / logout.\n *\n * `user` is `null` unless this frame holds `auth:identity` (see\n * {@link SandboxUser}); `status` alone is baseline and always available.\n *\n * Off-host (plain `vite dev`) `status` stays `'unknown'` forever — see\n * {@link getAuthState} for why \"wait until it settles\" hangs locally and how to\n * gate on it safely (signed-in check plus a timeout/fallback).\n */\nexport const useAuth = (): AuthState => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAkC;AAClC,sBAA+C;AAsC/C,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,WAAW,aAAa,EAAE,WAAW,eAAe,EAAE,WAAW,kBACnE,EAAE,SAAS,QAAS,OAAO,EAAE,SAAS,YAAY,OAAQ,EAAE,KAAqB,UAAU;AAEhG;AAIA,MAAM,cAAU,sCAA6B;AAAA,EAC3C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,EAAE,QAAQ,WAAW,MAAM,KAAK;AAAA,EACzC,OAAO,CAAC,QAAS,YAAY,IAAI,KAAK,IAAK,IAAI,QAAsB;AACvE,CAAC;AAeM,MAAM,eAAe,MAAiB,QAAQ,IAAI;AAMlD,MAAM,eAAe,CAAC,aAAuD,QAAQ,SAAS,QAAQ;AAatG,MAAM,UAAU,MAAiB,QAAQ,IAAI;","names":[]}
package/dist/auth.d.cts CHANGED
@@ -6,12 +6,27 @@
6
6
  * distinguish "still loading" from a confirmed signed-out session).
7
7
  */
8
8
  type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';
9
- /** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */
9
+ /**
10
+ * The signed-in immediately.run user, as seen by the sandbox (no token, ever).
11
+ *
12
+ * Identity is gated by the `auth:identity` capability (elevated): the host
13
+ * redacts `user` to `null` for any frame not holding it, while the baseline
14
+ * `auth:status` still reports whether *a* session exists. A stage app therefore
15
+ * sees `{ status: 'signed-in', user: null }` unless it declares `auth:identity`
16
+ * under `immediately.run.capabilities` in its `package.json` and the user
17
+ * consents once (a durable per-(app, user) grant). First-party / elevated
18
+ * frames receive it via their region binding.
19
+ */
10
20
  interface SandboxUser {
11
21
  /** GitHub login (handle) of the signed-in user. */
12
22
  login: string;
13
23
  }
14
- /** The user's login / account state: a `status` plus the `user` when signed in. */
24
+ /**
25
+ * The user's login / account state: a `status` plus the `user` when signed in.
26
+ *
27
+ * `user` is `null` for any frame not granted `auth:identity` — even when
28
+ * `status` is `'signed-in'`. See {@link SandboxUser} for how an app earns it.
29
+ */
15
30
  interface AuthState {
16
31
  status: AuthStatus;
17
32
  user: SandboxUser | null;
@@ -19,6 +34,15 @@ interface AuthState {
19
34
  /**
20
35
  * Returns the current login / account state. Poll this whenever you need a
21
36
  * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.
37
+ *
38
+ * `user` is `null` unless this frame holds `auth:identity` (see
39
+ * {@link SandboxUser}); `status` alone is baseline and always available.
40
+ *
41
+ * Off-host (plain `vite dev` — no host to report a session) `status` stays
42
+ * `'unknown'` FOREVER — it never settles to `'signed-out'`. Code that waits for a
43
+ * settled status before proceeding (e.g. attributing authorship) hangs locally:
44
+ * gate such flows on `status === 'signed-in'` combined with a timeout or an
45
+ * explicit local fallback, never on "status is no longer 'unknown'".
22
46
  */
23
47
  declare const getAuthState: () => AuthState;
24
48
  /**
@@ -29,6 +53,13 @@ declare const onAuthChange: (listener: (state: AuthState) => void) => (() => voi
29
53
  /**
30
54
  * React hook returning the current login / account state, re-rendering on
31
55
  * login / logout.
56
+ *
57
+ * `user` is `null` unless this frame holds `auth:identity` (see
58
+ * {@link SandboxUser}); `status` alone is baseline and always available.
59
+ *
60
+ * Off-host (plain `vite dev`) `status` stays `'unknown'` forever — see
61
+ * {@link getAuthState} for why "wait until it settles" hangs locally and how to
62
+ * gate on it safely (signed-in check plus a timeout/fallback).
32
63
  */
33
64
  declare const useAuth: () => AuthState;
34
65
 
package/dist/auth.d.ts CHANGED
@@ -6,12 +6,27 @@
6
6
  * distinguish "still loading" from a confirmed signed-out session).
7
7
  */
8
8
  type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';
9
- /** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */
9
+ /**
10
+ * The signed-in immediately.run user, as seen by the sandbox (no token, ever).
11
+ *
12
+ * Identity is gated by the `auth:identity` capability (elevated): the host
13
+ * redacts `user` to `null` for any frame not holding it, while the baseline
14
+ * `auth:status` still reports whether *a* session exists. A stage app therefore
15
+ * sees `{ status: 'signed-in', user: null }` unless it declares `auth:identity`
16
+ * under `immediately.run.capabilities` in its `package.json` and the user
17
+ * consents once (a durable per-(app, user) grant). First-party / elevated
18
+ * frames receive it via their region binding.
19
+ */
10
20
  interface SandboxUser {
11
21
  /** GitHub login (handle) of the signed-in user. */
12
22
  login: string;
13
23
  }
14
- /** The user's login / account state: a `status` plus the `user` when signed in. */
24
+ /**
25
+ * The user's login / account state: a `status` plus the `user` when signed in.
26
+ *
27
+ * `user` is `null` for any frame not granted `auth:identity` — even when
28
+ * `status` is `'signed-in'`. See {@link SandboxUser} for how an app earns it.
29
+ */
15
30
  interface AuthState {
16
31
  status: AuthStatus;
17
32
  user: SandboxUser | null;
@@ -19,6 +34,15 @@ interface AuthState {
19
34
  /**
20
35
  * Returns the current login / account state. Poll this whenever you need a
21
36
  * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.
37
+ *
38
+ * `user` is `null` unless this frame holds `auth:identity` (see
39
+ * {@link SandboxUser}); `status` alone is baseline and always available.
40
+ *
41
+ * Off-host (plain `vite dev` — no host to report a session) `status` stays
42
+ * `'unknown'` FOREVER — it never settles to `'signed-out'`. Code that waits for a
43
+ * settled status before proceeding (e.g. attributing authorship) hangs locally:
44
+ * gate such flows on `status === 'signed-in'` combined with a timeout or an
45
+ * explicit local fallback, never on "status is no longer 'unknown'".
22
46
  */
23
47
  declare const getAuthState: () => AuthState;
24
48
  /**
@@ -29,6 +53,13 @@ declare const onAuthChange: (listener: (state: AuthState) => void) => (() => voi
29
53
  /**
30
54
  * React hook returning the current login / account state, re-rendering on
31
55
  * login / logout.
56
+ *
57
+ * `user` is `null` unless this frame holds `auth:identity` (see
58
+ * {@link SandboxUser}); `status` alone is baseline and always available.
59
+ *
60
+ * Off-host (plain `vite dev`) `status` stays `'unknown'` forever — see
61
+ * {@link getAuthState} for why "wait until it settles" hangs locally and how to
62
+ * gate on it safely (signed-in check plus a timeout/fallback).
32
63
  */
33
64
  declare const useAuth: () => AuthState;
34
65
 
package/dist/auth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\nimport { AUTH_STATE, REQUEST_AUTH_STATE } from './generated/protocol';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\n/** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\n/** The user's login / account state: a `status` plus the `user` when signed in. */\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType: AUTH_STATE,\n requestType: REQUEST_AUTH_STATE,\n initial: { status: 'unknown', user: null },\n parse: (msg) => (isAuthState(msg.state) ? (msg.state as AuthState) : undefined),\n});\n\n/**\n * Returns the current login / account state. Poll this whenever you need a\n * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.\n */\nexport const getAuthState = (): AuthState => channel.get();\n\n/**\n * Subscribe to login / logout changes. The listener is invoked immediately with\n * the current state, then again on every change. Returns an unsubscribe fn.\n */\nexport const onAuthChange = (listener: (state: AuthState) => void): (() => void) => channel.onChange(listener);\n\n/**\n * React hook returning the current login / account state, re-rendering on\n * login / logout.\n */\nexport const useAuth = (): AuthState => channel.use();\n"],"mappings":";AAAA,SAAS,yBAAyB;AAClC,SAAS,YAAY,0BAA0B;AAuB/C,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,WAAW,aAAa,EAAE,WAAW,eAAe,EAAE,WAAW,kBACnE,EAAE,SAAS,QAAS,OAAO,EAAE,SAAS,YAAY,OAAQ,EAAE,KAAqB,UAAU;AAEhG;AAIA,MAAM,UAAU,kBAA6B;AAAA,EAC3C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,EAAE,QAAQ,WAAW,MAAM,KAAK;AAAA,EACzC,OAAO,CAAC,QAAS,YAAY,IAAI,KAAK,IAAK,IAAI,QAAsB;AACvE,CAAC;AAMM,MAAM,eAAe,MAAiB,QAAQ,IAAI;AAMlD,MAAM,eAAe,CAAC,aAAuD,QAAQ,SAAS,QAAQ;AAMtG,MAAM,UAAU,MAAiB,QAAQ,IAAI;","names":[]}
1
+ {"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\nimport { AUTH_STATE, REQUEST_AUTH_STATE } from './generated/protocol';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\n/**\n * The signed-in immediately.run user, as seen by the sandbox (no token, ever).\n *\n * Identity is gated by the `auth:identity` capability (elevated): the host\n * redacts `user` to `null` for any frame not holding it, while the baseline\n * `auth:status` still reports whether *a* session exists. A stage app therefore\n * sees `{ status: 'signed-in', user: null }` unless it declares `auth:identity`\n * under `immediately.run.capabilities` in its `package.json` and the user\n * consents once (a durable per-(app, user) grant). First-party / elevated\n * frames receive it via their region binding.\n */\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\n/**\n * The user's login / account state: a `status` plus the `user` when signed in.\n *\n * `user` is `null` for any frame not granted `auth:identity` — even when\n * `status` is `'signed-in'`. See {@link SandboxUser} for how an app earns it.\n */\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType: AUTH_STATE,\n requestType: REQUEST_AUTH_STATE,\n initial: { status: 'unknown', user: null },\n parse: (msg) => (isAuthState(msg.state) ? (msg.state as AuthState) : undefined),\n});\n\n/**\n * Returns the current login / account state. Poll this whenever you need a\n * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.\n *\n * `user` is `null` unless this frame holds `auth:identity` (see\n * {@link SandboxUser}); `status` alone is baseline and always available.\n *\n * Off-host (plain `vite dev` — no host to report a session) `status` stays\n * `'unknown'` FOREVER — it never settles to `'signed-out'`. Code that waits for a\n * settled status before proceeding (e.g. attributing authorship) hangs locally:\n * gate such flows on `status === 'signed-in'` combined with a timeout or an\n * explicit local fallback, never on \"status is no longer 'unknown'\".\n */\nexport const getAuthState = (): AuthState => channel.get();\n\n/**\n * Subscribe to login / logout changes. The listener is invoked immediately with\n * the current state, then again on every change. Returns an unsubscribe fn.\n */\nexport const onAuthChange = (listener: (state: AuthState) => void): (() => void) => channel.onChange(listener);\n\n/**\n * React hook returning the current login / account state, re-rendering on\n * login / logout.\n *\n * `user` is `null` unless this frame holds `auth:identity` (see\n * {@link SandboxUser}); `status` alone is baseline and always available.\n *\n * Off-host (plain `vite dev`) `status` stays `'unknown'` forever — see\n * {@link getAuthState} for why \"wait until it settles\" hangs locally and how to\n * gate on it safely (signed-in check plus a timeout/fallback).\n */\nexport const useAuth = (): AuthState => channel.use();\n"],"mappings":";AAAA,SAAS,yBAAyB;AAClC,SAAS,YAAY,0BAA0B;AAsC/C,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,WAAW,aAAa,EAAE,WAAW,eAAe,EAAE,WAAW,kBACnE,EAAE,SAAS,QAAS,OAAO,EAAE,SAAS,YAAY,OAAQ,EAAE,KAAqB,UAAU;AAEhG;AAIA,MAAM,UAAU,kBAA6B;AAAA,EAC3C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,EAAE,QAAQ,WAAW,MAAM,KAAK;AAAA,EACzC,OAAO,CAAC,QAAS,YAAY,IAAI,KAAK,IAAK,IAAI,QAAsB;AACvE,CAAC;AAeM,MAAM,eAAe,MAAiB,QAAQ,IAAI;AAMlD,MAAM,eAAe,CAAC,aAAuD,QAAQ,SAAS,QAAQ;AAatG,MAAM,UAAU,MAAiB,QAAQ,IAAI;","names":[]}
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var bundle_exports = {};
20
+ __export(bundle_exports, {
21
+ BundleContext: () => BundleContext,
22
+ fromBundlePath: () => fromBundlePath,
23
+ toBundlePath: () => toBundlePath,
24
+ useBundle: () => useBundle,
25
+ useBundleEntries: () => useBundleEntries,
26
+ useBundleEntry: () => useBundleEntry,
27
+ useCurrentEntry: () => useCurrentEntry
28
+ });
29
+ module.exports = __toCommonJS(bundle_exports);
30
+ var import_react = require("react");
31
+ var import_metadataSource = require("./metadataSource");
32
+ const EMPTY_SCOPE = { root: null, entry: null, toHref: (p) => p };
33
+ const BundleContext = (0, import_react.createContext)(EMPTY_SCOPE);
34
+ const useBundle = () => (0, import_react.use)(BundleContext);
35
+ const toBundlePath = (absolute, root) => {
36
+ if (root === null) return null;
37
+ const base = root.replace(/\/+$/, "");
38
+ if (base === "") return absolute;
39
+ if (!absolute.startsWith(`${base}/`)) return null;
40
+ return absolute.slice(base.length);
41
+ };
42
+ const fromBundlePath = (bundlePath, root) => {
43
+ if (root === null) return null;
44
+ const base = root.replace(/\/+$/, "");
45
+ return `${base}${bundlePath.startsWith("/") ? "" : "/"}${bundlePath}`;
46
+ };
47
+ const useBundleEntries = () => {
48
+ const files = (0, import_metadataSource.useMetadataStore)();
49
+ const { root, toHref } = useBundle();
50
+ return (0, import_react.useMemo)(() => {
51
+ if (root === null) return [];
52
+ const out = [];
53
+ for (const [absolute, meta] of Object.entries(files)) {
54
+ const path = toBundlePath(absolute, root);
55
+ if (path === null) continue;
56
+ out.push({ path, href: toHref(path), meta });
57
+ }
58
+ out.sort((a, b) => a.path.localeCompare(b.path));
59
+ return out;
60
+ }, [files, root, toHref]);
61
+ };
62
+ const useBundleEntry = (bundlePath) => {
63
+ const files = (0, import_metadataSource.useMetadataStore)();
64
+ const { root } = useBundle();
65
+ return (0, import_react.useMemo)(() => {
66
+ const absolute = fromBundlePath(bundlePath, root);
67
+ return absolute === null ? void 0 : files[absolute];
68
+ }, [bundlePath, files, root]);
69
+ };
70
+ const useCurrentEntry = () => {
71
+ const files = (0, import_metadataSource.useMetadataStore)();
72
+ const { root, entry, toHref } = useBundle();
73
+ return (0, import_react.useMemo)(() => {
74
+ if (root === null || entry === null) return null;
75
+ const absolute = fromBundlePath(entry, root);
76
+ const meta = absolute === null ? void 0 : files[absolute];
77
+ if (meta === void 0) return null;
78
+ return { path: entry, href: toHref(entry), meta };
79
+ }, [files, root, entry, toHref]);
80
+ };
81
+ // Annotate the CommonJS export names for ESM import in node:
82
+ 0 && (module.exports = {
83
+ BundleContext,
84
+ fromBundlePath,
85
+ toBundlePath,
86
+ useBundle,
87
+ useBundleEntries,
88
+ useBundleEntry,
89
+ useCurrentEntry
90
+ });
91
+ //# sourceMappingURL=bundle.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bundle.ts"],"sourcesContent":["// The bundle scope — what a component rendered INSIDE a bundle may know about it\n// (R3-174; MDX_FROM_MOUNT_SPEC §2, §7 1a).\n//\n// A bundle viewer can register components the bundle itself ships, so an entry writes\n// `<ProjectIndex/>` and the viewer resolves it — no engine fork, no import line\n// (`MDX_FROM_MOUNT_SPEC` §2's \"provider composition is consumer policy\"). Those components\n// live in the CONTENT filesystem and the viewer lives in its own, and the seam between\n// them is this package: a content component cannot import the viewer (it would resolve a\n// second copy from the registry, with its own module state, and read the wrong bundle), so\n// anything the viewer must tell it has to arrive through a surface both sides already\n// share. This is that surface.\n//\n// **Why paths here are bundle-absolute, not filesystem-absolute.** The metadata store is\n// keyed by absolute module path, which under dispatch means `/mnt/<hash>/roadmap/x.mdx` —\n// the host-minted chroot. That prefix is host knowledge a viewer may read THROUGH but must\n// never publish (the property `grove/src/components/DirectoryList.test.tsx` pins with\n// `expect(el.innerHTML).not.toContain('mnt')`), and it is not stable across loads. A\n// content component that saw it would embed it in hrefs and keys. So the bundle scope\n// rebases every path it hands out to the bundle root (`/roadmap/x.mdx`), which is the same\n// address under fork, library and dispatch composition — `PLATFORM_LAYERING_SPEC` §1.1's\n// mode-invariance rule, applied to metadata rather than to links.\n//\n// **Why the href mapping is injected rather than derived.** Bundle path → in-app URL is\n// VIEWER policy, and the two packagings genuinely disagree (a fork's URLs are anchored at\n// its app root and are already published and cited; a dispatched viewer's are bundle-\n// relative). A content component must not have to know which it is in, and the SDK must\n// not guess — so the viewer supplies `toHref` and content never computes a URL itself.\n\nimport { createContext, use, useMemo } from 'react';\nimport { useMetadataStore } from './metadataSource';\nimport type { Metadata } from './sandboxTypes';\n\n/** What a bundle viewer declares about the bundle it is rendering. */\nexport interface BundleScope {\n /**\n * Absolute filesystem path of the bundle root, WITHOUT a trailing slash\n * (`/app/content` in a fork, `/mnt/<hash>` under dispatch), or `null` when the\n * surrounding app is not rendering a bundle — the default, which leaves every hook here\n * returning the empty result rather than guessing a root.\n */\n root: string | null;\n /** Bundle-absolute path of the entry currently being read (`/roadmap/R3-174.mdx`), or\n * `null` when nothing is. This is the entry, NOT the file being rendered: a component\n * in a `_layout.mdx` wrapping that entry sees the entry, which is what makes furniture\n * in the layout chain (a status line, a dependency rail) able to describe the page. */\n entry: string | null;\n /** Bundle-absolute path → the href to navigate to it. Supplied by the viewer; the\n * identity function by default, which is correct for a viewer whose URL space IS the\n * bundle space. */\n toHref: (bundlePath: string) => string;\n}\n\nconst EMPTY_SCOPE: BundleScope = { root: null, entry: null, toHref: (p) => p };\n\n/** Ambient bundle scope. A bundle-rendering app wraps its content tree in\n * `<BundleContext value={{ root, entry, toHref }}>`; nesting a second provider inside a\n * rendered sub-bundle makes the innermost win, as with {@link LinkSpaceContext}. */\nexport const BundleContext = createContext<BundleScope>(EMPTY_SCOPE);\n\n/** Read the ambient bundle scope. Every hook below is a convenience over this. */\nexport const useBundle = (): BundleScope => use(BundleContext);\n\n/**\n * Filesystem-absolute path → bundle-absolute, or `null` when the path is not inside the\n * bundle. Exact-root and separator-boundary aware, so `/mnt/hash2/x` is not treated as\n * living under `/mnt/hash`.\n */\nexport const toBundlePath = (absolute: string, root: string | null): string | null => {\n if (root === null) return null;\n const base = root.replace(/\\/+$/, '');\n if (base === '') return absolute;\n if (!absolute.startsWith(`${base}/`)) return null;\n return absolute.slice(base.length);\n};\n\n/** Bundle-absolute path → filesystem-absolute, or `null` without a root. The inverse of\n * {@link toBundlePath}, for the rare content component that must read raw bytes. */\nexport const fromBundlePath = (bundlePath: string, root: string | null): string | null => {\n if (root === null) return null;\n const base = root.replace(/\\/+$/, '');\n return `${base}${bundlePath.startsWith('/') ? '' : '/'}${bundlePath}`;\n};\n\n/** One entry as content sees it: where it is, where it links, and its frontmatter. */\nexport interface BundleEntry<T = Metadata> {\n /** Bundle-absolute path (`/roadmap/R3-174.mdx`). */\n path: string;\n /** The href that navigates to it, per the viewer's mapping. */\n href: string;\n meta: T;\n}\n\n/**\n * Every entry in the surrounding bundle, with bundle-absolute paths and viewer-supplied\n * hrefs — the surface a content component queries instead of {@link useMetadataQuery},\n * whose keys are filesystem-absolute and therefore carry the mount prefix.\n *\n * Returns an empty array outside a bundle, so a component written for a bundle renders\n * nothing rather than throwing when someone drops it elsewhere. The array keeps its\n * identity while the store and root are unchanged, so it is safe in dependency arrays.\n */\nexport const useBundleEntries = <T = Metadata>(): BundleEntry<T>[] => {\n const files = useMetadataStore<T>();\n const { root, toHref } = useBundle();\n return useMemo(() => {\n if (root === null) return [];\n const out: BundleEntry<T>[] = [];\n for (const [absolute, meta] of Object.entries(files)) {\n const path = toBundlePath(absolute, root);\n if (path === null) continue;\n out.push({ path, href: toHref(path), meta: meta as T });\n }\n // Path order, so a consumer that does not sort still renders deterministically\n // (object key order is insertion order, which is scan order, which is not stable).\n out.sort((a, b) => a.path.localeCompare(b.path));\n return out;\n }, [files, root, toHref]);\n};\n\n/** One entry's frontmatter by bundle-absolute path, or `undefined`. */\nexport const useBundleEntry = <T = Metadata>(bundlePath: string): T | undefined => {\n const files = useMetadataStore<T>();\n const { root } = useBundle();\n return useMemo(() => {\n const absolute = fromBundlePath(bundlePath, root);\n return absolute === null ? undefined : files[absolute];\n }, [bundlePath, files, root]);\n};\n\n/** The entry currently being read — `null` outside a bundle, or when the viewer declares\n * no entry (a directory listing, a 404). See {@link BundleScope.entry} for why this is\n * the entry rather than the file the component happens to be written in. */\nexport const useCurrentEntry = <T = Metadata>(): BundleEntry<T> | null => {\n const files = useMetadataStore<T>();\n const { root, entry, toHref } = useBundle();\n return useMemo(() => {\n if (root === null || entry === null) return null;\n const absolute = fromBundlePath(entry, root);\n const meta = absolute === null ? undefined : files[absolute];\n if (meta === undefined) return null;\n return { path: entry, href: toHref(entry), meta: meta as T };\n }, [files, root, entry, toHref]);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BA,mBAA4C;AAC5C,4BAAiC;AAuBjC,MAAM,cAA2B,EAAE,MAAM,MAAM,OAAO,MAAM,QAAQ,CAAC,MAAM,EAAE;AAKtE,MAAM,oBAAgB,4BAA2B,WAAW;AAG5D,MAAM,YAAY,UAAmB,kBAAI,aAAa;AAOtD,MAAM,eAAe,CAAC,UAAkB,SAAuC;AACpF,MAAI,SAAS,KAAM,QAAO;AAC1B,QAAM,OAAO,KAAK,QAAQ,QAAQ,EAAE;AACpC,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,CAAC,SAAS,WAAW,GAAG,IAAI,GAAG,EAAG,QAAO;AAC7C,SAAO,SAAS,MAAM,KAAK,MAAM;AACnC;AAIO,MAAM,iBAAiB,CAAC,YAAoB,SAAuC;AACxF,MAAI,SAAS,KAAM,QAAO;AAC1B,QAAM,OAAO,KAAK,QAAQ,QAAQ,EAAE;AACpC,SAAO,GAAG,IAAI,GAAG,WAAW,WAAW,GAAG,IAAI,KAAK,GAAG,GAAG,UAAU;AACrE;AAoBO,MAAM,mBAAmB,MAAsC;AACpE,QAAM,YAAQ,wCAAoB;AAClC,QAAM,EAAE,MAAM,OAAO,IAAI,UAAU;AACnC,aAAO,sBAAQ,MAAM;AACnB,QAAI,SAAS,KAAM,QAAO,CAAC;AAC3B,UAAM,MAAwB,CAAC;AAC/B,eAAW,CAAC,UAAU,IAAI,KAAK,OAAO,QAAQ,KAAK,GAAG;AACpD,YAAM,OAAO,aAAa,UAAU,IAAI;AACxC,UAAI,SAAS,KAAM;AACnB,UAAI,KAAK,EAAE,MAAM,MAAM,OAAO,IAAI,GAAG,KAAgB,CAAC;AAAA,IACxD;AAGA,QAAI,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAC/C,WAAO;AAAA,EACT,GAAG,CAAC,OAAO,MAAM,MAAM,CAAC;AAC1B;AAGO,MAAM,iBAAiB,CAAe,eAAsC;AACjF,QAAM,YAAQ,wCAAoB;AAClC,QAAM,EAAE,KAAK,IAAI,UAAU;AAC3B,aAAO,sBAAQ,MAAM;AACnB,UAAM,WAAW,eAAe,YAAY,IAAI;AAChD,WAAO,aAAa,OAAO,SAAY,MAAM,QAAQ;AAAA,EACvD,GAAG,CAAC,YAAY,OAAO,IAAI,CAAC;AAC9B;AAKO,MAAM,kBAAkB,MAA2C;AACxE,QAAM,YAAQ,wCAAoB;AAClC,QAAM,EAAE,MAAM,OAAO,OAAO,IAAI,UAAU;AAC1C,aAAO,sBAAQ,MAAM;AACnB,QAAI,SAAS,QAAQ,UAAU,KAAM,QAAO;AAC5C,UAAM,WAAW,eAAe,OAAO,IAAI;AAC3C,UAAM,OAAO,aAAa,OAAO,SAAY,MAAM,QAAQ;AAC3D,QAAI,SAAS,OAAW,QAAO;AAC/B,WAAO,EAAE,MAAM,OAAO,MAAM,OAAO,KAAK,GAAG,KAAgB;AAAA,EAC7D,GAAG,CAAC,OAAO,MAAM,OAAO,MAAM,CAAC;AACjC;","names":[]}
@@ -0,0 +1,63 @@
1
+ import * as react from 'react';
2
+ import { Metadata } from './sandboxTypes.cjs';
3
+
4
+ /** What a bundle viewer declares about the bundle it is rendering. */
5
+ interface BundleScope {
6
+ /**
7
+ * Absolute filesystem path of the bundle root, WITHOUT a trailing slash
8
+ * (`/app/content` in a fork, `/mnt/<hash>` under dispatch), or `null` when the
9
+ * surrounding app is not rendering a bundle — the default, which leaves every hook here
10
+ * returning the empty result rather than guessing a root.
11
+ */
12
+ root: string | null;
13
+ /** Bundle-absolute path of the entry currently being read (`/roadmap/R3-174.mdx`), or
14
+ * `null` when nothing is. This is the entry, NOT the file being rendered: a component
15
+ * in a `_layout.mdx` wrapping that entry sees the entry, which is what makes furniture
16
+ * in the layout chain (a status line, a dependency rail) able to describe the page. */
17
+ entry: string | null;
18
+ /** Bundle-absolute path → the href to navigate to it. Supplied by the viewer; the
19
+ * identity function by default, which is correct for a viewer whose URL space IS the
20
+ * bundle space. */
21
+ toHref: (bundlePath: string) => string;
22
+ }
23
+ /** Ambient bundle scope. A bundle-rendering app wraps its content tree in
24
+ * `<BundleContext value={{ root, entry, toHref }}>`; nesting a second provider inside a
25
+ * rendered sub-bundle makes the innermost win, as with {@link LinkSpaceContext}. */
26
+ declare const BundleContext: react.Context<BundleScope>;
27
+ /** Read the ambient bundle scope. Every hook below is a convenience over this. */
28
+ declare const useBundle: () => BundleScope;
29
+ /**
30
+ * Filesystem-absolute path → bundle-absolute, or `null` when the path is not inside the
31
+ * bundle. Exact-root and separator-boundary aware, so `/mnt/hash2/x` is not treated as
32
+ * living under `/mnt/hash`.
33
+ */
34
+ declare const toBundlePath: (absolute: string, root: string | null) => string | null;
35
+ /** Bundle-absolute path → filesystem-absolute, or `null` without a root. The inverse of
36
+ * {@link toBundlePath}, for the rare content component that must read raw bytes. */
37
+ declare const fromBundlePath: (bundlePath: string, root: string | null) => string | null;
38
+ /** One entry as content sees it: where it is, where it links, and its frontmatter. */
39
+ interface BundleEntry<T = Metadata> {
40
+ /** Bundle-absolute path (`/roadmap/R3-174.mdx`). */
41
+ path: string;
42
+ /** The href that navigates to it, per the viewer's mapping. */
43
+ href: string;
44
+ meta: T;
45
+ }
46
+ /**
47
+ * Every entry in the surrounding bundle, with bundle-absolute paths and viewer-supplied
48
+ * hrefs — the surface a content component queries instead of {@link useMetadataQuery},
49
+ * whose keys are filesystem-absolute and therefore carry the mount prefix.
50
+ *
51
+ * Returns an empty array outside a bundle, so a component written for a bundle renders
52
+ * nothing rather than throwing when someone drops it elsewhere. The array keeps its
53
+ * identity while the store and root are unchanged, so it is safe in dependency arrays.
54
+ */
55
+ declare const useBundleEntries: <T = Metadata>() => BundleEntry<T>[];
56
+ /** One entry's frontmatter by bundle-absolute path, or `undefined`. */
57
+ declare const useBundleEntry: <T = Metadata>(bundlePath: string) => T | undefined;
58
+ /** The entry currently being read — `null` outside a bundle, or when the viewer declares
59
+ * no entry (a directory listing, a 404). See {@link BundleScope.entry} for why this is
60
+ * the entry rather than the file the component happens to be written in. */
61
+ declare const useCurrentEntry: <T = Metadata>() => BundleEntry<T> | null;
62
+
63
+ export { BundleContext, type BundleEntry, type BundleScope, fromBundlePath, toBundlePath, useBundle, useBundleEntries, useBundleEntry, useCurrentEntry };
@@ -0,0 +1,63 @@
1
+ import * as react from 'react';
2
+ import { Metadata } from './sandboxTypes.js';
3
+
4
+ /** What a bundle viewer declares about the bundle it is rendering. */
5
+ interface BundleScope {
6
+ /**
7
+ * Absolute filesystem path of the bundle root, WITHOUT a trailing slash
8
+ * (`/app/content` in a fork, `/mnt/<hash>` under dispatch), or `null` when the
9
+ * surrounding app is not rendering a bundle — the default, which leaves every hook here
10
+ * returning the empty result rather than guessing a root.
11
+ */
12
+ root: string | null;
13
+ /** Bundle-absolute path of the entry currently being read (`/roadmap/R3-174.mdx`), or
14
+ * `null` when nothing is. This is the entry, NOT the file being rendered: a component
15
+ * in a `_layout.mdx` wrapping that entry sees the entry, which is what makes furniture
16
+ * in the layout chain (a status line, a dependency rail) able to describe the page. */
17
+ entry: string | null;
18
+ /** Bundle-absolute path → the href to navigate to it. Supplied by the viewer; the
19
+ * identity function by default, which is correct for a viewer whose URL space IS the
20
+ * bundle space. */
21
+ toHref: (bundlePath: string) => string;
22
+ }
23
+ /** Ambient bundle scope. A bundle-rendering app wraps its content tree in
24
+ * `<BundleContext value={{ root, entry, toHref }}>`; nesting a second provider inside a
25
+ * rendered sub-bundle makes the innermost win, as with {@link LinkSpaceContext}. */
26
+ declare const BundleContext: react.Context<BundleScope>;
27
+ /** Read the ambient bundle scope. Every hook below is a convenience over this. */
28
+ declare const useBundle: () => BundleScope;
29
+ /**
30
+ * Filesystem-absolute path → bundle-absolute, or `null` when the path is not inside the
31
+ * bundle. Exact-root and separator-boundary aware, so `/mnt/hash2/x` is not treated as
32
+ * living under `/mnt/hash`.
33
+ */
34
+ declare const toBundlePath: (absolute: string, root: string | null) => string | null;
35
+ /** Bundle-absolute path → filesystem-absolute, or `null` without a root. The inverse of
36
+ * {@link toBundlePath}, for the rare content component that must read raw bytes. */
37
+ declare const fromBundlePath: (bundlePath: string, root: string | null) => string | null;
38
+ /** One entry as content sees it: where it is, where it links, and its frontmatter. */
39
+ interface BundleEntry<T = Metadata> {
40
+ /** Bundle-absolute path (`/roadmap/R3-174.mdx`). */
41
+ path: string;
42
+ /** The href that navigates to it, per the viewer's mapping. */
43
+ href: string;
44
+ meta: T;
45
+ }
46
+ /**
47
+ * Every entry in the surrounding bundle, with bundle-absolute paths and viewer-supplied
48
+ * hrefs — the surface a content component queries instead of {@link useMetadataQuery},
49
+ * whose keys are filesystem-absolute and therefore carry the mount prefix.
50
+ *
51
+ * Returns an empty array outside a bundle, so a component written for a bundle renders
52
+ * nothing rather than throwing when someone drops it elsewhere. The array keeps its
53
+ * identity while the store and root are unchanged, so it is safe in dependency arrays.
54
+ */
55
+ declare const useBundleEntries: <T = Metadata>() => BundleEntry<T>[];
56
+ /** One entry's frontmatter by bundle-absolute path, or `undefined`. */
57
+ declare const useBundleEntry: <T = Metadata>(bundlePath: string) => T | undefined;
58
+ /** The entry currently being read — `null` outside a bundle, or when the viewer declares
59
+ * no entry (a directory listing, a 404). See {@link BundleScope.entry} for why this is
60
+ * the entry rather than the file the component happens to be written in. */
61
+ declare const useCurrentEntry: <T = Metadata>() => BundleEntry<T> | null;
62
+
63
+ export { BundleContext, type BundleEntry, type BundleScope, fromBundlePath, toBundlePath, useBundle, useBundleEntries, useBundleEntry, useCurrentEntry };