makersclaw 0.5.2 → 0.7.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 (148) hide show
  1. package/README.md +73 -7
  2. package/package.json +3 -2
  3. package/r/annotate.json +4 -4
  4. package/r/approval-request.json +3 -3
  5. package/r/audio-player.json +3 -3
  6. package/r/auth-form.json +2 -2
  7. package/r/board.json +4 -4
  8. package/r/calendar-view.json +4 -4
  9. package/r/cart.json +3 -3
  10. package/r/chart.json +3 -3
  11. package/r/chat.json +3 -3
  12. package/r/code-editor.json +4 -4
  13. package/r/comment-box.json +4 -4
  14. package/r/contact-section.json +3 -3
  15. package/r/core.json +3 -3
  16. package/r/csv-import.json +4 -4
  17. package/r/data-table.json +3 -3
  18. package/r/diagram.json +3 -3
  19. package/r/doc-editor.json +4 -4
  20. package/r/doc-page.json +4 -4
  21. package/r/email-render.json +2 -2
  22. package/r/faq.json +2 -2
  23. package/r/file-upload.json +3 -3
  24. package/r/flow-canvas.json +4 -4
  25. package/r/footer.json +3 -3
  26. package/r/gantt.json +4 -4
  27. package/r/job-panel.json +3 -3
  28. package/r/json-editor.json +4 -4
  29. package/r/map.json +5 -5
  30. package/r/menubar.json +2 -2
  31. package/r/mind-map.json +4 -4
  32. package/r/navigation-menu.json +2 -2
  33. package/r/order-detail.json +2 -2
  34. package/r/org-chart.json +4 -4
  35. package/r/page-builder.json +6 -6
  36. package/r/payment-methods.json +2 -2
  37. package/r/pdf-document.json +2 -2
  38. package/r/pdf-viewer.json +6 -6
  39. package/r/product-detail.json +3 -3
  40. package/r/product-grid.json +4 -4
  41. package/r/record-form.json +3 -3
  42. package/r/record-notes.json +5 -5
  43. package/r/registry.json +121 -121
  44. package/r/search-dialog.json +3 -3
  45. package/r/settings-list.json +3 -3
  46. package/r/setup-checklist.json +3 -3
  47. package/r/share-dialog.json +3 -3
  48. package/r/sidebar.json +2 -2
  49. package/r/signature-pad.json +3 -3
  50. package/r/sketch-canvas.json +5 -5
  51. package/r/spreadsheet.json +3 -3
  52. package/r/stat-cards.json +4 -4
  53. package/r/task-list.json +4 -4
  54. package/r/task-model.json +3 -3
  55. package/r/testimonials.json +2 -2
  56. package/r/transaction-list.json +3 -3
  57. package/r/whiteboard.json +4 -4
  58. package/registry/blocks/annotate/README.md +14 -5
  59. package/registry/blocks/approval-request/README.md +15 -8
  60. package/registry/blocks/audio-player/README.md +17 -10
  61. package/registry/blocks/board/README.md +13 -9
  62. package/registry/blocks/board/board.jsx +1 -1
  63. package/registry/blocks/calendar-view/README.md +28 -4
  64. package/registry/blocks/cart/README.md +14 -10
  65. package/registry/blocks/chart/README.md +14 -7
  66. package/registry/blocks/chat/README.md +43 -13
  67. package/registry/blocks/code-editor/README.md +4 -0
  68. package/registry/blocks/code-editor/code-editor.jsx +1 -1
  69. package/registry/blocks/comment-box/README.md +20 -13
  70. package/registry/blocks/contact-section/README.md +7 -6
  71. package/registry/blocks/csv-import/README.md +34 -12
  72. package/registry/blocks/data-table/README.md +41 -8
  73. package/registry/blocks/diagram/diagram.jsx +1 -1
  74. package/registry/blocks/doc-editor/README.md +18 -6
  75. package/registry/blocks/doc-editor/doc-editor.jsx +1 -1
  76. package/registry/blocks/doc-page/README.md +16 -6
  77. package/registry/blocks/file-upload/README.md +7 -1
  78. package/registry/blocks/flow-canvas/README.md +16 -5
  79. package/registry/blocks/flow-canvas/flow-canvas.jsx +1 -1
  80. package/registry/blocks/footer/README.md +4 -2
  81. package/registry/blocks/gantt/README.md +18 -12
  82. package/registry/blocks/job-panel/README.md +1 -1
  83. package/registry/blocks/json-editor/README.md +16 -4
  84. package/registry/blocks/map/README.md +19 -3
  85. package/registry/blocks/map/map-view.jsx +1 -1
  86. package/registry/blocks/map/maplibre.js +1 -1
  87. package/registry/blocks/mind-map/README.md +16 -5
  88. package/registry/blocks/org-chart/README.md +10 -7
  89. package/registry/blocks/page-builder/README.md +13 -6
  90. package/registry/blocks/page-builder/page-builder.jsx +1 -1
  91. package/registry/blocks/page-builder/page-view.jsx +1 -1
  92. package/registry/blocks/page-builder/page.js +1 -1
  93. package/registry/blocks/pdf-viewer/README.md +22 -20
  94. package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1 -1
  95. package/registry/blocks/pdf-viewer/pdfjs.js +1 -1
  96. package/registry/blocks/pdf-viewer/ssr-shim.js +1 -1
  97. package/registry/blocks/product-detail/README.md +13 -7
  98. package/registry/blocks/product-detail/demo.jsx +1 -1
  99. package/registry/blocks/product-grid/README.md +12 -10
  100. package/registry/blocks/record-form/README.md +26 -9
  101. package/registry/blocks/record-notes/README.md +42 -30
  102. package/registry/blocks/search-dialog/README.md +38 -7
  103. package/registry/blocks/settings-list/README.md +29 -17
  104. package/registry/blocks/setup-checklist/README.md +12 -14
  105. package/registry/blocks/share-dialog/README.md +1 -1
  106. package/registry/blocks/signature-pad/README.md +15 -4
  107. package/registry/blocks/sketch-canvas/README.md +13 -4
  108. package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1 -1
  109. package/registry/blocks/sketch-canvas/ssr-shim.js +3 -3
  110. package/registry/blocks/spreadsheet/README.md +17 -8
  111. package/registry/blocks/stat-cards/README.md +17 -8
  112. package/registry/blocks/task-list/README.md +14 -11
  113. package/registry/blocks/task-model/README.md +3 -3
  114. package/registry/blocks/transaction-list/README.md +15 -7
  115. package/registry/blocks/whiteboard/README.md +19 -13
  116. package/src/cli.mjs +65 -9
  117. package/src/install.mjs +360 -21
  118. package/src/lockfile.mjs +468 -0
  119. package/src/pm.mjs +473 -0
  120. package/src/registry.mjs +125 -1
  121. package/templates/next/browser/app/auto-refresh.jsx +67 -0
  122. package/templates/next/browser/app/layout.jsx +94 -0
  123. package/templates/next/browser/app/page.jsx +60 -0
  124. package/templates/next/browser/app/s/[key]/page.jsx +95 -0
  125. package/templates/next/browser/lib/actions.js +16 -0
  126. package/templates/next/browser/lib/data.js +43 -0
  127. package/templates/next/files/README.md +818 -82
  128. package/templates/next/files/app/claw-alive/route.js +2 -2
  129. package/templates/next/files/app/frame.js +134 -9
  130. package/templates/next/files/app/layout.jsx +2 -2
  131. package/templates/next/files/check/run.mjs +1491 -53
  132. package/templates/next/files/components/ui/item.jsx +2 -2
  133. package/templates/next/files/components/ui/notice.jsx +1 -1
  134. package/templates/next/files/components/ui/separator.jsx +1 -1
  135. package/templates/next/files/lib/actions.js +171 -52
  136. package/templates/next/files/lib/brain-server.js +96 -0
  137. package/templates/next/files/lib/brain.js +413 -0
  138. package/templates/next/files/lib/claw.js +11 -0
  139. package/templates/next/files/lib/data.js +120 -30
  140. package/templates/next/files/lib/db.js +97 -17
  141. package/templates/next/files/lib/door.js +680 -0
  142. package/templates/next/files/next.config.mjs +85 -9
  143. package/templates/next/files/package.json +3 -3
  144. package/templates/next/files/{middleware.js → proxy.js} +13 -6
  145. package/templates/next/files/surfaces.js +3 -2
  146. package/templates/next/pnpm-lock.yaml +5870 -0
  147. package/templates/next/template.json +13 -6
  148. package/templates/next/files/schema.sql +0 -22
@@ -4,8 +4,8 @@
4
4
  // before every refresh: only a 2xx from here means the refresh will get
5
5
  // its payload and not reload the frame (app/frame.js, answering, has the
6
6
  // story). It renders nothing and reads nothing, so the question costs the
7
- // app almost nothing; the middleware's token check still runs first, as
8
- // on every request.
7
+ // app almost nothing; proxy.js's token check still runs first, as on
8
+ // every request.
9
9
  //
10
10
  // A 204 and not the 404 of a path nobody serves: the browser logs every
11
11
  // 404 fetch as a console error, and an app that logs one every 5 s reads
@@ -63,7 +63,8 @@ export const PRESENCE_EVENTS = ["pointerdown", "pointermove", "keydown", "wheel"
63
63
  * and no probe, while any element on the page carries it. For a surface
64
64
  * whose rows only its own writes change, or one too big to send every
65
65
  * few seconds (a base of 4,812 records was about 2 MB a refresh). Its
66
- * writes still re-render it (lib/actions.js revalidates), and so does
66
+ * writes still re-render it (a server app's lib/actions.js revalidates;
67
+ * a browser app's writes call lib/door.js dataChanged()), and so does
67
68
  * every navigation. */
68
69
  export const REFRESH_OFF = '[data-claw-refresh="off"]';
69
70
 
@@ -142,9 +143,9 @@ export function nextRefreshIn(lastCostMs = 0) {
142
143
  }
143
144
 
144
145
  /** What to ask before a refresh: app/claw-alive/route.js, a fixed route
145
- * that answers 204 with no body — no render, no database. The middleware
146
- * checks the proxy's token first, as on every request, so a 204 says the
147
- * page's own request would be answered too.
146
+ * that answers 204 with no body — no render, no database. proxy.js
147
+ * checks the product proxy's token first, as on every request, so a 204
148
+ * says the page's own request would be answered too.
148
149
  *
149
150
  * Not a path the app merely 404s. The first draft of 0.3.3 asked for a
150
151
  * file under _next/static/ that no build writes and read the 404 as
@@ -172,8 +173,9 @@ export function probeUrl(doc) {
172
173
  *
173
174
  * Why ask first: router.refresh() treats any answer that is not its own
174
175
  * payload as "this is not my app any more" and loads the whole page
175
- * again (Next 15, fetch-server-response.js: a non-OK or non-RSC response
176
- * becomes a browser navigation). In the frame that reload wipes a
176
+ * again (fetch-server-response.js: a non-OK or non-RSC response becomes
177
+ * a browser navigation; Next 16's refresh reaches it through
178
+ * ppr-navigations.js and still does). In the frame that reload wipes a
177
179
  * half-typed field, and one 502 was enough (landing-page-reviewer,
178
180
  * okr-tracker, 2026-09-24). So the probe says yes exactly where the
179
181
  * refresh would not turn into a reload. Asking first leaves only the
@@ -194,6 +196,123 @@ export async function answering(url, fetchImpl = globalThis.fetch, timeoutMs = P
194
196
  }
195
197
  }
196
198
 
199
+ // ---------------------------------------------------------------------------
200
+ // Auto-refresh in a browser app
201
+
202
+ // A browser app (makersclaw.json "mode": "browser") has no server to ask
203
+ // and nothing to re-render from: its pages are files, prerendered once,
204
+ // and its rows come from the platform's data door through lib/door.js's
205
+ // hooks. So its refresh is not router.refresh(): under an export that
206
+ // fetches the same prebuilt payload and shows nothing new. It is the
207
+ // hooks reading again (lib/door.js dataChanged(), then whenLanded()),
208
+ // and only when something changed: a question the door answers in one
209
+ // small request, where the server app's refresh re-rendered every
210
+ // surface every 5 s whether anything had changed or not (claw's
211
+ // STATIC-SPEC §3, a browser app's auto-refresh).
212
+ //
213
+ // The question is the door's change feed, GET _claw/data/changes (claw's
214
+ // CONTRACTS §8), which answers two things:
215
+ // - `changes`: the company brain's changes in the app's kinds and the
216
+ // member's areas since the cursor, and `next`, the cursor after them;
217
+ // - `tables`: a stamp of the app's own tables (Postgres's counters:
218
+ // rows written, then rows live). It moves with any write to them, the
219
+ // agent's, a job's, another member's. The platform's counters lag:
220
+ // a write shows in the stamp within about 11 s, two or three ticks.
221
+ // The first answer only sets the cursor and the stamp: the hooks read
222
+ // the rows on their own as the page opened. After it, "something
223
+ // changed" is a brain change, a cursor that moved (a brain that
224
+ // appeared, or an app whose grant went away), or a different stamp, up
225
+ // or down. A stamp that moved for nothing (autovacuum re-counting, a
226
+ // rolled-back write, a restart that zeroed the counters) costs one read
227
+ // too many, never one too few; so does the page's own write, which
228
+ // reads again at once and then shows in the stamp too.
229
+
230
+ /** The feed's path under _claw/: the changes after `since`, the last
231
+ * answer's `next`. None yet: the door answers where the feed is now. */
232
+ export function changesPath(since) {
233
+ return since === undefined || since === null || since === "" ? "data/changes" : `data/changes?since=${encodeURIComponent(String(since))}`;
234
+ }
235
+
236
+ /** An abort signal that fires at the deadline or on `stop`, whichever is
237
+ * first. A browser without AbortSignal.any keeps the deadline: a held
238
+ * request must not stall every tick after it. */
239
+ function askSignal(timeoutMs, stop) {
240
+ const timeout = typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(timeoutMs) : undefined;
241
+ if (!timeout || !stop) return timeout ?? stop;
242
+ return typeof AbortSignal.any === "function" ? AbortSignal.any([timeout, stop]) : timeout;
243
+ }
244
+
245
+ /**
246
+ * The browser app's probe: has anything changed since the last refresh?
247
+ * `ask(path, signal)` sends GET _claw/<path> and resolves to lib/claw.js
248
+ * claw()'s answer ({ ok, status, data, unavailable }); it may throw, and
249
+ * a throw is no answer. → { probe, covered, stop }
250
+ *
251
+ * - probe() resolves true when a change was seen that no refresh has
252
+ * covered yet. It asks the door every time (one ask at a time: a
253
+ * second probe while one is out shares its answer), within
254
+ * PROBE_TIMEOUT_MS.
255
+ * - covered(): a refresh has started, so what was seen is on its way.
256
+ * A change stays owed until then: a tick that saw one and found the
257
+ * founder typing refreshes on a later tick, not never.
258
+ * - A failed answer changes nothing: the cursor and the stamp stay, and
259
+ * the next tick asks again. A 5xx, a 401 (a grant gone stale), no
260
+ * network, no answer in time. A refresh would read nothing new.
261
+ * - A 404 that names no code is no door at all (a Makersclaw from
262
+ * before it, a `next dev` with nothing in front of it): the feed
263
+ * stops asking for the page's life, so the console does not log a
264
+ * failed request every five seconds.
265
+ * - stop() aborts an ask in flight; every probe after it is false.
266
+ */
267
+ export function changeFeed(ask, { timeoutMs = PROBE_TIMEOUT_MS } = {}) {
268
+ let first = true; // no answer yet: the next one only sets where we are
269
+ let cursor = null; // the feed's `next`: where the next ask starts
270
+ let stamp = null; // the own tables' stamp, as last answered
271
+ let owed = false; // a change seen and not yet covered by a refresh
272
+ let gone = false; // no door answers here
273
+ let stopped = false;
274
+ let asking = null; // the ask in flight
275
+ const halt = typeof AbortController === "function" ? new AbortController() : null;
276
+
277
+ async function askOnce() {
278
+ let answer;
279
+ try {
280
+ answer = await ask(changesPath(cursor), askSignal(timeoutMs, halt?.signal));
281
+ } catch {
282
+ return; // no answer
283
+ }
284
+ if (stopped || !answer) return;
285
+ if (!answer.ok) {
286
+ if (answer.status === 404 && answer.unavailable) gone = true;
287
+ return;
288
+ }
289
+ const data = answer.data;
290
+ if (!data || typeof data !== "object" || Array.isArray(data) || data.ok === false) return;
291
+ const next = data.next === undefined || data.next === null ? null : String(data.next);
292
+ const tables = data.tables === undefined || data.tables === null ? null : String(data.tables);
293
+ const changed = (Array.isArray(data.changes) && data.changes.length > 0) || next !== cursor || tables !== stamp;
294
+ if (!first && changed) owed = true;
295
+ first = false;
296
+ cursor = next;
297
+ stamp = tables;
298
+ }
299
+
300
+ return {
301
+ probe() {
302
+ if (stopped || gone) return Promise.resolve(false);
303
+ if (!asking) asking = askOnce().finally(() => (asking = null));
304
+ return asking.then(() => owed && !stopped);
305
+ },
306
+ covered() {
307
+ owed = false;
308
+ },
309
+ stop() {
310
+ stopped = true;
311
+ halt?.abort();
312
+ },
313
+ };
314
+ }
315
+
197
316
  /** `promise`, or undefined once `ms` have passed, whichever comes first. */
198
317
  function within(promise, ms, timers) {
199
318
  let timer = null;
@@ -204,8 +323,14 @@ function within(promise, ms, timers) {
204
323
  /**
205
324
  * The refresh loop app/auto-refresh.jsx runs, as plain code over a
206
325
  * document so `npm test` can drive it. `refresh()` starts one refresh and
207
- * resolves when it has landed (the component's transition settling);
208
- * `probe()` is the question above, asked first. → { stop() }
326
+ * resolves when it has landed; `probe()` is asked first, and a tick
327
+ * refreshes only on its yes. → { stop() }
328
+ *
329
+ * The two modes differ only in those two. A server app's refresh is
330
+ * router.refresh() in a transition, landed when the transition settles,
331
+ * and its probe is `answering` (the default here): will the app answer?
332
+ * A browser app's refresh is lib/door.js dataChanged() then whenLanded(),
333
+ * and its probe is a changeFeed's: has anything changed?
209
334
  *
210
335
  * Until 0.5.0 a timer fired every REFRESH_MS whatever was in flight: the
211
336
  * probe was guarded, the refresh was not, so a render slower than the
@@ -222,7 +347,7 @@ function within(promise, ms, timers) {
222
347
  * (PRESENCE_EVENTS, in the frame): the next sign refreshes at once
223
348
  * and the loop goes on. Coming back from hidden is such a sign;
224
349
  * - a tick refreshes only when shouldRefresh says so, with the probe's
225
- * 2xx, and never while a surface has opted out (REFRESH_OFF).
350
+ * yes, and never while a surface has opted out (REFRESH_OFF).
226
351
  *
227
352
  * The rules are asked again after the probe: typing may have started, or
228
353
  * the tab changed, while it was on its way.
@@ -20,8 +20,8 @@ export const metadata = { title: process.env.CLAW_PRODUCT || "Artifact" };
20
20
  // lib/claw.js, the product name, and data-theme. The theme follows the
21
21
  // desk: the OS window opens the app with ?theme=light|dark and the
22
22
  // product proxy repeats it as the x-claw-theme header on every request
23
- // (middleware.js explains); the claw_theme cookie covers a run without
24
- // the proxy. Dark when neither says — the desk is dark.
23
+ // (proxy.js explains); the claw_theme cookie covers a run without
24
+ // the product proxy. Dark when neither says — the desk is dark.
25
25
  //
26
26
  // The palette follows the WORKSPACE: <head> links `_claw/theme.css`, the
27
27
  // preset picked in System Settings → Appearance, served by the product