makersclaw 0.6.0 → 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 (141) hide show
  1. package/README.md +48 -8
  2. package/package.json +1 -1
  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 +31 -4
  117. package/src/install.mjs +263 -21
  118. package/src/registry.mjs +78 -1
  119. package/templates/next/browser/app/auto-refresh.jsx +67 -0
  120. package/templates/next/browser/app/layout.jsx +94 -0
  121. package/templates/next/browser/app/page.jsx +60 -0
  122. package/templates/next/browser/app/s/[key]/page.jsx +95 -0
  123. package/templates/next/browser/lib/actions.js +16 -0
  124. package/templates/next/browser/lib/data.js +43 -0
  125. package/templates/next/files/README.md +810 -79
  126. package/templates/next/files/app/frame.js +128 -4
  127. package/templates/next/files/check/run.mjs +1438 -41
  128. package/templates/next/files/components/ui/item.jsx +2 -2
  129. package/templates/next/files/components/ui/notice.jsx +1 -1
  130. package/templates/next/files/components/ui/separator.jsx +1 -1
  131. package/templates/next/files/lib/actions.js +170 -51
  132. package/templates/next/files/lib/brain-server.js +96 -0
  133. package/templates/next/files/lib/brain.js +413 -0
  134. package/templates/next/files/lib/claw.js +11 -0
  135. package/templates/next/files/lib/data.js +119 -29
  136. package/templates/next/files/lib/db.js +97 -17
  137. package/templates/next/files/lib/door.js +680 -0
  138. package/templates/next/files/next.config.mjs +36 -2
  139. package/templates/next/files/surfaces.js +3 -2
  140. package/templates/next/template.json +9 -3
  141. package/templates/next/files/schema.sql +0 -22
@@ -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
 
@@ -195,6 +196,123 @@ export async function answering(url, fetchImpl = globalThis.fetch, timeoutMs = P
195
196
  }
196
197
  }
197
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
+
198
316
  /** `promise`, or undefined once `ms` have passed, whichever comes first. */
199
317
  function within(promise, ms, timers) {
200
318
  let timer = null;
@@ -205,8 +323,14 @@ function within(promise, ms, timers) {
205
323
  /**
206
324
  * The refresh loop app/auto-refresh.jsx runs, as plain code over a
207
325
  * document so `npm test` can drive it. `refresh()` starts one refresh and
208
- * resolves when it has landed (the component's transition settling);
209
- * `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?
210
334
  *
211
335
  * Until 0.5.0 a timer fired every REFRESH_MS whatever was in flight: the
212
336
  * probe was guarded, the refresh was not, so a render slower than the
@@ -223,7 +347,7 @@ function within(promise, ms, timers) {
223
347
  * (PRESENCE_EVENTS, in the frame): the next sign refreshes at once
224
348
  * and the loop goes on. Coming back from hidden is such a sign;
225
349
  * - a tick refreshes only when shouldRefresh says so, with the probe's
226
- * 2xx, and never while a surface has opted out (REFRESH_OFF).
350
+ * yes, and never while a surface has opted out (REFRESH_OFF).
227
351
  *
228
352
  * The rules are asked again after the probe: typing may have started, or
229
353
  * the tab changed, while it was on its way.