@voltro/cli 0.51.0 → 0.52.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.
- package/CHANGELOG.md +127 -0
- package/dist/{agentsMd-D6yD7IQv.js → agentsMd-0l980yhL.js} +4 -1
- package/dist/agentsMd-SDDSkyl4.js +2 -0
- package/dist/apiBuild-BYBpL7Pz.js +2 -0
- package/dist/{apiBuild-CPDHXF72.js → apiBuild-CSFI8QGq.js} +3 -3
- package/dist/bin.js +1 -1
- package/dist/build-CPgcMQug.js +793 -0
- package/dist/checkCommand-2SbqzukH.js +2 -0
- package/dist/{checkCommand-DNuPiWMc.js → checkCommand-COmqc2cB.js} +92 -46
- package/dist/{cloudCmd-F4YJeqM3.js → cloudCmd-QUXh-b5w.js} +1 -1
- package/dist/codegen-CctkDO-1.js +2 -0
- package/dist/{codegen-CrMXs4hb.js → codegen-VF479Cnb.js} +1 -1
- package/dist/{codegenCommand-BNBHcNNj.js → codegenCommand-DCdG2JN-.js} +12 -12
- package/dist/{codemodRunner-BDVixlSw.js → codemodRunner-r7J9lIa7.js} +588 -540
- package/dist/{commands-B1OiS9bX.js → commands-Cc_nV8WI.js} +35 -35
- package/dist/{dashboardCommand-C-vvPY1B.js → dashboardCommand-C-gKvwqh.js} +5 -5
- package/dist/{dataCommand-C1GxXW5q.js → dataCommand-BgpBHnlB.js} +27 -27
- package/dist/dbCommand-DNb6yeOG.js +2 -0
- package/dist/{dbCommand-If4Y1xQ-.js → dbCommand-sHedr-NJ.js} +2 -2
- package/dist/dev--A3nsxA3.js +3 -0
- package/dist/{dev-kdAg9Q7l.js → dev-CRHoCEiy.js} +2142 -2103
- package/dist/doctorCommand-CqoWA2p5.js +2 -0
- package/dist/{doctorCommand-nKmeW78u.js → doctorCommand-DtfJ3FA6.js} +314 -234
- package/dist/{dormancyCommand-CY3wa_SW.js → dormancyCommand-Drn7o0No.js} +1 -1
- package/dist/{embeddingsCommand-BDLIgje_.js → embeddingsCommand-Z-jO1fWN.js} +1 -1
- package/dist/{envCommand-C6V_xVlT.js → envCommand-D4gCrrTZ.js} +8 -8
- package/dist/{evolveCommand-D3c4DSfN.js → evolveCommand-CMROeKeA.js} +2 -2
- package/dist/fileConventions-DOqD3lPS.js +34 -0
- package/dist/{fileTaxonomy-CJfgOllU.js → fileTaxonomy-DvDUV9wq.js} +1 -1
- package/dist/frameworkTableAssembly-C6ETawPR.js +2 -0
- package/dist/{frameworkTableAssembly-BwJVEKLr.js → frameworkTableAssembly-w-XnLa3q.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{infoCommand-BnRFEF1o.js → infoCommand-DXM868o_.js} +1 -1
- package/dist/inspectMetrics-CGF94puw.js +143 -0
- package/dist/{metaCommands-CfRLra0s.js → metaCommands-C6RFmF1r.js} +2 -2
- package/dist/{migrate-DehuBakM.js → migrate-D0F-eTlK.js} +2 -2
- package/dist/{pageConvention-cEiRxdab.js → pageConvention-CzUiSbtU.js} +1 -1
- package/dist/{privacyCommand-XejDMvmu.js → privacyCommand-DGdopOI6.js} +1 -1
- package/dist/{projectScaffold-mIX_DpSe.js → projectScaffold-B4dmTlwT.js} +1 -1
- package/dist/{projectScaffold-BIl97_E6.js → projectScaffold-EzlErR4E.js} +1 -1
- package/dist/{renderModeScan-D7J1B7Kw.js → renderModeScan-CcH2X1_D.js} +25 -10
- package/dist/{renderProfile-1OWWAAtx.js → renderProfile-Ck32Fzxr.js} +2 -2
- package/dist/{runtimeTrace-ZsBU7Tkx.js → runtimeTrace-BPQyCmC5.js} +1 -1
- package/dist/{sdkgen-O4XqWOjM.js → sdkgen-Se88ifTd.js} +1 -1
- package/dist/serveCommand-DLc-BznW.js +2 -0
- package/dist/{serveCommand-DdiYNBBu.js → serveCommand-DkP3OT0W.js} +885 -868
- package/dist/serveEntry.js +1 -1
- package/dist/start-DfL3fOiN.js +3 -0
- package/dist/start-jw89Xbqy.js +1339 -0
- package/dist/startEntry.js +1 -1
- package/dist/{staticCommand-Dr2M6tpU.js → staticCommand-BwNEDlSU.js} +1 -1
- package/dist/{test-rXFq4S76.js → test-f3amja6a.js} +1 -1
- package/dist/updateCommand-5gFVfK5q.js +2 -0
- package/dist/{updateCommand-Bs322Q78.js → updateCommand-BMk2e4ky.js} +1 -1
- package/dist/{webDev-B-ubQEMX.js → webDev-BgWL9gKV.js} +1156 -835
- package/dist/webDev-CZbTsDcH.js +2 -0
- package/dist/{webhooksCommand-FLYY9IXh.js → webhooksCommand-CoIO3jbj.js} +1 -1
- package/package.json +30 -17
- package/templates/AGENTS.core.md +11 -0
- package/templates/AGENTS.md +15 -4
- package/templates/agent-docs/_index.md +4 -4
- package/templates/agent-docs/_manifest.json +11 -11
- package/templates/agent-docs/cli.md +96 -14
- package/templates/agent-docs/data.md +210 -7
- package/templates/agent-docs/database/schema.md +1 -1
- package/templates/agent-docs/database/seedsdialects.md +1 -1
- package/templates/agent-docs/deployment.md +22 -3
- package/templates/agent-docs/introduction.md +46 -0
- package/templates/agent-docs/local-first-mobile.md +34 -7
- package/templates/agent-docs/plugins/ai-flows.md +1 -1
- package/templates/agent-docs/plugins/audit.md +5 -5
- package/templates/agent-docs/plugins/cdc-out.md +2 -2
- package/templates/agent-docs/plugins/{versioning.md → row-history.md} +22 -22
- package/templates/agent-docs/plugins/storage.md +2 -2
- package/templates/agent-docs/plugins.md +29 -7
- package/templates/agent-docs/reference.md +39 -2
- package/templates/agent-docs/routing.md +341 -47
- package/templates/agent-docs/schema-driven-ui.md +78 -2
- package/templates/agent-docs/security.md +125 -8
- package/templates/agent-docs/templates/apibackends.md +14 -14
- package/templates/agent-docs/templates/overview.md +1 -1
- package/templates/agent-docs/whats-new.md +76 -53
- package/templates/apps/api-ai/package.json +6 -7
- package/templates/apps/api-ai/tests/summarize.test.ts +1 -1
- package/templates/apps/api-auth/package.json +8 -8
- package/templates/apps/api-backend/package.json +7 -7
- package/templates/apps/api-backend-deactivation/package.json +7 -7
- package/templates/apps/api-backend-mail/package.json +8 -8
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-sqlite/package.json +8 -8
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-cms/package.json +9 -10
- package/templates/apps/api-collab/package.json +8 -8
- package/templates/apps/api-data-advanced/package.json +8 -8
- package/templates/apps/api-durable/package.json +8 -8
- package/templates/apps/api-feature-flags/package.json +9 -9
- package/templates/apps/api-governance/package.json +8 -8
- package/templates/apps/api-kv/package.json +8 -8
- package/templates/apps/api-moderation/package.json +8 -8
- package/templates/apps/api-observability/package.json +8 -8
- package/templates/apps/api-ratelimit/package.json +8 -8
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/{api-versioning → api-row-history}/README.md +3 -3
- package/templates/apps/{api-versioning → api-row-history}/actions/documents.asOf.action.server.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/actions/documents.history.action.server.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/app.config.ts +3 -3
- package/templates/apps/{api-versioning → api-row-history}/database/schema.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/mutations/documents.create.mutation.server.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/mutations/documents.create.mutation.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/mutations/documents.update.mutation.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/package.json +8 -8
- package/templates/apps/api-row-history/template.json +6 -0
- package/templates/apps/{api-versioning → api-row-history}/tests/documents.create.test.ts +1 -1
- package/templates/apps/api-saas/app.config.ts +1 -0
- package/templates/apps/api-saas/package.json +10 -11
- package/templates/apps/api-saas-starter/package.json +10 -10
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-status/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +6 -6
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-app/package.json +9 -9
- package/templates/apps/frontend-auth/package.json +8 -8
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-cms/package.json +9 -9
- package/templates/apps/frontend-collab/package.json +10 -10
- package/templates/apps/frontend-collab/src/pages/page.test.tsx +10 -9
- package/templates/apps/frontend-collab/src/pages/page.tsx +42 -65
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-docs/package.json +6 -7
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +6 -7
- package/templates/apps/frontend-portal/package.json +8 -8
- package/templates/apps/frontend-saas/package.json +8 -8
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-static-blog/package.json +6 -6
- package/templates/apps/frontend-status/package.json +8 -8
- package/templates/apps/mobile-app/package.json +4 -4
- package/dist/agentsMd-Bu_XQgVf.js +0 -2
- package/dist/apiBuild-GDKuGOMV.js +0 -2
- package/dist/build-DETLZAFt.js +0 -752
- package/dist/checkCommand-CWcnDArJ.js +0 -2
- package/dist/codegen-DiMn2KkZ.js +0 -2
- package/dist/dbCommand-C27HIsGE.js +0 -2
- package/dist/dev-CK522MV5.js +0 -3
- package/dist/doctorCommand-BK4l18eG.js +0 -2
- package/dist/fileConventions-Cof68_BL.js +0 -33
- package/dist/frameworkTableAssembly-CVDB2hCq.js +0 -2
- package/dist/inspectMetrics-CfdKLh6t.js +0 -72
- package/dist/serveCommand-BRnPCxVd.js +0 -2
- package/dist/start-BLNmWkLa.js +0 -1154
- package/dist/start-Dzicuyw8.js +0 -3
- package/dist/updateCommand-eXB35SEv.js +0 -2
- package/dist/webDev-DposiF3j.js +0 -2
- package/templates/apps/api-versioning/template.json +0 -6
- /package/templates/apps/{api-versioning → api-row-history}/actions/documents.asOf.action.ts +0 -0
- /package/templates/apps/{api-versioning → api-row-history}/actions/documents.history.action.ts +0 -0
- /package/templates/apps/{api-versioning → api-row-history}/mutations/documents.update.mutation.server.ts +0 -0
- /package/templates/apps/{api-versioning → api-row-history}/tsconfig.json +0 -0
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@{{projectName}}/api": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/client": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/i18n": "0.
|
|
20
|
-
"@voltro/ui-shadcn": "0.
|
|
21
|
-
"@voltro/web": "0.
|
|
15
|
+
"@{{projectName}}/api": "0.52.0",
|
|
16
|
+
"@voltro/cli": "0.52.0",
|
|
17
|
+
"@voltro/client": "0.52.0",
|
|
18
|
+
"@voltro/env": "0.52.0",
|
|
19
|
+
"@voltro/i18n": "0.52.0",
|
|
20
|
+
"@voltro/ui-shadcn": "0.52.0",
|
|
21
|
+
"@voltro/web": "0.52.0",
|
|
22
22
|
"react": "^19.0.0",
|
|
23
23
|
"react-dom": "^19.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/devtools": "0.
|
|
26
|
+
"@voltro/devtools": "0.52.0",
|
|
27
27
|
"@types/react": "^19.0.0",
|
|
28
28
|
"@types/react-dom": "^19.0.0",
|
|
29
29
|
"jsdom": "^30.0.0",
|
|
@@ -12,19 +12,19 @@
|
|
|
12
12
|
"typecheck": "tsc --noEmit"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@{{projectName}}/api": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/client": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/i18n": "0.
|
|
20
|
-
"@voltro/ui-shadcn": "0.
|
|
21
|
-
"@voltro/web": "0.
|
|
15
|
+
"@{{projectName}}/api": "0.52.0",
|
|
16
|
+
"@voltro/cli": "0.52.0",
|
|
17
|
+
"@voltro/client": "0.52.0",
|
|
18
|
+
"@voltro/env": "0.52.0",
|
|
19
|
+
"@voltro/i18n": "0.52.0",
|
|
20
|
+
"@voltro/ui-shadcn": "0.52.0",
|
|
21
|
+
"@voltro/web": "0.52.0",
|
|
22
22
|
"react": "^19.0.0",
|
|
23
23
|
"react-dom": "^19.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/devtools": "0.
|
|
27
|
-
"@voltro/testing": "0.
|
|
26
|
+
"@voltro/devtools": "0.52.0",
|
|
27
|
+
"@voltro/testing": "0.52.0",
|
|
28
28
|
"@types/react": "^19.0.0",
|
|
29
29
|
"@types/react-dom": "^19.0.0",
|
|
30
30
|
"jsdom": "^30.0.0",
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@{{projectName}}/api": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/client": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/i18n": "0.
|
|
20
|
-
"@voltro/ui-shadcn": "0.
|
|
21
|
-
"@voltro/web": "0.
|
|
15
|
+
"@{{projectName}}/api": "0.52.0",
|
|
16
|
+
"@voltro/cli": "0.52.0",
|
|
17
|
+
"@voltro/client": "0.52.0",
|
|
18
|
+
"@voltro/env": "0.52.0",
|
|
19
|
+
"@voltro/i18n": "0.52.0",
|
|
20
|
+
"@voltro/ui-shadcn": "0.52.0",
|
|
21
|
+
"@voltro/web": "0.52.0",
|
|
22
22
|
"react": "^19.0.0",
|
|
23
23
|
"react-dom": "^19.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/devtools": "0.
|
|
26
|
+
"@voltro/devtools": "0.52.0",
|
|
27
27
|
"@types/react": "^19.0.0",
|
|
28
28
|
"@types/react-dom": "^19.0.0",
|
|
29
29
|
"jsdom": "^30.0.0",
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
"typecheck": "tsc --noEmit"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/client": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/i18n": "0.
|
|
19
|
-
"@voltro/ui-shadcn": "0.
|
|
20
|
-
"@voltro/web": "0.
|
|
15
|
+
"@voltro/cli": "0.52.0",
|
|
16
|
+
"@voltro/client": "0.52.0",
|
|
17
|
+
"@voltro/env": "0.52.0",
|
|
18
|
+
"@voltro/i18n": "0.52.0",
|
|
19
|
+
"@voltro/ui-shadcn": "0.52.0",
|
|
20
|
+
"@voltro/web": "0.52.0",
|
|
21
21
|
"react": "^19.0.0",
|
|
22
22
|
"react-dom": "^19.0.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@voltro/devtools": "0.
|
|
25
|
+
"@voltro/devtools": "0.52.0",
|
|
26
26
|
"@types/react": "^19.0.0",
|
|
27
27
|
"@types/react-dom": "^19.0.0",
|
|
28
28
|
"jsdom": "^30.0.0",
|
|
@@ -12,19 +12,19 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@{{projectName}}/api": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/client": "0.
|
|
18
|
-
"@voltro/cms": "0.
|
|
19
|
-
"@voltro/env": "0.
|
|
20
|
-
"@voltro/i18n": "0.
|
|
21
|
-
"@voltro/ui-shadcn": "0.
|
|
22
|
-
"@voltro/web": "0.
|
|
15
|
+
"@{{projectName}}/api": "0.52.0",
|
|
16
|
+
"@voltro/cli": "0.52.0",
|
|
17
|
+
"@voltro/client": "0.52.0",
|
|
18
|
+
"@voltro/cms": "0.52.0",
|
|
19
|
+
"@voltro/env": "0.52.0",
|
|
20
|
+
"@voltro/i18n": "0.52.0",
|
|
21
|
+
"@voltro/ui-shadcn": "0.52.0",
|
|
22
|
+
"@voltro/web": "0.52.0",
|
|
23
23
|
"react": "^19.0.0",
|
|
24
24
|
"react-dom": "^19.0.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@voltro/devtools": "0.
|
|
27
|
+
"@voltro/devtools": "0.52.0",
|
|
28
28
|
"@types/react": "^19.0.0",
|
|
29
29
|
"@types/react-dom": "^19.0.0",
|
|
30
30
|
"jsdom": "^30.0.0",
|
|
@@ -12,20 +12,20 @@
|
|
|
12
12
|
"typecheck": "tsc --noEmit"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@{{projectName}}/api": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/client": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/i18n": "0.
|
|
20
|
-
"@voltro/local-first": "0.
|
|
21
|
-
"@voltro/ui-shadcn": "0.
|
|
22
|
-
"@voltro/web": "0.
|
|
15
|
+
"@{{projectName}}/api": "0.52.0",
|
|
16
|
+
"@voltro/cli": "0.52.0",
|
|
17
|
+
"@voltro/client": "0.52.0",
|
|
18
|
+
"@voltro/env": "0.52.0",
|
|
19
|
+
"@voltro/i18n": "0.52.0",
|
|
20
|
+
"@voltro/local-first": "0.52.0",
|
|
21
|
+
"@voltro/ui-shadcn": "0.52.0",
|
|
22
|
+
"@voltro/web": "0.52.0",
|
|
23
23
|
"react": "^19.0.0",
|
|
24
24
|
"react-dom": "^19.0.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@voltro/devtools": "0.
|
|
28
|
-
"@voltro/testing": "0.
|
|
27
|
+
"@voltro/devtools": "0.52.0",
|
|
28
|
+
"@voltro/testing": "0.52.0",
|
|
29
29
|
"@types/react": "^19.0.0",
|
|
30
30
|
"@types/react-dom": "^19.0.0",
|
|
31
31
|
"jsdom": "^30.0.0",
|
|
@@ -3,21 +3,22 @@
|
|
|
3
3
|
// `/` — the collaborative editor, driven through `makeVoltroTestClient`
|
|
4
4
|
// (`@voltro/testing/client`) rather than a hand-rolled `vi.mock('@voltro/client')`.
|
|
5
5
|
//
|
|
6
|
-
// WHY THE HARNESS HERE and not a mock. This page is stateful:
|
|
7
|
-
//
|
|
8
|
-
// back in. A `vi.mock` can return one snapshot per
|
|
9
|
-
// the whole point of the page — was untestable, and the
|
|
10
|
-
// this one never pushed a second value into a mounted
|
|
11
|
-
// is a real server delta, so the last case below
|
|
6
|
+
// WHY THE HARNESS HERE and not a mock. This page is stateful: `useCrdtText`
|
|
7
|
+
// owns a sync client per document cell, seeded from the server body and
|
|
8
|
+
// folding every INCOMING body back in. A `vi.mock` can return one snapshot per
|
|
9
|
+
// mount, so the fold — the whole point of the page — was untestable, and the
|
|
10
|
+
// suite that replaced this one never pushed a second value into a mounted
|
|
11
|
+
// tree. `setSubscription` is a real server delta, so the last case below
|
|
12
|
+
// asserts convergence.
|
|
12
13
|
//
|
|
13
14
|
// Two other things fall out of it: the real `@voltro/client` hooks run (so a
|
|
14
15
|
// change in their loading/error semantics is caught here instead of being
|
|
15
16
|
// re-implemented by the mock), and the page can be imported statically —
|
|
16
17
|
// `await import('./page')` only existed to sequence around `vi.mock`'s hoist.
|
|
17
18
|
//
|
|
18
|
-
// `@voltro/local-first` is NOT faked either: the
|
|
19
|
-
// decodes the seeded body and turns keystrokes into encoded updates
|
|
20
|
-
// the behaviour worth asserting.
|
|
19
|
+
// `@voltro/local-first` is NOT faked either: the real `useCrdtText` hook
|
|
20
|
+
// decodes the seeded body and turns keystrokes into encoded updates (via its
|
|
21
|
+
// `crdtTextEdit` span diff), which is the behaviour worth asserting.
|
|
21
22
|
|
|
22
23
|
import { afterEach, beforeEach, describe, expect, test } from 'vitest'
|
|
23
24
|
import { act, createElement, type ReactNode } from 'react'
|
|
@@ -2,26 +2,30 @@
|
|
|
2
2
|
// by the framework's file-convention page discovery.
|
|
3
3
|
//
|
|
4
4
|
// THIS is local-first collaboration over the reactive loop. The api's
|
|
5
|
-
// `documents` table has a `crdtText()` `body`; this page
|
|
5
|
+
// `documents` table has a `crdtText()` `body`; this page binds ONE
|
|
6
|
+
// `useCrdtText` cell (`@voltro/local-first/react`) to it:
|
|
6
7
|
// 1. `useSubscription('app','documents.list')` — streams the shared document
|
|
7
|
-
// (with its MERGED body) live over WebSocket
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
8
|
+
// (with its MERGED body) live over WebSocket; the streamed body is the
|
|
9
|
+
// hook's `remote`, folded into the local CRDT on every change,
|
|
10
|
+
// 2. `useMutation('app','documents.setBody')` is the hook's `push` — every
|
|
11
|
+
// keystroke goes through `setText`, which diffs to a minimal single-span
|
|
12
|
+
// edit (the framework's `crdtTextEdit`) and queues the encoded update.
|
|
12
13
|
//
|
|
13
14
|
// The convergence is authoritative and SERVER-SIDE: the api merges each update
|
|
14
15
|
// into the stored state before broadcasting, so two tabs typing at once both
|
|
15
16
|
// survive — no last-write-wins loser. Open this page in two tabs to see it.
|
|
16
17
|
//
|
|
17
|
-
// This is the app-level binding the docs describe
|
|
18
|
-
// `push` = the setBody mutation, `
|
|
18
|
+
// This is the app-level binding the local-first docs describe — the two names
|
|
19
|
+
// nothing can derive: `push` = the setBody mutation, `remote` = the documents
|
|
20
|
+
// subscription. Everything else (sync client lifecycle, offline queue, edit
|
|
21
|
+
// encoding) lives in the hook.
|
|
19
22
|
|
|
20
23
|
import { useMutation, useSubscription } from '@voltro/client'
|
|
21
|
-
import {
|
|
24
|
+
import { useMemo, type ReactNode } from 'react'
|
|
22
25
|
import type { PageMeta } from '@voltro/web'
|
|
23
26
|
import { T, useTFn } from '@voltro/i18n'
|
|
24
|
-
import {
|
|
27
|
+
import type { CrdtState } from '@voltro/local-first'
|
|
28
|
+
import { useCrdtText } from '@voltro/local-first/react'
|
|
25
29
|
import { getCatalog } from '../locales'
|
|
26
30
|
|
|
27
31
|
export const meta = ({ locale }: { readonly locale: string }): PageMeta => ({
|
|
@@ -47,33 +51,8 @@ interface DocRow {
|
|
|
47
51
|
const base64ToBytes = (b64: string): Uint8Array =>
|
|
48
52
|
Uint8Array.from(atob(b64), (ch) => ch.charCodeAt(0))
|
|
49
53
|
|
|
50
|
-
// A null/absent body decodes to the empty document — the CRDT merge identity.
|
|
51
|
-
const toState = (body: Uint8Array | string | null): CrdtState =>
|
|
52
|
-
body instanceof Uint8Array ? body : typeof body === 'string' ? base64ToBytes(body) : emptyCrdtState()
|
|
53
|
-
|
|
54
|
-
// Turn a whole-string edit into a single contiguous CRDT insert/delete: the
|
|
55
|
-
// common prefix + suffix are unchanged; the middle is what the user replaced.
|
|
56
|
-
// Covers ordinary typing, deletion, and paste (one contiguous run).
|
|
57
|
-
const applyDiff = (handle: CrdtText, prev: string, next: string): void => {
|
|
58
|
-
if (prev === next) return
|
|
59
|
-
let start = 0
|
|
60
|
-
const shorter = Math.min(prev.length, next.length)
|
|
61
|
-
while (start < shorter && prev[start] === next[start]) start += 1
|
|
62
|
-
let end = 0
|
|
63
|
-
while (
|
|
64
|
-
end < shorter - start &&
|
|
65
|
-
prev[prev.length - 1 - end] === next[next.length - 1 - end]
|
|
66
|
-
) {
|
|
67
|
-
end += 1
|
|
68
|
-
}
|
|
69
|
-
const deleteLen = prev.length - start - end
|
|
70
|
-
const inserted = next.slice(start, next.length - end)
|
|
71
|
-
if (deleteLen > 0) handle.delete(start, deleteLen)
|
|
72
|
-
if (inserted.length > 0) handle.insert(start, inserted)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
54
|
// One editor bound to ONE document. Remounted (via `key={doc.id}`) when the
|
|
76
|
-
// bound document changes, so the
|
|
55
|
+
// bound document changes, so the hook's sync-client lifecycle is scoped to it.
|
|
77
56
|
const DocumentEditor = ({
|
|
78
57
|
doc,
|
|
79
58
|
saving,
|
|
@@ -81,30 +60,30 @@ const DocumentEditor = ({
|
|
|
81
60
|
}: {
|
|
82
61
|
readonly doc: DocRow
|
|
83
62
|
readonly saving: boolean
|
|
84
|
-
readonly onUpdate: (update: CrdtState) =>
|
|
63
|
+
readonly onUpdate: (update: CrdtState) => Promise<unknown>
|
|
85
64
|
}): ReactNode => {
|
|
86
65
|
const t = useTFn()
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
66
|
+
// The authoritative state as the subscription streams it. `null` while the
|
|
67
|
+
// body is absent — useCrdtText folds nothing then, NOT an empty document.
|
|
68
|
+
const remote = useMemo(
|
|
69
|
+
() =>
|
|
70
|
+
doc.body instanceof Uint8Array
|
|
71
|
+
? doc.body
|
|
72
|
+
: typeof doc.body === 'string'
|
|
73
|
+
? base64ToBytes(doc.body)
|
|
74
|
+
: null,
|
|
75
|
+
[doc.body],
|
|
76
|
+
)
|
|
77
|
+
const body = useCrdtText({
|
|
78
|
+
cell: { table: 'documents', id: doc.id, column: 'body' },
|
|
79
|
+
remote,
|
|
80
|
+
// A rejected push re-drives the queued write; log it and keep the rejection.
|
|
81
|
+
push: (write) =>
|
|
82
|
+
onUpdate(write.update).catch((mutationError: unknown) => {
|
|
83
|
+
console.error('documents.setBody failed:', mutationError)
|
|
84
|
+
throw mutationError
|
|
85
|
+
}),
|
|
86
|
+
})
|
|
108
87
|
|
|
109
88
|
return (
|
|
110
89
|
<section>
|
|
@@ -113,8 +92,8 @@ const DocumentEditor = ({
|
|
|
113
92
|
{saving ? <T id="editor.status.saving" /> : null}
|
|
114
93
|
</p>
|
|
115
94
|
<textarea
|
|
116
|
-
value={text}
|
|
117
|
-
onChange={(event) =>
|
|
95
|
+
value={body.text}
|
|
96
|
+
onChange={(event) => body.setText(event.target.value)}
|
|
118
97
|
placeholder={t('editor.placeholder')}
|
|
119
98
|
rows={12}
|
|
120
99
|
style={{ width: '100%', padding: '10px 12px', fontSize: 14, fontFamily: 'ui-monospace, monospace', resize: 'vertical', boxSizing: 'border-box' }}
|
|
@@ -140,11 +119,9 @@ const IndexPage = (): ReactNode => {
|
|
|
140
119
|
}
|
|
141
120
|
}
|
|
142
121
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
})
|
|
147
|
-
}
|
|
122
|
+
// Returned promise = the hook's ack; a rejection triggers its bounded retry.
|
|
123
|
+
const handleUpdate = (id: string) => (update: CrdtState): Promise<unknown> =>
|
|
124
|
+
setBody.mutate({ id, update })
|
|
148
125
|
|
|
149
126
|
return (
|
|
150
127
|
<div>
|
|
@@ -15,18 +15,18 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@effect/platform": "^0.97.0",
|
|
18
|
-
"@voltro/cli": "0.
|
|
19
|
-
"@voltro/client": "0.
|
|
20
|
-
"@voltro/env": "0.
|
|
21
|
-
"@voltro/i18n": "0.
|
|
22
|
-
"@voltro/serverless": "0.
|
|
23
|
-
"@voltro/web": "0.
|
|
18
|
+
"@voltro/cli": "0.52.0",
|
|
19
|
+
"@voltro/client": "0.52.0",
|
|
20
|
+
"@voltro/env": "0.52.0",
|
|
21
|
+
"@voltro/i18n": "0.52.0",
|
|
22
|
+
"@voltro/serverless": "0.52.0",
|
|
23
|
+
"@voltro/web": "0.52.0",
|
|
24
24
|
"effect": "^3.22.0",
|
|
25
25
|
"react": "^19.0.0",
|
|
26
26
|
"react-dom": "^19.0.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@voltro/devtools": "0.
|
|
29
|
+
"@voltro/devtools": "0.52.0",
|
|
30
30
|
"@types/react": "^19.0.0",
|
|
31
31
|
"@types/react-dom": "^19.0.0",
|
|
32
32
|
"jsdom": "^30.0.0",
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/client": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/i18n": "0.
|
|
19
|
-
"@voltro/ui-shadcn": "0.
|
|
20
|
-
"@voltro/web": "0.
|
|
15
|
+
"@voltro/cli": "0.52.0",
|
|
16
|
+
"@voltro/client": "0.52.0",
|
|
17
|
+
"@voltro/env": "0.52.0",
|
|
18
|
+
"@voltro/i18n": "0.52.0",
|
|
19
|
+
"@voltro/ui-shadcn": "0.52.0",
|
|
20
|
+
"@voltro/web": "0.52.0",
|
|
21
21
|
"react": "^19.0.0",
|
|
22
22
|
"react-dom": "^19.0.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@voltro/devtools": "0.
|
|
25
|
+
"@voltro/devtools": "0.52.0",
|
|
26
26
|
"@types/react": "^19.0.0",
|
|
27
27
|
"@types/react-dom": "^19.0.0",
|
|
28
28
|
"jsdom": "^30.0.0",
|
|
@@ -12,17 +12,16 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/client": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/i18n": "0.
|
|
19
|
-
"@voltro/
|
|
20
|
-
"@voltro/web": "0.51.0",
|
|
15
|
+
"@voltro/cli": "0.52.0",
|
|
16
|
+
"@voltro/client": "0.52.0",
|
|
17
|
+
"@voltro/env": "0.52.0",
|
|
18
|
+
"@voltro/i18n": "0.52.0",
|
|
19
|
+
"@voltro/web": "0.52.0",
|
|
21
20
|
"react": "^19.0.0",
|
|
22
21
|
"react-dom": "^19.0.0"
|
|
23
22
|
},
|
|
24
23
|
"devDependencies": {
|
|
25
|
-
"@voltro/devtools": "0.
|
|
24
|
+
"@voltro/devtools": "0.52.0",
|
|
26
25
|
"@types/react": "^19.0.0",
|
|
27
26
|
"@types/react-dom": "^19.0.0",
|
|
28
27
|
"jsdom": "^30.0.0",
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/client": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/i18n": "0.
|
|
19
|
-
"@voltro/web": "0.
|
|
15
|
+
"@voltro/cli": "0.52.0",
|
|
16
|
+
"@voltro/client": "0.52.0",
|
|
17
|
+
"@voltro/env": "0.52.0",
|
|
18
|
+
"@voltro/i18n": "0.52.0",
|
|
19
|
+
"@voltro/web": "0.52.0",
|
|
20
20
|
"react": "^19.0.0",
|
|
21
21
|
"react-dom": "^19.0.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/devtools": "0.
|
|
24
|
+
"@voltro/devtools": "0.52.0",
|
|
25
25
|
"@types/react": "^19.0.0",
|
|
26
26
|
"@types/react-dom": "^19.0.0",
|
|
27
27
|
"jsdom": "^30.0.0",
|
|
@@ -12,17 +12,16 @@
|
|
|
12
12
|
"typecheck": "tsc --noEmit"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/client": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/i18n": "0.
|
|
19
|
-
"@voltro/
|
|
20
|
-
"@voltro/web": "0.51.0",
|
|
15
|
+
"@voltro/cli": "0.52.0",
|
|
16
|
+
"@voltro/client": "0.52.0",
|
|
17
|
+
"@voltro/env": "0.52.0",
|
|
18
|
+
"@voltro/i18n": "0.52.0",
|
|
19
|
+
"@voltro/web": "0.52.0",
|
|
21
20
|
"react": "^19.0.0",
|
|
22
21
|
"react-dom": "^19.0.0"
|
|
23
22
|
},
|
|
24
23
|
"devDependencies": {
|
|
25
|
-
"@voltro/devtools": "0.
|
|
24
|
+
"@voltro/devtools": "0.52.0",
|
|
26
25
|
"@types/react": "^19.0.0",
|
|
27
26
|
"@types/react-dom": "^19.0.0",
|
|
28
27
|
"jsdom": "^30.0.0",
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@{{projectName}}/api": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/client": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/i18n": "0.
|
|
20
|
-
"@voltro/ui-shadcn": "0.
|
|
21
|
-
"@voltro/web": "0.
|
|
15
|
+
"@{{projectName}}/api": "0.52.0",
|
|
16
|
+
"@voltro/cli": "0.52.0",
|
|
17
|
+
"@voltro/client": "0.52.0",
|
|
18
|
+
"@voltro/env": "0.52.0",
|
|
19
|
+
"@voltro/i18n": "0.52.0",
|
|
20
|
+
"@voltro/ui-shadcn": "0.52.0",
|
|
21
|
+
"@voltro/web": "0.52.0",
|
|
22
22
|
"react": "^19.0.0",
|
|
23
23
|
"react-dom": "^19.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/devtools": "0.
|
|
26
|
+
"@voltro/devtools": "0.52.0",
|
|
27
27
|
"@types/react": "^19.0.0",
|
|
28
28
|
"@types/react-dom": "^19.0.0",
|
|
29
29
|
"jsdom": "^30.0.0",
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@{{projectName}}/api": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/client": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/i18n": "0.
|
|
20
|
-
"@voltro/ui-shadcn": "0.
|
|
21
|
-
"@voltro/web": "0.
|
|
15
|
+
"@{{projectName}}/api": "0.52.0",
|
|
16
|
+
"@voltro/cli": "0.52.0",
|
|
17
|
+
"@voltro/client": "0.52.0",
|
|
18
|
+
"@voltro/env": "0.52.0",
|
|
19
|
+
"@voltro/i18n": "0.52.0",
|
|
20
|
+
"@voltro/ui-shadcn": "0.52.0",
|
|
21
|
+
"@voltro/web": "0.52.0",
|
|
22
22
|
"react": "^19.0.0",
|
|
23
23
|
"react-dom": "^19.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/devtools": "0.
|
|
26
|
+
"@voltro/devtools": "0.52.0",
|
|
27
27
|
"@types/react": "^19.0.0",
|
|
28
28
|
"@types/react-dom": "^19.0.0",
|
|
29
29
|
"jsdom": "^30.0.0",
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
"typecheck": "tsc --noEmit"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/client": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/i18n": "0.
|
|
19
|
-
"@voltro/ui-shadcn": "0.
|
|
20
|
-
"@voltro/web": "0.
|
|
15
|
+
"@voltro/cli": "0.52.0",
|
|
16
|
+
"@voltro/client": "0.52.0",
|
|
17
|
+
"@voltro/env": "0.52.0",
|
|
18
|
+
"@voltro/i18n": "0.52.0",
|
|
19
|
+
"@voltro/ui-shadcn": "0.52.0",
|
|
20
|
+
"@voltro/web": "0.52.0",
|
|
21
21
|
"react": "^19.0.0",
|
|
22
22
|
"react-dom": "^19.0.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@voltro/devtools": "0.
|
|
25
|
+
"@voltro/devtools": "0.52.0",
|
|
26
26
|
"@types/react": "^19.0.0",
|
|
27
27
|
"@types/react-dom": "^19.0.0",
|
|
28
28
|
"jsdom": "^30.0.0",
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/client": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/i18n": "0.
|
|
19
|
-
"@voltro/ui-shadcn": "0.
|
|
20
|
-
"@voltro/web": "0.
|
|
15
|
+
"@voltro/cli": "0.52.0",
|
|
16
|
+
"@voltro/client": "0.52.0",
|
|
17
|
+
"@voltro/env": "0.52.0",
|
|
18
|
+
"@voltro/i18n": "0.52.0",
|
|
19
|
+
"@voltro/ui-shadcn": "0.52.0",
|
|
20
|
+
"@voltro/web": "0.52.0",
|
|
21
21
|
"react": "^19.0.0",
|
|
22
22
|
"react-dom": "^19.0.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@voltro/devtools": "0.
|
|
25
|
+
"@voltro/devtools": "0.52.0",
|
|
26
26
|
"@types/react": "^19.0.0",
|
|
27
27
|
"@types/react-dom": "^19.0.0",
|
|
28
28
|
"jsdom": "^30.0.0",
|