@ixo/oracle-runtime-workers 0.12.0 → 0.15.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 (205) hide show
  1. package/README.md +32 -10
  2. package/package.json +14 -9
  3. package/src/artifacts/config.ts +158 -0
  4. package/src/artifacts/crypto.ts +94 -0
  5. package/src/artifacts/routes.ts +73 -0
  6. package/src/artifacts/store.ts +238 -0
  7. package/src/artifacts/sweep.ts +200 -0
  8. package/src/artifacts/tool.ts +103 -0
  9. package/src/artifacts/viewer-page.ts +614 -0
  10. package/src/attachments/download.ts +168 -45
  11. package/src/attachments/index.ts +9 -0
  12. package/src/attachments/magic.ts +33 -0
  13. package/src/attachments/pipeline.ts +105 -49
  14. package/src/attachments/sandbox-archive.ts +9 -8
  15. package/src/attachments/view-cache.ts +142 -0
  16. package/src/attachments/view.ts +53 -12
  17. package/src/channels/auth.ts +267 -0
  18. package/src/channels/contract.ts +137 -0
  19. package/src/channels/turns.ts +270 -0
  20. package/src/core/budgeted-llm.ts +223 -0
  21. package/src/core/capability-router.ts +273 -0
  22. package/src/core/context-budget.ts +97 -10
  23. package/src/core/env.ts +111 -0
  24. package/src/core/execution-profile.ts +17 -0
  25. package/src/core/index.ts +267 -9
  26. package/src/core/llm.ts +30 -9
  27. package/src/core/main-agent-types.ts +41 -1
  28. package/src/core/main-agent.ts +367 -65
  29. package/src/core/manifest.ts +34 -0
  30. package/src/core/meta-tools.ts +128 -18
  31. package/src/core/middlewares/capability-gate.ts +130 -12
  32. package/src/core/middlewares/context-guard.ts +55 -24
  33. package/src/core/middlewares/index.ts +20 -2
  34. package/src/core/middlewares/page-context.ts +46 -2
  35. package/src/core/middlewares/return-direct-first.ts +47 -0
  36. package/src/core/middlewares/summarization.ts +152 -10
  37. package/src/core/middlewares/tool-execution.ts +323 -0
  38. package/src/core/middlewares/tool-repetition-guard.ts +202 -35
  39. package/src/core/middlewares/turn-boundary.ts +162 -0
  40. package/src/core/openrouter-pricing.ts +155 -22
  41. package/src/core/prompt-composer.ts +112 -7
  42. package/src/core/registries.ts +253 -48
  43. package/src/core/request-admission.ts +159 -0
  44. package/src/core/runtime-context.ts +107 -8
  45. package/src/core/state.ts +1 -1
  46. package/src/core/sub-agent-fallback.ts +21 -2
  47. package/src/core/subagent-as-tool.ts +78 -43
  48. package/src/core/tool-access.ts +189 -0
  49. package/src/core/tool-scheduler.ts +125 -0
  50. package/src/core/turn-budget.ts +205 -0
  51. package/src/core/turn-time-note.ts +43 -0
  52. package/src/core/user-kv.ts +446 -0
  53. package/src/core/wrap-plugin-tool.ts +20 -0
  54. package/src/delivery/plan.ts +278 -0
  55. package/src/delivery/profile.ts +161 -0
  56. package/src/delivery/prompt.ts +27 -0
  57. package/src/delivery/schema.ts +61 -0
  58. package/src/delivery/shaper.ts +389 -0
  59. package/src/delivery/types.ts +84 -0
  60. package/src/do/ambient.ts +51 -15
  61. package/src/do/contracts.ts +198 -6
  62. package/src/do/idle-eviction.ts +38 -0
  63. package/src/do/matrix-turn-ledger.ts +24 -5
  64. package/src/do/result-store-test-do.ts +137 -3
  65. package/src/do/result-store.ts +239 -58
  66. package/src/do/room-mirror.ts +29 -0
  67. package/src/do/run-buffer.ts +64 -9
  68. package/src/do/run-coordinator.ts +451 -76
  69. package/src/do/run-request.ts +2 -0
  70. package/src/do/run-store-test-do.ts +301 -1
  71. package/src/do/run-store.ts +385 -43
  72. package/src/do/secrets-adapter.ts +22 -3
  73. package/src/do/sse-stream.ts +60 -9
  74. package/src/do/transcript.ts +48 -5
  75. package/src/do/turn-body.ts +105 -5
  76. package/src/do/ucan-service.ts +204 -34
  77. package/src/do/user-oracle-do.ts +1883 -264
  78. package/src/feedback/config.ts +127 -0
  79. package/src/feedback/contract.ts +148 -0
  80. package/src/feedback/linear-sink.ts +285 -0
  81. package/src/feedback/privacy.ts +150 -0
  82. package/src/feedback/reservation.ts +298 -0
  83. package/src/feedback/submit.ts +247 -0
  84. package/src/index.ts +138 -6
  85. package/src/llm/byo-adapter.ts +27 -6
  86. package/src/llm/byo-catalog.ts +13 -0
  87. package/src/llm/byo-model-fallback.ts +248 -0
  88. package/src/llm/byo-service.ts +45 -8
  89. package/src/llm/provider-error.ts +139 -2
  90. package/src/matrix/gateway-do.ts +523 -111
  91. package/src/matrix/group-chat.ts +66 -23
  92. package/src/matrix/inbox-store.ts +16 -1
  93. package/src/matrix/ingest.ts +79 -14
  94. package/src/matrix/media-deadline.ts +130 -0
  95. package/src/matrix/replay-format.ts +5 -3
  96. package/src/matrix/reply-parts.ts +92 -0
  97. package/src/matrix/room-alias.ts +96 -0
  98. package/src/matrix/user-homeserver.ts +166 -7
  99. package/src/owner-store/ixo-vfs-store.ts +170 -43
  100. package/src/owner-store/migrating-store.ts +31 -15
  101. package/src/owner-store/types.ts +18 -3
  102. package/src/plugin-api/index.ts +43 -2
  103. package/src/plugin-api/oracle-plugin.ts +35 -0
  104. package/src/plugin-api/request-admission.ts +41 -0
  105. package/src/plugin-api/tool-helper.ts +25 -1
  106. package/src/plugin-api/tool-plane.ts +92 -0
  107. package/src/plugin-api/types.ts +194 -6
  108. package/src/plugins/agui/agui.plugin.ts +31 -16
  109. package/src/plugins/composio/composio-tools.ts +57 -6
  110. package/src/plugins/composio/composio-ucan.ts +3 -2
  111. package/src/plugins/composio/composio.plugin.ts +2 -0
  112. package/src/plugins/delegated-capabilities.ts +25 -0
  113. package/src/plugins/editor/content-session.ts +173 -83
  114. package/src/plugins/editor/content-tools.ts +76 -17
  115. package/src/plugins/editor/editor-mx.ts +28 -9
  116. package/src/plugins/editor/editor.plugin.ts +3 -1
  117. package/src/plugins/editor/failures.ts +28 -7
  118. package/src/plugins/editor/prompts.ts +2 -1
  119. package/src/plugins/editor/provider.ts +553 -40
  120. package/src/plugins/editor/standalone-editor-tool.ts +61 -18
  121. package/src/plugins/firecrawl/firecrawl-tools.ts +13 -5
  122. package/src/plugins/flows/action-metadata.ts +111 -11
  123. package/src/plugins/flows/actions.ts +18 -0
  124. package/src/plugins/flows/decision-context.ts +31 -0
  125. package/src/plugins/flows/edit.ts +223 -52
  126. package/src/plugins/flows/errors.ts +22 -1
  127. package/src/plugins/flows/flow-doc.ts +92 -9
  128. package/src/plugins/flows/flows.plugin.ts +11 -5
  129. package/src/plugins/flows/forms.ts +34 -0
  130. package/src/plugins/flows/index.ts +2 -0
  131. package/src/plugins/flows/input-policy.ts +140 -0
  132. package/src/plugins/flows/prompts.ts +14 -11
  133. package/src/plugins/flows/read.ts +49 -9
  134. package/src/plugins/flows/ref-syntax.ts +129 -0
  135. package/src/plugins/flows/test-homeserver.ts +245 -0
  136. package/src/plugins/flows/tools/authoring.ts +216 -112
  137. package/src/plugins/flows/tools/discovery.ts +4 -1
  138. package/src/plugins/flows/tools/forms.ts +21 -2
  139. package/src/plugins/flows/tools/settings.ts +34 -13
  140. package/src/plugins/flows/translator.ts +13 -10
  141. package/src/plugins/flows/types.ts +167 -42
  142. package/src/plugins/index.ts +11 -1
  143. package/src/plugins/ixo-transaction/chain-ids.ts +43 -0
  144. package/src/plugins/ixo-transaction/index.ts +2 -0
  145. package/src/plugins/ixo-transaction/ixo-transaction.plugin.ts +119 -0
  146. package/src/plugins/ixo-transaction/receipts.ts +99 -0
  147. package/src/plugins/ixo-transaction/tools.ts +329 -0
  148. package/src/plugins/ixo-transaction/wallet-signing.ts +202 -0
  149. package/src/plugins/mcp-tool-adapter.ts +23 -2
  150. package/src/plugins/memory/memory-tools.ts +7 -2
  151. package/src/plugins/memory/memory-ucan.ts +3 -2
  152. package/src/plugins/memory/memory.plugin.ts +2 -0
  153. package/src/plugins/pod-creator/blueprint-store.ts +163 -0
  154. package/src/plugins/pod-creator/blueprint-types.ts +61 -0
  155. package/src/plugins/pod-creator/bounded-map.ts +87 -0
  156. package/src/plugins/pod-creator/capsule-content-client.ts +285 -0
  157. package/src/plugins/pod-creator/chain-gateway.ts +110 -0
  158. package/src/plugins/pod-creator/config.ts +85 -0
  159. package/src/plugins/pod-creator/create-session-store.ts +180 -0
  160. package/src/plugins/pod-creator/create-tools.ts +538 -0
  161. package/src/plugins/pod-creator/design-pod-roles.ts +111 -0
  162. package/src/plugins/pod-creator/index.ts +65 -0
  163. package/src/plugins/pod-creator/orchestration-tools.ts +172 -0
  164. package/src/plugins/pod-creator/pod-creator.plugin.ts +190 -0
  165. package/src/plugins/pod-creator/stage.ts +201 -0
  166. package/src/plugins/pod-creator/sub-agents.ts +189 -0
  167. package/src/plugins/portal/action-log.ts +21 -1
  168. package/src/plugins/portal/declared-tools.ts +90 -0
  169. package/src/plugins/portal/portal.plugin.ts +42 -16
  170. package/src/plugins/sandbox/sandbox-bridge.ts +79 -10
  171. package/src/plugins/sandbox/sandbox-mcp.ts +3 -2
  172. package/src/plugins/sandbox/sandbox-write-blob.ts +11 -1
  173. package/src/plugins/sandbox/sandbox.plugin.ts +8 -16
  174. package/src/plugins/tasks/manifest.ts +1 -1
  175. package/src/plugins/tasks/middleware.ts +20 -7
  176. package/src/plugins/tasks/tasks-tools.ts +30 -3
  177. package/src/plugins/tasks/tasks.plugin.ts +64 -11
  178. package/src/plugins/user-preferences/schema.ts +12 -1
  179. package/src/plugins/user-preferences/user-preferences-store.ts +38 -13
  180. package/src/plugins/user-preferences/user-preferences-tool.ts +3 -2
  181. package/src/plugins/vfs/vfs-client.ts +250 -71
  182. package/src/plugins/vfs/vfs-content.ts +30 -11
  183. package/src/plugins/vfs/vfs-errors.ts +22 -0
  184. package/src/plugins/vfs/vfs-sandbox-tools.ts +12 -5
  185. package/src/plugins/vfs/vfs-tools.ts +60 -20
  186. package/src/realtime/frontend-call-registry.ts +228 -81
  187. package/src/realtime/realtime-endpoint.ts +215 -44
  188. package/src/realtime/session-socket-hub.ts +47 -2
  189. package/src/secrets/secrets-service.ts +9 -3
  190. package/src/shell/app.ts +529 -82
  191. package/src/shell/auth.ts +131 -37
  192. package/src/shell/turn-body-cap.ts +11 -13
  193. package/src/sqlite/blob-compactor.ts +61 -28
  194. package/src/sqlite/database.ts +156 -90
  195. package/src/sqlite/do-vfs.ts +416 -129
  196. package/src/sqlite/page-tier.ts +133 -40
  197. package/src/sqlite/serialization.ts +40 -0
  198. package/src/sqlite/sessions-store.ts +10 -2
  199. package/src/sqlite/sqlite-saver.ts +139 -19
  200. package/src/sqlite/user-kv-store.ts +317 -0
  201. package/src/tasks/schedule.ts +201 -12
  202. package/src/tasks/scheduler.ts +586 -98
  203. package/src/tasks/spec.ts +67 -3
  204. package/src/tasks/store.ts +292 -27
  205. package/src/tasks/topic-deliverables.ts +70 -0
@@ -0,0 +1,614 @@
1
+ /**
2
+ * The oracle's own artefact page (`GET /a/:id`), used when no shared viewer
3
+ * is configured. Identical for every artefact: the key arrives in the URL
4
+ * fragment, the page fetches `/a/:id/data`, decrypts with WebCrypto and
5
+ * renders the Markdown with DOM calls only (`textContent`, never
6
+ * `innerHTML`), so nothing in a document can run script. Images render as
7
+ * links: loading them would tell a third party the page was opened.
8
+ *
9
+ * The page script is one static string: nothing in it is built from code
10
+ * the bundler compiled, so what the browser runs (and the CSP hash pins) is
11
+ * exactly this text. Every pattern it runs is linear in its input; the tests
12
+ * evaluate the parsing part of the text itself.
13
+ */
14
+
15
+ export const VIEWER_STYLE = String.raw`
16
+ :root { --bg: #f6f7f7; --paper: #ffffff; --ink: #1c2322; --muted: #5f6b69; --line: #dfe4e2; --accent: #0e6b63; --code: #eef2f1; color-scheme: light; }
17
+ @media (prefers-color-scheme: dark) { :root { --bg: #0f1413; --paper: #171f1e; --ink: #e2e8e6; --muted: #98a4a1; --line: #2b3533; --accent: #5bbdb2; --code: #1f2a28; color-scheme: dark; } }
18
+ * { box-sizing: border-box; }
19
+ body { margin: 0; background: var(--bg); color: var(--ink); font: 17px/1.62 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; }
20
+ main { max-width: 46rem; margin: 0 auto; padding: 28px 18px 64px; }
21
+ header { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px 16px; margin-bottom: 18px; }
22
+ .from { font-size: 0.82rem; color: var(--muted); letter-spacing: 0.02em; }
23
+ .actions { display: flex; gap: 8px; }
24
+ button, .download { font: inherit; font-size: 0.85rem; padding: 6px 12px; border-radius: 6px; border: 1px solid var(--line); background: var(--paper); color: var(--ink); cursor: pointer; text-decoration: none; }
25
+ button:focus-visible, a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
26
+ article { background: var(--paper); border: 1px solid var(--line); border-radius: 10px; padding: 24px 22px; overflow-wrap: anywhere; }
27
+ article h1, article h2, article h3, article h4 { line-height: 1.25; margin: 1.4em 0 0.5em; }
28
+ article h1:first-child, article h2:first-child, article p:first-child { margin-top: 0; }
29
+ article h1 { font-size: 1.6rem; } article h2 { font-size: 1.3rem; } article h3 { font-size: 1.1rem; }
30
+ article p, article ul, article ol, article blockquote, article pre, .table { margin: 0 0 1em; }
31
+ article a { color: var(--accent); }
32
+ article code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.88em; background: var(--code); padding: 0.1em 0.35em; border-radius: 4px; }
33
+ article pre { background: var(--code); padding: 12px 14px; border-radius: 8px; overflow-x: auto; }
34
+ article pre code { background: none; padding: 0; }
35
+ article blockquote { border-left: 3px solid var(--line); margin-left: 0; padding-left: 14px; color: var(--muted); }
36
+ article hr { border: none; border-top: 1px solid var(--line); margin: 1.5em 0; }
37
+ .table { overflow-x: auto; }
38
+ article table { border-collapse: collapse; width: 100%; font-size: 0.92rem; font-variant-numeric: tabular-nums; }
39
+ article th, article td { border-bottom: 1px solid var(--line); padding: 7px 10px; text-align: left; vertical-align: top; }
40
+ article th { font-weight: 600; }
41
+ .note { color: var(--muted); }
42
+ footer { margin-top: 16px; font-size: 0.8rem; color: var(--muted); }
43
+ `;
44
+
45
+ export const VIEWER_SCRIPT = String.raw`
46
+ (function () {
47
+ 'use strict';
48
+ // ── Inline and block parsing: pure, linear in its input ──
49
+ // A document is model output and may carry text shaped by anything the
50
+ // model read, so no line may make the reader's tab backtrack for seconds:
51
+ // every closer is found by a forward search whose start only moves on.
52
+
53
+ // The inline constructs of a paragraph, left to right, in one pass: code
54
+ // spans (a backtick run closed by a run of the same length), **, __, ~~,
55
+ // *, _ (not inside a word), images, links, <https://...> and bare http(s)
56
+ // URLs.
57
+ function scanInline(value) {
58
+ var tokens = [];
59
+ var n = value.length;
60
+ var word = /[A-Za-z0-9_]/;
61
+ var space = /\s/;
62
+ // Backtick runs: start, length, and per length the runs in order.
63
+ var runStart = [];
64
+ var runLength = [];
65
+ var runsOfLength = {};
66
+ var runCursor = {};
67
+ var runAt = {};
68
+ for (var r = 0; r < n; r++) {
69
+ if (value.charAt(r) !== '\x60') continue;
70
+ var length = 1;
71
+ while (r + length < n && value.charAt(r + length) === '\x60') length++;
72
+ runAt[r] = runStart.length;
73
+ if (!runsOfLength[length]) {
74
+ runsOfLength[length] = [];
75
+ runCursor[length] = 0;
76
+ }
77
+ runsOfLength[length].push(runStart.length);
78
+ runStart.push(r);
79
+ runLength.push(length);
80
+ r += length - 1;
81
+ }
82
+ // The next closer at or after the last query of each kind (-1: none).
83
+ var nextStrong = -2;
84
+ var nextUnder = -2;
85
+ var nextTilde = -2;
86
+ var nextStar = -2;
87
+ var nextEmUnder = -2;
88
+ var nextBracket = -2;
89
+ var nextAngle = -2;
90
+ var nextUrlEnd = -2;
91
+ // The target scanned after the last ]( (every opener before it shares it).
92
+ var targetOf = -1;
93
+ var targetAt = 0;
94
+ var targetEnd = -1;
95
+ var i = 0;
96
+ while (i < n) {
97
+ var c = value.charAt(i);
98
+ var after = value.charAt(i + 1);
99
+ if (c === '\x60' && runAt[i] !== undefined) {
100
+ var run = runAt[i];
101
+ var k = runLength[run];
102
+ var same = runsOfLength[k];
103
+ while (runCursor[k] < same.length && same[runCursor[k]] <= run)
104
+ runCursor[k]++;
105
+ if (runCursor[k] < same.length) {
106
+ var closer = runStart[same[runCursor[k]]];
107
+ tokens.push({
108
+ kind: 'code',
109
+ start: i,
110
+ end: closer + k,
111
+ text: value.slice(i + k, closer),
112
+ href: '',
113
+ });
114
+ i = closer + k;
115
+ continue;
116
+ }
117
+ i += k;
118
+ continue;
119
+ }
120
+ if ((c === '*' || c === '_' || c === '~') && after === c) {
121
+ var pair = c + c;
122
+ var found = c === '*' ? nextStrong : c === '_' ? nextUnder : nextTilde;
123
+ if (found !== -1 && found < i + 3) found = value.indexOf(pair, i + 3);
124
+ if (c === '*') nextStrong = found;
125
+ else if (c === '_') nextUnder = found;
126
+ else nextTilde = found;
127
+ if (found !== -1) {
128
+ tokens.push({
129
+ kind: c === '~' ? 'del' : 'strong',
130
+ start: i,
131
+ end: found + 2,
132
+ text: value.slice(i + 2, found),
133
+ href: '',
134
+ });
135
+ i = found + 2;
136
+ continue;
137
+ }
138
+ }
139
+ if (c === '*' && i + 1 < n && after !== '*' && !space.test(after)) {
140
+ if (nextStar !== -1 && nextStar < i + 2)
141
+ nextStar = value.indexOf('*', i + 2);
142
+ if (nextStar !== -1) {
143
+ tokens.push({
144
+ kind: 'em',
145
+ start: i,
146
+ end: nextStar + 1,
147
+ text: value.slice(i + 1, nextStar),
148
+ href: '',
149
+ });
150
+ i = nextStar + 1;
151
+ continue;
152
+ }
153
+ }
154
+ if (
155
+ c === '_' &&
156
+ i + 1 < n &&
157
+ after !== '_' &&
158
+ !space.test(after) &&
159
+ (i === 0 || !word.test(value.charAt(i - 1)))
160
+ ) {
161
+ if (nextEmUnder !== -1 && nextEmUnder < i + 2) {
162
+ nextEmUnder = value.indexOf('_', i + 2);
163
+ while (
164
+ nextEmUnder !== -1 &&
165
+ nextEmUnder + 1 < n &&
166
+ word.test(value.charAt(nextEmUnder + 1))
167
+ )
168
+ nextEmUnder = value.indexOf('_', nextEmUnder + 1);
169
+ }
170
+ if (nextEmUnder !== -1) {
171
+ tokens.push({
172
+ kind: 'em',
173
+ start: i,
174
+ end: nextEmUnder + 1,
175
+ text: value.slice(i + 1, nextEmUnder),
176
+ href: '',
177
+ });
178
+ i = nextEmUnder + 1;
179
+ continue;
180
+ }
181
+ }
182
+ var open = c === '[' ? i : c === '!' && after === '[' ? i + 1 : -1;
183
+ if (open !== -1) {
184
+ if (nextBracket !== -1 && nextBracket < open + 1)
185
+ nextBracket = value.indexOf(']', open + 1);
186
+ var close = nextBracket;
187
+ var image = open !== i;
188
+ if (
189
+ close !== -1 &&
190
+ (image || close > open + 1) &&
191
+ value.charAt(close + 1) === '(' &&
192
+ targetOf !== close
193
+ ) {
194
+ // The target: no spaces or parentheses, except balanced (…) pairs.
195
+ var at = close + 2;
196
+ for (;;) {
197
+ var ch = value.charAt(at);
198
+ if (at < n && ch !== '(' && ch !== ')' && !space.test(ch)) {
199
+ at++;
200
+ continue;
201
+ }
202
+ if (ch === '(') {
203
+ var inner = at + 1;
204
+ while (inner < n) {
205
+ var ic = value.charAt(inner);
206
+ if (ic === '(' || ic === ')' || space.test(ic)) break;
207
+ inner++;
208
+ }
209
+ if (value.charAt(inner) === ')') {
210
+ at = inner + 1;
211
+ continue;
212
+ }
213
+ }
214
+ break;
215
+ }
216
+ var end = -1;
217
+ if (at > close + 2) {
218
+ if (value.charAt(at) === ')') end = at + 1;
219
+ else if (space.test(value.charAt(at))) {
220
+ var quote = at;
221
+ while (quote < n && space.test(value.charAt(quote))) quote++;
222
+ if (value.charAt(quote) === '"') {
223
+ var shut = value.indexOf('"', quote + 1);
224
+ if (shut !== -1 && value.charAt(shut + 1) === ')') end = shut + 2;
225
+ }
226
+ }
227
+ }
228
+ targetOf = close;
229
+ targetAt = at;
230
+ targetEnd = end;
231
+ }
232
+ if (
233
+ close !== -1 &&
234
+ targetOf === close &&
235
+ targetEnd !== -1 &&
236
+ (image || close > open + 1)
237
+ ) {
238
+ tokens.push({
239
+ kind: image ? 'image' : 'link',
240
+ start: i,
241
+ end: targetEnd,
242
+ text: value.slice(open + 1, close),
243
+ href: value.slice(close + 2, targetAt),
244
+ });
245
+ i = targetEnd;
246
+ continue;
247
+ }
248
+ }
249
+ var scheme = value.startsWith('https://', c === '<' ? i + 1 : i)
250
+ ? 8
251
+ : value.startsWith('http://', c === '<' ? i + 1 : i)
252
+ ? 7
253
+ : 0;
254
+ if (c === '<' && scheme > 0) {
255
+ if (nextAngle !== -1 && nextAngle < i + 1 + scheme) {
256
+ nextAngle = i + 1 + scheme;
257
+ while (
258
+ nextAngle < n &&
259
+ value.charAt(nextAngle) !== '>' &&
260
+ !space.test(value.charAt(nextAngle))
261
+ )
262
+ nextAngle++;
263
+ if (nextAngle >= n) nextAngle = -1;
264
+ }
265
+ if (
266
+ nextAngle !== -1 &&
267
+ nextAngle > i + 1 + scheme &&
268
+ value.charAt(nextAngle) === '>'
269
+ ) {
270
+ var target = value.slice(i + 1, nextAngle);
271
+ tokens.push({
272
+ kind: 'url',
273
+ start: i,
274
+ end: nextAngle + 1,
275
+ text: target,
276
+ href: target,
277
+ });
278
+ i = nextAngle + 1;
279
+ continue;
280
+ }
281
+ }
282
+ if (c === 'h' && scheme > 0) {
283
+ if (nextUrlEnd !== -1 && nextUrlEnd < i + scheme) {
284
+ nextUrlEnd = i + scheme;
285
+ while (nextUrlEnd < n && !/[\s<>()]/.test(value.charAt(nextUrlEnd)))
286
+ nextUrlEnd++;
287
+ }
288
+ // Trailing punctuation is the sentence's, not the URL's.
289
+ var last = nextUrlEnd - 1;
290
+ while (last > i + scheme && /[.,;:!?'"]/.test(value.charAt(last))) last--;
291
+ if (last > i + scheme) {
292
+ var url = value.slice(i, last + 1);
293
+ tokens.push({
294
+ kind: 'url',
295
+ start: i,
296
+ end: last + 1,
297
+ text: url,
298
+ href: url,
299
+ });
300
+ i = last + 1;
301
+ continue;
302
+ }
303
+ }
304
+ i++;
305
+ }
306
+ return tokens;
307
+ }
308
+
309
+ // A Markdown ATX heading (## Title ##): its level and text, or null.
310
+ function parseHeading(line) {
311
+ var match = /^(#{1,6})[ \t]+(.*)$/.exec(line);
312
+ if (!match) return null;
313
+ var text = match[2].trimEnd();
314
+ // A closing run of #s is dropped when a space sets it apart (or it is
315
+ // all there is); # C# keeps its #.
316
+ var cut = text.length;
317
+ while (cut > 0 && text.charAt(cut - 1) === '#') cut--;
318
+ if (cut === 0) text = '';
319
+ else if (cut < text.length && /[ \t]/.test(text.charAt(cut - 1)))
320
+ text = text.slice(0, cut).trimEnd();
321
+ return { level: match[1].length, text: text };
322
+ }
323
+
324
+ // The |---|:---:| line under a table's header row.
325
+ var DIVIDER =
326
+ /^[ \t]*(?:\|[ \t]*)?:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)*(?:\|[ \t]*)?$/;
327
+ // ── End of parsing ──
328
+ var article = document.getElementById('doc');
329
+ var status = document.getElementById('status');
330
+ var actions = document.getElementById('actions');
331
+ var footer = document.getElementById('meta');
332
+ var TICK = '\x60';
333
+
334
+ function el(tag, text) {
335
+ var node = document.createElement(tag);
336
+ if (text !== undefined) node.textContent = text;
337
+ return node;
338
+ }
339
+ function fail(message) {
340
+ status.textContent = message;
341
+ status.hidden = false;
342
+ article.hidden = true;
343
+ }
344
+ function safeHref(href) {
345
+ try {
346
+ var url = new URL(href, location.href);
347
+ return url.protocol === 'https:' || url.protocol === 'http:' || url.protocol === 'mailto:' ? url.href : null;
348
+ } catch (e) {
349
+ return null;
350
+ }
351
+ }
352
+ function link(href, parent) {
353
+ var safe = safeHref(href);
354
+ if (!safe) return null;
355
+ var a = el('a');
356
+ a.href = safe;
357
+ a.rel = 'noopener noreferrer';
358
+ a.target = '_blank';
359
+ parent.appendChild(a);
360
+ return a;
361
+ }
362
+ function text(value, parent) {
363
+ var lines = value.split('\n');
364
+ for (var i = 0; i < lines.length; i++) {
365
+ if (i > 0) parent.appendChild(el('br'));
366
+ if (lines[i]) parent.appendChild(document.createTextNode(lines[i]));
367
+ }
368
+ }
369
+
370
+ function inline(value, parent) {
371
+ var tokens = scanInline(value);
372
+ var last = 0;
373
+ for (var i = 0; i < tokens.length; i++) {
374
+ var t = tokens[i];
375
+ if (t.start > last) text(value.slice(last, t.start), parent);
376
+ last = t.end;
377
+ if (t.kind === 'code') parent.appendChild(el('code', t.text));
378
+ else if (t.kind === 'strong') inline(t.text, parent.appendChild(el('strong')));
379
+ else if (t.kind === 'del') inline(t.text, parent.appendChild(el('del')));
380
+ else if (t.kind === 'em') inline(t.text, parent.appendChild(el('em')));
381
+ else if (t.kind === 'image') {
382
+ var image = link(t.href, parent);
383
+ if (image) image.textContent = 'Image: ' + (t.text || t.href);
384
+ else text(t.text || '', parent);
385
+ } else if (t.kind === 'link') {
386
+ var anchor = link(t.href, parent);
387
+ if (anchor) inline(t.text, anchor);
388
+ else inline(t.text, parent);
389
+ } else {
390
+ var auto = link(t.href, parent);
391
+ if (auto) auto.textContent = t.href;
392
+ else text(t.href, parent);
393
+ }
394
+ }
395
+ if (last < value.length) text(value.slice(last), parent);
396
+ }
397
+
398
+ var LIST = /^(\s*)([-*+]|\d+[.)])\s+(.*)$/;
399
+ var FENCE = new RegExp('^\\s*(' + TICK + '{3,}|~{3,})');
400
+ var RULE = /^\s*([-*_])(\s*\1){2,}\s*$/;
401
+
402
+ function isBlockStart(line, next) {
403
+ return FENCE.test(line) || parseHeading(line) !== null || RULE.test(line) || /^\s*>/.test(line) || LIST.test(line) ||
404
+ (line.indexOf('|') !== -1 && next !== undefined && DIVIDER.test(next));
405
+ }
406
+ function cells(line) {
407
+ var trimmed = line.trim().replace(/^\|/, '').replace(/\|$/, '');
408
+ return trimmed.split('|').map(function (c) { return c.trim(); });
409
+ }
410
+ function list(lines, i, indent) {
411
+ var first = lines[i].match(LIST);
412
+ var ordered = /\d/.test(first[2]);
413
+ var node = el(ordered ? 'ol' : 'ul');
414
+ if (ordered) node.start = parseInt(first[2], 10);
415
+ while (i < lines.length) {
416
+ var line = lines[i];
417
+ var m = line.match(LIST);
418
+ if (!line.trim()) {
419
+ var ahead = i + 1;
420
+ while (ahead < lines.length && !lines[ahead].trim()) ahead++;
421
+ var nextItem = ahead < lines.length ? lines[ahead].match(LIST) : null;
422
+ if (nextItem && nextItem[1].length >= indent) { i = ahead; continue; }
423
+ break;
424
+ }
425
+ if (m && m[1].length > indent && node.lastChild) {
426
+ var nested = list(lines, i, m[1].length);
427
+ node.lastChild.appendChild(nested[0]);
428
+ i = nested[1];
429
+ continue;
430
+ }
431
+ if (!m && /^\s+\S/.test(line) && node.lastChild) {
432
+ node.lastChild.appendChild(el('br'));
433
+ inline(line.trim(), node.lastChild);
434
+ i++;
435
+ continue;
436
+ }
437
+ if (!m || m[1].length !== indent || /\d/.test(m[2]) !== ordered) break;
438
+ var item = el('li');
439
+ var task = m[3].match(/^\[( |x|X)\]\s+(.*)$/);
440
+ if (task) {
441
+ item.appendChild(document.createTextNode(task[1] === ' ' ? '☐ ' : '☑ '));
442
+ inline(task[2], item);
443
+ } else inline(m[3], item);
444
+ node.appendChild(item);
445
+ i++;
446
+ }
447
+ return [node, i];
448
+ }
449
+ function blocks(source, parent) {
450
+ var lines = source.replace(/\r\n?/g, '\n').split('\n');
451
+ var i = 0;
452
+ while (i < lines.length) {
453
+ var line = lines[i];
454
+ if (!line.trim()) { i++; continue; }
455
+ var fence = line.match(FENCE);
456
+ if (fence) {
457
+ var body = [];
458
+ i++;
459
+ while (i < lines.length && lines[i].trim().indexOf(fence[1]) !== 0) { body.push(lines[i]); i++; }
460
+ i++;
461
+ parent.appendChild(el('pre')).appendChild(el('code', body.join('\n')));
462
+ continue;
463
+ }
464
+ var heading = parseHeading(line);
465
+ if (heading) { inline(heading.text, parent.appendChild(el('h' + heading.level))); i++; continue; }
466
+ if (RULE.test(line)) { parent.appendChild(el('hr')); i++; continue; }
467
+ if (/^\s*>/.test(line)) {
468
+ var quoted = [];
469
+ while (i < lines.length && /^\s*>/.test(lines[i])) { quoted.push(lines[i].replace(/^\s*>\s?/, '')); i++; }
470
+ blocks(quoted.join('\n'), parent.appendChild(el('blockquote')));
471
+ continue;
472
+ }
473
+ if (line.indexOf('|') !== -1 && i + 1 < lines.length && DIVIDER.test(lines[i + 1])) {
474
+ var wrap = parent.appendChild(el('div'));
475
+ wrap.className = 'table';
476
+ var table = wrap.appendChild(el('table'));
477
+ var headRow = table.appendChild(el('thead')).appendChild(el('tr'));
478
+ cells(line).forEach(function (c) { inline(c, headRow.appendChild(el('th'))); });
479
+ var tbody = table.appendChild(el('tbody'));
480
+ i += 2;
481
+ while (i < lines.length && lines[i].trim() && lines[i].indexOf('|') !== -1) {
482
+ var row = tbody.appendChild(el('tr'));
483
+ cells(lines[i]).forEach(function (c) { inline(c, row.appendChild(el('td'))); });
484
+ i++;
485
+ }
486
+ continue;
487
+ }
488
+ var item = line.match(LIST);
489
+ if (item) {
490
+ var built = list(lines, i, item[1].length);
491
+ parent.appendChild(built[0]);
492
+ i = built[1];
493
+ continue;
494
+ }
495
+ var paragraph = [line];
496
+ i++;
497
+ while (i < lines.length && lines[i].trim() && !isBlockStart(lines[i], lines[i + 1])) { paragraph.push(lines[i]); i++; }
498
+ inline(paragraph.join('\n'), parent.appendChild(el('p')));
499
+ }
500
+ }
501
+
502
+ function b64(value) {
503
+ var padded = value.replace(/-/g, '+').replace(/_/g, '/');
504
+ padded += '===='.slice((padded.length % 4) || 4);
505
+ var binary = atob(padded);
506
+ var bytes = new Uint8Array(binary.length);
507
+ for (var i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
508
+ return bytes;
509
+ }
510
+ function slug(value) {
511
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 60) || 'document';
512
+ }
513
+
514
+ var key = new URLSearchParams(location.hash.slice(1)).get('k');
515
+ if (!key) { fail('This link is incomplete. Ask for the link again and open it in full.'); return; }
516
+ var dataUrl = location.pathname.replace(/\/$/, '') + '/data';
517
+ fetch(dataUrl, { cache: 'no-store', credentials: 'omit' })
518
+ .then(function (response) {
519
+ if (response.status === 404 || response.status === 410) throw new Error('gone');
520
+ if (!response.ok) throw new Error('unavailable');
521
+ return response.arrayBuffer();
522
+ })
523
+ .then(function (buffer) {
524
+ var sealed = new Uint8Array(buffer);
525
+ return crypto.subtle.importKey('raw', b64(key), 'AES-GCM', false, ['decrypt']).then(function (cryptoKey) {
526
+ return crypto.subtle.decrypt({ name: 'AES-GCM', iv: sealed.slice(0, 12) }, cryptoKey, sealed.slice(12));
527
+ });
528
+ })
529
+ .then(function (plain) {
530
+ var doc = JSON.parse(new TextDecoder().decode(plain));
531
+ document.title = doc.title;
532
+ status.hidden = true;
533
+ article.hidden = false;
534
+ var markdown = String(doc.content);
535
+ if (!/^\s*#\s/.test(markdown)) article.appendChild(el('h1', doc.title));
536
+ blocks(markdown, article);
537
+ footer.textContent = 'Created ' + new Date(doc.createdAt).toLocaleString() + '. Anyone with this link can read it until it expires.';
538
+ var copy = el('button', 'Copy text');
539
+ copy.type = 'button';
540
+ copy.addEventListener('click', function () {
541
+ navigator.clipboard.writeText(markdown).then(function () { copy.textContent = 'Copied'; }, function () { copy.textContent = 'Copy failed'; });
542
+ });
543
+ var download = el('a', 'Download .md');
544
+ download.className = 'download';
545
+ download.href = URL.createObjectURL(new Blob([markdown], { type: 'text/markdown' }));
546
+ download.download = slug(doc.title) + '.md';
547
+ actions.appendChild(copy);
548
+ actions.appendChild(download);
549
+ })
550
+ .catch(function (error) {
551
+ fail(error && error.message === 'gone'
552
+ ? 'This document has expired or was removed.'
553
+ : 'This document could not be opened. The link may be incomplete or damaged.');
554
+ });
555
+ })();
556
+ `;
557
+
558
+ function escapeHtml(value: string): string {
559
+ return value
560
+ .replace(/&/g, '&amp;')
561
+ .replace(/</g, '&lt;')
562
+ .replace(/>/g, '&gt;')
563
+ .replace(/"/g, '&quot;');
564
+ }
565
+
566
+ async function sha256Base64(text: string): Promise<string> {
567
+ const digest = await crypto.subtle.digest(
568
+ 'SHA-256',
569
+ new TextEncoder().encode(text),
570
+ );
571
+ let binary = '';
572
+ for (const byte of new Uint8Array(digest))
573
+ binary += String.fromCharCode(byte);
574
+ return btoa(binary);
575
+ }
576
+
577
+ let policyPromise: Promise<string> | undefined;
578
+
579
+ /** Only this page's own script and style may run; it may only fetch its origin. */
580
+ export function viewerContentSecurityPolicy(): Promise<string> {
581
+ policyPromise ??= Promise.all([
582
+ sha256Base64(VIEWER_SCRIPT),
583
+ sha256Base64(VIEWER_STYLE),
584
+ ]).then(
585
+ ([script, style]) =>
586
+ `default-src 'none'; script-src 'sha256-${script}'; style-src 'sha256-${style}'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'`,
587
+ );
588
+ return policyPromise;
589
+ }
590
+
591
+ export function viewerHtml(oracleName: string): string {
592
+ return `<!doctype html>
593
+ <html lang="en">
594
+ <head>
595
+ <meta charset="utf-8">
596
+ <meta name="viewport" content="width=device-width, initial-scale=1">
597
+ <meta name="robots" content="noindex, nofollow">
598
+ <meta name="referrer" content="no-referrer">
599
+ <meta property="og:title" content="Shared document">
600
+ <meta property="og:description" content="Open the link to read it.">
601
+ <title>Shared document</title>
602
+ <style>${VIEWER_STYLE}</style>
603
+ </head>
604
+ <body>
605
+ <main>
606
+ <header><span class="from">Shared by ${escapeHtml(oracleName)}</span><span class="actions" id="actions"></span></header>
607
+ <p class="note" id="status">Opening…</p>
608
+ <article id="doc" hidden></article>
609
+ <footer id="meta"></footer>
610
+ </main>
611
+ <script>${VIEWER_SCRIPT}</script>
612
+ </body>
613
+ </html>`;
614
+ }