syncorejs 0.2.1 → 0.2.2

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 (135) hide show
  1. package/README.md +2 -1
  2. package/dist/_vendor/cli/app.d.mts.map +1 -1
  3. package/dist/_vendor/cli/app.mjs +323 -42
  4. package/dist/_vendor/cli/app.mjs.map +1 -1
  5. package/dist/_vendor/cli/context.mjs +27 -9
  6. package/dist/_vendor/cli/context.mjs.map +1 -1
  7. package/dist/_vendor/cli/doctor.mjs +513 -46
  8. package/dist/_vendor/cli/doctor.mjs.map +1 -1
  9. package/dist/_vendor/cli/messages.mjs +5 -4
  10. package/dist/_vendor/cli/messages.mjs.map +1 -1
  11. package/dist/_vendor/cli/project.mjs +110 -12
  12. package/dist/_vendor/cli/project.mjs.map +1 -1
  13. package/dist/_vendor/cli/render.mjs +57 -9
  14. package/dist/_vendor/cli/render.mjs.map +1 -1
  15. package/dist/_vendor/cli/targets.mjs +4 -3
  16. package/dist/_vendor/cli/targets.mjs.map +1 -1
  17. package/dist/_vendor/core/cli.d.mts +13 -3
  18. package/dist/_vendor/core/cli.d.mts.map +1 -1
  19. package/dist/_vendor/core/cli.mjs +242 -91
  20. package/dist/_vendor/core/cli.mjs.map +1 -1
  21. package/dist/_vendor/core/devtools-auth.mjs +60 -0
  22. package/dist/_vendor/core/devtools-auth.mjs.map +1 -0
  23. package/dist/_vendor/core/index.d.mts +5 -3
  24. package/dist/_vendor/core/index.mjs +22 -2
  25. package/dist/_vendor/core/index.mjs.map +1 -1
  26. package/dist/_vendor/core/runtime/components.d.mts +111 -0
  27. package/dist/_vendor/core/runtime/components.d.mts.map +1 -0
  28. package/dist/_vendor/core/runtime/components.mjs +186 -0
  29. package/dist/_vendor/core/runtime/components.mjs.map +1 -0
  30. package/dist/_vendor/core/runtime/devtools.d.mts +4 -4
  31. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  32. package/dist/_vendor/core/runtime/devtools.mjs +52 -41
  33. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  34. package/dist/_vendor/core/runtime/functions.d.mts +10 -10
  35. package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
  36. package/dist/_vendor/core/runtime/functions.mjs +2 -2
  37. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  38. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +77 -0
  39. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -0
  40. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +617 -0
  41. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -0
  42. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +186 -0
  43. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -0
  44. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +220 -0
  45. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -0
  46. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +203 -0
  47. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -0
  48. package/dist/_vendor/core/runtime/internal/engines/shared.mjs +177 -0
  49. package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -0
  50. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +144 -0
  51. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -0
  52. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +220 -0
  53. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -0
  54. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs +32 -0
  55. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -0
  56. package/dist/_vendor/core/runtime/internal/systemMeta.mjs +61 -0
  57. package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -0
  58. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +37 -0
  59. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -0
  60. package/dist/_vendor/core/runtime/runtime.d.mts +159 -205
  61. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  62. package/dist/_vendor/core/runtime/runtime.mjs +16 -1371
  63. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  64. package/dist/_vendor/core/transport.d.mts +111 -0
  65. package/dist/_vendor/core/transport.d.mts.map +1 -0
  66. package/dist/_vendor/core/transport.mjs +419 -0
  67. package/dist/_vendor/core/transport.mjs.map +1 -0
  68. package/dist/_vendor/devtools-protocol/index.d.ts +39 -1
  69. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  70. package/dist/_vendor/devtools-protocol/index.js +25 -9
  71. package/dist/_vendor/devtools-protocol/index.js.map +1 -1
  72. package/dist/_vendor/next/index.d.ts +1 -1
  73. package/dist/_vendor/next/index.d.ts.map +1 -1
  74. package/dist/_vendor/next/index.js +31 -13
  75. package/dist/_vendor/next/index.js.map +1 -1
  76. package/dist/_vendor/platform-expo/index.d.ts +12 -12
  77. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  78. package/dist/_vendor/platform-expo/index.js +4 -2
  79. package/dist/_vendor/platform-expo/index.js.map +1 -1
  80. package/dist/_vendor/platform-expo/react.d.ts.map +1 -1
  81. package/dist/_vendor/platform-expo/react.js +11 -10
  82. package/dist/_vendor/platform-expo/react.js.map +1 -1
  83. package/dist/_vendor/platform-node/index.d.mts +23 -19
  84. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  85. package/dist/_vendor/platform-node/index.mjs +13 -5
  86. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  87. package/dist/_vendor/platform-node/ipc-react.d.mts.map +1 -1
  88. package/dist/_vendor/platform-node/ipc-react.mjs +15 -2
  89. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  90. package/dist/_vendor/platform-node/ipc.d.mts +11 -35
  91. package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
  92. package/dist/_vendor/platform-node/ipc.mjs +3 -273
  93. package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
  94. package/dist/_vendor/platform-web/external-change.d.ts +2 -1
  95. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
  96. package/dist/_vendor/platform-web/external-change.js +2 -1
  97. package/dist/_vendor/platform-web/external-change.js.map +1 -1
  98. package/dist/_vendor/platform-web/index.d.ts +21 -21
  99. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  100. package/dist/_vendor/platform-web/index.js +44 -7
  101. package/dist/_vendor/platform-web/index.js.map +1 -1
  102. package/dist/_vendor/platform-web/react.d.ts.map +1 -1
  103. package/dist/_vendor/platform-web/react.js +29 -13
  104. package/dist/_vendor/platform-web/react.js.map +1 -1
  105. package/dist/_vendor/platform-web/worker.d.ts +11 -35
  106. package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
  107. package/dist/_vendor/platform-web/worker.js +3 -267
  108. package/dist/_vendor/platform-web/worker.js.map +1 -1
  109. package/dist/_vendor/react/index.d.ts +36 -20
  110. package/dist/_vendor/react/index.d.ts.map +1 -1
  111. package/dist/_vendor/react/index.js +279 -57
  112. package/dist/_vendor/react/index.js.map +1 -1
  113. package/dist/_vendor/schema/definition.d.ts +48 -63
  114. package/dist/_vendor/schema/definition.d.ts.map +1 -1
  115. package/dist/_vendor/schema/definition.js +22 -39
  116. package/dist/_vendor/schema/definition.js.map +1 -1
  117. package/dist/_vendor/schema/index.d.ts +4 -4
  118. package/dist/_vendor/schema/index.js +2 -2
  119. package/dist/_vendor/schema/planner.d.ts +19 -2
  120. package/dist/_vendor/schema/planner.d.ts.map +1 -1
  121. package/dist/_vendor/schema/planner.js +79 -3
  122. package/dist/_vendor/schema/planner.js.map +1 -1
  123. package/dist/_vendor/schema/validators.d.ts +141 -121
  124. package/dist/_vendor/schema/validators.d.ts.map +1 -1
  125. package/dist/_vendor/schema/validators.js +300 -42
  126. package/dist/_vendor/schema/validators.js.map +1 -1
  127. package/dist/_vendor/svelte/index.d.ts +47 -19
  128. package/dist/_vendor/svelte/index.d.ts.map +1 -1
  129. package/dist/_vendor/svelte/index.js +250 -20
  130. package/dist/_vendor/svelte/index.js.map +1 -1
  131. package/dist/components.d.ts +2 -0
  132. package/dist/components.js +2 -0
  133. package/dist/index.d.ts +3 -2
  134. package/dist/index.js +2 -1
  135. package/package.json +8 -3
package/README.md CHANGED
@@ -18,7 +18,7 @@ npx syncorejs dev
18
18
  ## Imports
19
19
 
20
20
  ```ts
21
- import { defineSchema, defineTable, query, mutation, v } from "syncorejs";
21
+ import { defineSchema, defineTable, query, mutation, s } from "syncorejs";
22
22
  import { useQuery, useMutation } from "syncorejs/react";
23
23
  import { createBrowserWorkerClient } from "syncorejs/browser";
24
24
  import { SyncoreNextProvider } from "syncorejs/next";
@@ -28,3 +28,4 @@ import { SyncoreNextProvider } from "syncorejs/next";
28
28
 
29
29
  - Repository: https://github.com/JPSAUD501/Syncore
30
30
  - Quickstarts: https://github.com/JPSAUD501/Syncore/tree/main/docs/quickstarts
31
+
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.mts","names":[],"sources":["../src/app.ts"],"mappings":";;;iBAqIsB,aAAA,CAAc,IAAA,cAAsB,OAAA"}
1
+ {"version":3,"file":"app.d.mts","names":[],"sources":["../src/app.ts"],"mappings":";;;iBAgJsB,aAAA,CAAc,IAAA,cAAsB,OAAA"}
@@ -1,16 +1,17 @@
1
1
  import { CliContext, openTarget } from "./context.mjs";
2
2
  import { printCompactDevPhase, printDevSessionIntro, runShellCommand, withConsoleCapture } from "./dev-session.mjs";
3
- import { buildRuntimeLookup, connectToProjectHub, createManagedProjectClient, createPublicRuntimeId, exportProjectData, importProjectData, isKnownTemplate, listAvailableTargets, listConnectedClientTargets, listProjectTables, loadImportDocumentBatches, readProjectTable, resolveDashboardUrl, resolveDevtoolsUrl, resolveDocsTarget, resolveProjectFunction, targetSupportsCapability, writeExportData } from "./project.mjs";
4
- import { buildDevBootstrapNextSteps, buildHubUnavailableNextSteps, buildInitNextSteps, buildTargetCommandNextSteps, templateUsesConnectedClients } from "./messages.mjs";
5
- import { buildDoctorReport } from "./doctor.mjs";
3
+ import { buildRuntimeLookup, connectToProjectHub, createManagedProjectClient, createPublicRuntimeId, exportProjectData, importProjectData, isKnownTemplate, listAvailableTargets, listConnectedClientTargets, listProjectTables, loadImportDocumentBatches, readProjectTable, resolveActiveDashboardUrl, resolveDashboardUrl, resolveDevtoolsUrl, resolveDocsTarget, resolveProjectFunction, targetSupportsCapability, writeExportData } from "./project.mjs";
4
+ import { buildDevBootstrapNextSteps, buildHubUnavailableNextSteps, buildInitNextSteps, buildTargetCommandNextSteps, templateUsesConnectedClients as templateUsesConnectedClients$1 } from "./messages.mjs";
5
+ import { applyDoctorFixes, buildDoctorReport } from "./doctor.mjs";
6
6
  import { applyRootHelp } from "./help.mjs";
7
- import { printDevReadySummary, printDoctorReport, printTargetsTable, renderOutput } from "./render.mjs";
7
+ import { formatPersistedLogEntry, printDevReadySummary, printDoctorReport, printTargetsTable, renderOutput } from "./render.mjs";
8
8
  import { resolveClientRuntime, resolveOperationalTarget } from "./targets.mjs";
9
9
  import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
10
10
  import path from "node:path";
11
+ import { spawn } from "node:child_process";
11
12
  import { Command } from "commander";
12
13
  import { createSchemaSnapshot, diffSchemaSnapshots, renderMigrationSql } from "../core/index.mjs";
13
- import { VALID_SYNCORE_TEMPLATES, applyProjectMigrations, detectProjectTemplate, formatError, getNextMigrationNumber, hasSyncoreProject, isLocalPortInUse, loadProjectSchema, readStoredSnapshot, resolveRequestedTemplate, runCodegen, runDevProjectBootstrap, scaffoldProject, slugify, startDevHub, writeStoredSnapshot } from "../core/cli.mjs";
14
+ import { VALID_SYNCORE_TEMPLATES, applyProjectMigrations, detectProjectTemplate, fileExists, formatError, getNextMigrationNumber, hasSyncoreProject, isLocalPortInUse, loadProjectSchema, readStoredSnapshot, resolveRequestedTemplate, runCodegen, runDevProjectBootstrap, scaffoldProject, slugify, startDevHub, writeStoredSnapshot } from "../core/cli.mjs";
14
15
  //#region src/app.ts
15
16
  async function runSyncoreCli(argv = process.argv) {
16
17
  const program = buildProgram();
@@ -91,26 +92,37 @@ function addCodegenCommand(program) {
91
92
  });
92
93
  }
93
94
  function addDoctorCommand(program) {
94
- program.command("doctor").summary("Inspect the current Syncore project state").description("Check project structure, template capabilities, hub state, and available targets.").addHelpText("after", [
95
+ program.command("doctor").summary("Inspect the current Syncore project state").description("Check project structure, template capabilities, hub state, and available targets.").option("--fix", "Apply safe low-risk fixes like codegen and snapshot refresh").addHelpText("after", [
95
96
  "",
96
97
  "Examples:",
97
98
  " npx syncorejs doctor",
98
- " npx syncorejs doctor --json"
99
- ].join("\n")).action(async (_options, command) => {
99
+ " npx syncorejs doctor --json",
100
+ " npx syncorejs doctor --fix"
101
+ ].join("\n")).action(async (options, command) => {
100
102
  const ctx = createContext(command);
101
103
  await executeCommand(ctx, async () => {
102
- const report = await buildDoctorReport(ctx.cwd);
104
+ let report = await buildDoctorReport(ctx.cwd);
105
+ let appliedFixes = [];
106
+ if (options.fix) {
107
+ appliedFixes = await applyDoctorFixes(ctx.cwd, report);
108
+ report = await buildDoctorReport(ctx.cwd);
109
+ }
103
110
  if (ctx.json) {
104
111
  ctx.printResult({
105
112
  command: "doctor",
106
- data: report
113
+ ...options.fix && appliedFixes.length > 0 ? { summary: `Applied ${appliedFixes.length} safe fix(es).` } : {},
114
+ data: {
115
+ ...report,
116
+ ...options.fix ? { appliedFixes } : {}
117
+ }
107
118
  });
108
119
  return;
109
120
  }
110
- ctx.info(`Detected template: ${report.template}`);
111
- ctx.info(`Project status: ${report.status}`);
112
- ctx.info(`Project target: ${report.projectTarget ? report.projectTarget.databasePath : "not configured"}`);
113
- ctx.info(`Devtools hub: ${report.hub.running ? report.hub.url : "not running"}`);
121
+ ctx.info(report.primaryIssue.summary);
122
+ if (options.fix) if (appliedFixes.length > 0) {
123
+ ctx.success(`Applied ${appliedFixes.length} safe fix(es).`);
124
+ for (const fix of appliedFixes) ctx.info(fix);
125
+ } else ctx.info("No safe fixes were applied.");
114
126
  printDoctorReport(report, { verbose: ctx.verbose });
115
127
  if (report.workspaceMatches.length > 0) {
116
128
  ctx.warn("You appear to be at a workspace root instead of inside an app package.");
@@ -147,26 +159,31 @@ function addTargetsCommand(program) {
147
159
  });
148
160
  }
149
161
  function addDevCommand(program) {
150
- program.command("dev").summary("Run the Syncore development loop").description("Bootstrap the local Syncore project, start the hub, discover targets, and keep the local workflow in sync.").option("--template <template>", `Template to scaffold when Syncore is missing (${VALID_SYNCORE_TEMPLATES.join(", ")}, or auto)`, "auto").option("--once", "Run bootstrap once and exit").option("--until-success", "Retry bootstrap until it succeeds").option("--run <function>", "Run a Syncore function after bootstrap succeeds").option("--run-sh <command>", "Run a shell command after bootstrap succeeds").option("--open-dashboard", "Open the dashboard URL even in non-interactive mode").addHelpText("after", [
162
+ program.command("dev").summary("Run the Syncore development loop").description("Bootstrap the local Syncore project, start the hub, discover targets, and keep the local workflow in sync.").option("--template <template>", `Template to scaffold when Syncore is missing (${VALID_SYNCORE_TEMPLATES.join(", ")}, or auto)`, "auto").option("--once", "Run bootstrap once and exit").option("--until-success", "Retry bootstrap until it succeeds").option("--run <function>", "Run a Syncore function after bootstrap succeeds").option("--run-sh <command>", "Run a shell command after bootstrap succeeds").option("--typecheck <mode>", "Run TypeScript typecheck before entering the local dev loop: enable, try, or disable", "try").option("--tail-logs <mode>", "Control whether runtime logs appear in this terminal: always, errors, or disable", "errors").option("--no-open-dashboard", "Do not open the dashboard automatically after the hub is ready").addHelpText("after", [
151
163
  "",
152
164
  "Examples:",
153
165
  " npx syncorejs dev",
154
166
  " npx syncorejs dev --once",
155
167
  " npx syncorejs dev --until-success",
156
168
  " npx syncorejs dev --run tasks/list",
157
- " npx syncorejs dev --open-dashboard",
158
- " npx syncorejs dev --run-sh \"npm run dev\""
169
+ " npx syncorejs dev --no-open-dashboard",
170
+ " npx syncorejs dev --run-sh \"npm run dev\"",
171
+ " npx syncorejs dev --typecheck enable",
172
+ " npx syncorejs dev --tail-logs always"
159
173
  ].join("\n")).action(async (options, command) => {
160
174
  const ctx = createContext(command);
161
175
  await executeCommand(ctx, async () => {
162
176
  if (options.run && options.runSh) ctx.fail("`syncorejs dev` accepts either --run or --run-sh, not both.");
163
- const shouldOpenDashboard = Boolean(options.openDashboard) || isRealInteractiveTerminal(ctx);
177
+ const shouldOpenDashboard = options.openDashboard ?? true;
164
178
  await ensureLocalPortConfiguration(ctx);
165
179
  const template = await resolveRequestedTemplate(ctx.cwd, options.template);
180
+ validateDevModes(ctx, options);
166
181
  printDevSessionIntro(ctx);
167
182
  await ensureDevProjectExists(ctx, template);
183
+ printDevPreflight(ctx, await buildDoctorReport(ctx.cwd));
184
+ const typecheckResult = await runDevTypecheck(ctx, options.typecheck);
168
185
  if (options.once) {
169
- await runDevBootstrapLoop(ctx, template, options.untilSuccess ?? false);
186
+ const bootstrapReport = await runDevBootstrapLoop(ctx, template, options.untilSuccess ?? false, options.tailLogs);
170
187
  await runDevFollowup(ctx, options);
171
188
  const targets = await listAvailableTargets(ctx.cwd);
172
189
  ctx.printResult({
@@ -179,32 +196,39 @@ function addDevCommand(program) {
179
196
  projectTargetConfigured: targets.some((target) => target.kind === "project"),
180
197
  dashboardUrl: resolveDashboardUrl(),
181
198
  devtoolsUrl: resolveDevtoolsUrl(),
182
- targets
199
+ targets,
200
+ codegenStatus: "refreshed",
201
+ driftStatus: describeDevDriftStatus(bootstrapReport),
202
+ typecheckStatus: describeTypecheckStatus(typecheckResult)
183
203
  });
184
204
  return;
185
205
  }
186
- await runDevBootstrapLoop(ctx, template, options.untilSuccess ?? false);
206
+ const bootstrapReport = await runDevBootstrapLoop(ctx, template, options.untilSuccess ?? false, options.tailLogs);
187
207
  const targets = await listAvailableTargets(ctx.cwd);
188
208
  printDevReadySummary(ctx, {
189
209
  template,
190
210
  projectTargetConfigured: targets.some((target) => target.kind === "project"),
191
211
  dashboardUrl: resolveDashboardUrl(),
192
212
  devtoolsUrl: resolveDevtoolsUrl(),
193
- targets
213
+ targets,
214
+ codegenStatus: "refreshed",
215
+ driftStatus: describeDevDriftStatus(bootstrapReport),
216
+ typecheckStatus: describeTypecheckStatus(typecheckResult)
194
217
  });
195
- await startManagedDevHub(ctx, template);
196
- await maybeOpenDashboard(ctx, shouldOpenDashboard);
197
- await monitorLiveDevSession(ctx, template);
218
+ await maybeOpenDashboard(ctx, shouldOpenDashboard, await startManagedDevHub(ctx, template));
219
+ await monitorLiveDevSession(ctx, template, options.tailLogs);
198
220
  });
199
221
  });
200
222
  }
201
- function isRealInteractiveTerminal(context) {
202
- return context.interactive && Boolean(process.stdin.isTTY && process.stdout.isTTY);
203
- }
204
- async function maybeOpenDashboard(context, shouldOpenDashboard) {
223
+ async function maybeOpenDashboard(context, shouldOpenDashboard, hubSession) {
205
224
  if (!shouldOpenDashboard) return;
206
- if (await openTarget(resolveDashboardUrl())) {
207
- context.info(`Opened dashboard at ${resolveDashboardUrl()}.`);
225
+ const targetUrl = hubSession?.authenticatedDashboardUrl ?? await resolveActiveDashboardUrl(context.cwd);
226
+ if (!await waitForDashboardReady(targetUrl)) {
227
+ context.warn("Dashboard did not become ready in time, so it was not opened.");
228
+ return;
229
+ }
230
+ if (await openTarget(targetUrl)) {
231
+ context.info(`Opened dashboard at ${targetUrl}.`);
208
232
  return;
209
233
  }
210
234
  context.warn("Unable to open the dashboard automatically.");
@@ -395,7 +419,12 @@ function addDataCommand(program) {
395
419
  data: tables,
396
420
  target: target.id
397
421
  });
398
- if (!ctx.json) for (const entry of tables) process.stdout.write(` ${entry.name} (${entry.documentCount} document(s))\n`);
422
+ if (!ctx.json) for (const entry of tables) {
423
+ const tableEntry = entry;
424
+ const label = typeof tableEntry.displayName === "string" && tableEntry.displayName.length > 0 && tableEntry.displayName !== tableEntry.name ? `${tableEntry.displayName} -> ${tableEntry.name}` : tableEntry.name;
425
+ const owner = tableEntry.owner === "component" ? ` component:${String(tableEntry.componentPath ?? "unknown")}` : "";
426
+ process.stdout.write(` ${label} (${tableEntry.documentCount} document(s))${owner}\n`);
427
+ }
399
428
  return;
400
429
  }
401
430
  if (target.kind === "project") {
@@ -506,14 +535,18 @@ function addDashboardCommand(program) {
506
535
  const ctx = createContext(command);
507
536
  await executeCommand(ctx, async () => {
508
537
  const url = resolveDashboardUrl();
538
+ const activeUrl = await resolveActiveDashboardUrl(ctx.cwd);
509
539
  if (options.open) {
510
- if (!await openTarget(url)) ctx.warn("Unable to open the dashboard automatically.");
540
+ if (!(await waitForDashboardReady(activeUrl) ? await openTarget(activeUrl) : false)) ctx.warn("Unable to open the dashboard automatically.");
511
541
  }
512
542
  ctx.printResult({
513
543
  summary: "Dashboard URL resolved.",
514
544
  command: "dashboard",
515
- data: { url },
516
- nextSteps: [`Open ${url}`]
545
+ data: {
546
+ url: activeUrl,
547
+ baseUrl: url
548
+ },
549
+ nextSteps: [`Open ${activeUrl}`]
517
550
  });
518
551
  });
519
552
  });
@@ -559,13 +592,15 @@ async function ensureLocalPortConfiguration(context) {
559
592
  if (Number.isFinite(dashboardPort) && Number.isFinite(devtoolsPort) && dashboardPort === devtoolsPort) context.fail([`Dashboard and devtools cannot share the same port (${dashboardPort}).`, "Set different values for SYNCORE_DASHBOARD_PORT and SYNCORE_DEVTOOLS_PORT, then rerun `npx syncorejs dev`."].join(" "));
560
593
  if (Number.isFinite(dashboardPort) && dashboardPort > 0 && Number.isFinite(devtoolsPort) && devtoolsPort > 0 && await isLocalPortInUse(dashboardPort) && !await isLocalPortInUse(devtoolsPort)) context.warn(`Dashboard port ${dashboardPort} is already in use. If Syncore does not start cleanly, set SYNCORE_DASHBOARD_PORT to a different value.`);
561
594
  }
562
- async function runDevBootstrapLoop(context, template, untilSuccess) {
595
+ async function runDevBootstrapLoop(context, template, untilSuccess, tailLogs) {
563
596
  while (true) try {
597
+ let sawBootstrapFailure = false;
564
598
  printCompactDevPhase(context, "Project");
565
599
  printCompactDevPhase(context, "Codegen");
566
600
  printCompactDevPhase(context, "Schema");
567
601
  await withConsoleCapture((method, message) => {
568
602
  if (/destructive schema changes/i.test(message)) {
603
+ sawBootstrapFailure = true;
569
604
  context.error("Syncore dev blocked by destructive schema changes.");
570
605
  return;
571
606
  }
@@ -573,9 +608,20 @@ async function runDevBootstrapLoop(context, template, untilSuccess) {
573
608
  context.warn(message.replace(/^Syncore dev warning:\s*/i, ""));
574
609
  return;
575
610
  }
576
- if (/bootstrap failed/i.test(message) || method === "error") context.error(message);
611
+ if (/bootstrap failed/i.test(message) || method === "error") {
612
+ sawBootstrapFailure = true;
613
+ context.error(message);
614
+ }
577
615
  }, async () => runDevProjectBootstrap(context.cwd, template));
578
- return;
616
+ const report = await buildDoctorReport(context.cwd);
617
+ if (sawBootstrapFailure || report.status === "schema-destructive-drift") {
618
+ if (tailLogs === "errors") await printRecentRuntimeSignals(context, context.cwd);
619
+ context.fail(report.primaryIssue.summary, 1, report, {
620
+ category: "runtime",
621
+ nextSteps: report.suggestions
622
+ });
623
+ }
624
+ return report;
579
625
  } catch (error) {
580
626
  if (!untilSuccess) throw error;
581
627
  context.warn(`Syncore dev bootstrap failed, retrying: ${formatError(error)}`);
@@ -585,7 +631,7 @@ async function runDevBootstrapLoop(context, template, untilSuccess) {
585
631
  async function startManagedDevHub(context, template) {
586
632
  printCompactDevPhase(context, "Hub");
587
633
  printCompactDevPhase(context, "Targets");
588
- await withConsoleCapture((method, message) => {
634
+ return await withConsoleCapture((method, message) => {
589
635
  if (/already running/i.test(message)) {
590
636
  context.info(message.replaceAll("127.0.0.1", "localhost"));
591
637
  return;
@@ -609,6 +655,28 @@ async function startManagedDevHub(context, template) {
609
655
  template
610
656
  }));
611
657
  }
658
+ async function waitForDashboardReady(url, options = {}) {
659
+ const timeoutMs = options.timeoutMs ?? 15e3;
660
+ const intervalMs = options.intervalMs ?? 250;
661
+ const deadline = Date.now() + timeoutMs;
662
+ while (Date.now() < deadline) {
663
+ try {
664
+ const controller = new AbortController();
665
+ const timeout = setTimeout(() => controller.abort(), intervalMs);
666
+ try {
667
+ const response = await fetch(url, {
668
+ signal: controller.signal,
669
+ redirect: "manual"
670
+ });
671
+ if (response.ok || response.status === 304) return true;
672
+ } finally {
673
+ clearTimeout(timeout);
674
+ }
675
+ } catch {}
676
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
677
+ }
678
+ return false;
679
+ }
612
680
  async function runDevFollowup(context, options) {
613
681
  if (options.run) {
614
682
  const target = await resolveOperationalTarget(context, void 0, {
@@ -642,10 +710,13 @@ async function runDevFollowup(context, options) {
642
710
  }
643
711
  if (options.runSh) await runShellCommand(context, options.runSh);
644
712
  }
645
- async function monitorLiveDevSession(context, template) {
646
- if (!templateUsesConnectedClients(template)) {
713
+ async function monitorLiveDevSession(context, template, tailLogs) {
714
+ const stopTailing = tailLogs === "always" ? startRuntimeLogTail(context, context.cwd) : void 0;
715
+ if (!templateUsesConnectedClients$1(template)) try {
647
716
  await waitForSignal();
648
717
  return;
718
+ } finally {
719
+ stopTailing?.();
649
720
  }
650
721
  let knownTargets = /* @__PURE__ */ new Set();
651
722
  let waitingNoticeVisible = false;
@@ -653,7 +724,10 @@ async function monitorLiveDevSession(context, template) {
653
724
  const nextTargets = await listConnectedClientTargets();
654
725
  const nextIds = new Set(nextTargets.map((target) => target.id));
655
726
  for (const target of nextTargets) if (!knownTargets.has(target.id)) context.info(`Client target connected: ${target.id}`);
656
- for (const targetId of knownTargets) if (!nextIds.has(targetId)) context.warn(`Client target disconnected: ${targetId}`);
727
+ for (const targetId of knownTargets) if (!nextIds.has(targetId)) {
728
+ context.warn(`Client target disconnected: ${targetId}`);
729
+ if (tailLogs === "errors") await printRecentRuntimeSignals(context, context.cwd);
730
+ }
657
731
  if (nextTargets.length === 0 && !waitingNoticeVisible) {
658
732
  context.info("Hub ready. Start your app to connect a client target.");
659
733
  waitingNoticeVisible = true;
@@ -668,6 +742,157 @@ async function monitorLiveDevSession(context, template) {
668
742
  await waitForSignal();
669
743
  } finally {
670
744
  clearInterval(interval);
745
+ stopTailing?.();
746
+ }
747
+ }
748
+ function validateDevModes(context, options) {
749
+ if (![
750
+ "enable",
751
+ "try",
752
+ "disable"
753
+ ].includes(options.typecheck)) context.fail(`Unknown typecheck mode ${JSON.stringify(options.typecheck)}. Expected enable, try, or disable.`);
754
+ if (![
755
+ "always",
756
+ "errors",
757
+ "disable"
758
+ ].includes(options.tailLogs)) context.fail(`Unknown tail log mode ${JSON.stringify(options.tailLogs)}. Expected always, errors, or disable.`);
759
+ }
760
+ function printDevPreflight(context, report) {
761
+ if (report.status === "missing-generated") {
762
+ context.info("Preflight: generated Syncore files are missing; dev will refresh them.");
763
+ return;
764
+ }
765
+ if (report.status === "schema-drift") {
766
+ context.info("Preflight: schema drift detected; dev will refresh local Syncore state where safe.");
767
+ return;
768
+ }
769
+ if (report.status === "waiting-for-client") {
770
+ context.info("Preflight: this template uses client-managed runtimes; connect the app to unlock live targets.");
771
+ return;
772
+ }
773
+ context.info("Preflight: Syncore project structure and local runtime prerequisites were inspected.");
774
+ }
775
+ async function runDevTypecheck(context, mode) {
776
+ if (mode === "disable") return {
777
+ mode,
778
+ attempted: false,
779
+ ok: true,
780
+ summary: "disabled"
781
+ };
782
+ const tsconfigPath = await findTypecheckConfig(context.cwd);
783
+ if (!tsconfigPath) {
784
+ if (mode === "enable") context.fail("Typecheck was enabled but no tsconfig.json was found in this project.");
785
+ context.warn("Typecheck skipped because no tsconfig.json was found.");
786
+ return {
787
+ mode,
788
+ attempted: false,
789
+ ok: true,
790
+ summary: "skipped (no tsconfig)"
791
+ };
792
+ }
793
+ const tscPath = await findTypeScriptCompiler(context.cwd);
794
+ if (!tscPath) {
795
+ if (mode === "enable") context.fail("Typecheck was enabled but no local TypeScript compiler was found.");
796
+ context.warn("Typecheck skipped because TypeScript is not available in this workspace.");
797
+ return {
798
+ mode,
799
+ attempted: false,
800
+ ok: true,
801
+ summary: "skipped (tsc unavailable)"
802
+ };
803
+ }
804
+ context.info(`Typecheck: running tsc --noEmit -p ${path.basename(tsconfigPath)}.`);
805
+ const result = await runTypeScriptCompiler(context.cwd, tscPath, tsconfigPath);
806
+ if (result.ok) {
807
+ context.success("Typecheck passed.");
808
+ return {
809
+ mode,
810
+ attempted: true,
811
+ ok: true,
812
+ summary: "passed"
813
+ };
814
+ }
815
+ const detail = summarizeCompilerOutput(result.output);
816
+ if (mode === "enable") context.fail(`Typecheck failed.${detail ? ` ${detail}` : ""}`, 1, result.output, { category: "validation" });
817
+ context.warn(`Typecheck reported issues but dev will continue.${detail ? ` ${detail}` : ""}`);
818
+ return {
819
+ mode,
820
+ attempted: true,
821
+ ok: false,
822
+ summary: "warning",
823
+ ...detail ? { details: detail } : {}
824
+ };
825
+ }
826
+ async function findTypecheckConfig(cwd) {
827
+ for (const candidate of [
828
+ "tsconfig.json",
829
+ "tsconfig.app.json",
830
+ "tsconfig.main.json",
831
+ "tsconfig.build.json"
832
+ ]) {
833
+ const resolved = path.join(cwd, candidate);
834
+ if (await fileExists(resolved)) return resolved;
835
+ }
836
+ return null;
837
+ }
838
+ async function findTypeScriptCompiler(cwd) {
839
+ const executableName = process.platform === "win32" ? "tsc.cmd" : "tsc";
840
+ let current = cwd;
841
+ while (true) {
842
+ const candidate = path.join(current, "node_modules", ".bin", executableName);
843
+ if (await fileExists(candidate)) return candidate;
844
+ const parent = path.dirname(current);
845
+ if (parent === current) break;
846
+ current = parent;
847
+ }
848
+ return null;
849
+ }
850
+ async function runTypeScriptCompiler(cwd, compilerPath, tsconfigPath) {
851
+ return await new Promise((resolve, reject) => {
852
+ const isWindowsCmd = process.platform === "win32" && /\.(cmd|bat)$/i.test(compilerPath);
853
+ const child = spawn(compilerPath, [
854
+ "--noEmit",
855
+ "-p",
856
+ tsconfigPath
857
+ ], {
858
+ cwd,
859
+ stdio: [
860
+ "ignore",
861
+ "pipe",
862
+ "pipe"
863
+ ],
864
+ ...isWindowsCmd ? { shell: true } : {}
865
+ });
866
+ let output = "";
867
+ child.stdout.on("data", (chunk) => {
868
+ output += chunk.toString();
869
+ });
870
+ child.stderr.on("data", (chunk) => {
871
+ output += chunk.toString();
872
+ });
873
+ child.once("error", reject);
874
+ child.once("exit", (code) => {
875
+ resolve({
876
+ ok: (code ?? 1) === 0,
877
+ output: output.trim()
878
+ });
879
+ });
880
+ });
881
+ }
882
+ function summarizeCompilerOutput(output) {
883
+ return output.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
884
+ }
885
+ function describeTypecheckStatus(result) {
886
+ return result.details ? `${result.summary} (${result.details})` : result.summary;
887
+ }
888
+ function describeDevDriftStatus(report) {
889
+ switch (report.drift.state) {
890
+ case "clean": return "clean";
891
+ case "missing-snapshot": return "snapshot refreshed";
892
+ case "snapshot-outdated": return "snapshot refreshed";
893
+ case "migration-pending": return "migration review pending";
894
+ case "destructive": return "blocked";
895
+ default: return "unavailable";
671
896
  }
672
897
  }
673
898
  async function requireHubConnection(context) {
@@ -777,8 +1002,41 @@ async function readPersistedLogs(cwd) {
777
1002
  }
778
1003
  return (await readFile(logPath, "utf8")).split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line)).filter((entry) => entry.version === 2).filter((entry) => !shouldSuppressLogEntry(entry));
779
1004
  }
1005
+ async function printRecentRuntimeSignals(context, cwd, limit = 5) {
1006
+ const recent = (await readPersistedLogs(cwd)).slice(-limit);
1007
+ if (recent.length === 0) return;
1008
+ context.info("Recent runtime signals:");
1009
+ for (const entry of recent) process.stdout.write(` ${formatPersistedLogEntry(entry)}\n`);
1010
+ }
1011
+ function startRuntimeLogTail(context, cwd) {
1012
+ let seenCount = 0;
1013
+ let disposed = false;
1014
+ const poll = async () => {
1015
+ if (disposed) return;
1016
+ const entries = await readPersistedLogs(cwd);
1017
+ const next = entries.slice(seenCount);
1018
+ for (const entry of next) process.stdout.write(`${formatPersistedLogEntry(entry)}\n`);
1019
+ seenCount = entries.length;
1020
+ };
1021
+ const interval = setInterval(() => {
1022
+ poll();
1023
+ }, 1200);
1024
+ poll();
1025
+ return () => {
1026
+ disposed = true;
1027
+ clearInterval(interval);
1028
+ };
1029
+ }
780
1030
  function normalizeRuntimeEvent(event, runtimeEntry) {
781
1031
  const functionName = typeof event.functionName === "string" ? event.functionName : "unknown";
1032
+ const functionComponent = resolveComponentInfoFromFunctionName(functionName);
1033
+ const queryComponent = typeof event.queryId === "string" ? resolveComponentInfoFromFunctionName(formatInvalidatedQueryId(event.queryId)) : void 0;
1034
+ const storageComponent = typeof event.storageId === "string" ? resolveComponentInfoFromScopedValue(event.storageId) : void 0;
1035
+ const componentInfo = typeof event.componentPath === "string" ? {
1036
+ owner: "component",
1037
+ componentPath: event.componentPath,
1038
+ ...typeof event.componentName === "string" ? { componentName: event.componentName } : {}
1039
+ } : functionComponent ?? queryComponent ?? storageComponent;
782
1040
  const logMessage = typeof event.message === "string" ? event.message : "Syncore log";
783
1041
  const resolvedTargetId = event.runtimeId === "syncore-dev-hub" ? "all" : runtimeEntry?.targetId ?? event.runtimeId;
784
1042
  const runtimeLabel = event.runtimeId === "syncore-dev-hub" ? "dashboard" : runtimeEntry?.label ?? "runtime";
@@ -798,6 +1056,7 @@ function normalizeRuntimeEvent(event, runtimeEntry) {
798
1056
  ...entryBase,
799
1057
  eventType: event.type,
800
1058
  category: "system",
1059
+ ...componentInfo ?? {},
801
1060
  message: logMessage,
802
1061
  event
803
1062
  })) return null;
@@ -806,6 +1065,7 @@ function normalizeRuntimeEvent(event, runtimeEntry) {
806
1065
  ...entryBase,
807
1066
  eventType: event.type,
808
1067
  category: "query",
1068
+ ...componentInfo ?? {},
809
1069
  message: `${functionName} executed`,
810
1070
  event
811
1071
  };
@@ -813,6 +1073,7 @@ function normalizeRuntimeEvent(event, runtimeEntry) {
813
1073
  ...entryBase,
814
1074
  eventType: event.type,
815
1075
  category: "system",
1076
+ ...componentInfo ?? {},
816
1077
  message: `${formatInvalidatedQueryId(event.queryId)} invalidated${typeof event.reason === "string" ? ` (${event.reason})` : ""}`,
817
1078
  event
818
1079
  };
@@ -820,6 +1081,7 @@ function normalizeRuntimeEvent(event, runtimeEntry) {
820
1081
  ...entryBase,
821
1082
  eventType: event.type,
822
1083
  category: "mutation",
1084
+ ...componentInfo ?? {},
823
1085
  message: Array.isArray(event.changedTables) && event.changedTables.length > 0 ? `${functionName} committed (${event.changedTables.join(", ")})` : `${functionName} committed`,
824
1086
  event
825
1087
  };
@@ -827,6 +1089,7 @@ function normalizeRuntimeEvent(event, runtimeEntry) {
827
1089
  ...entryBase,
828
1090
  eventType: event.type,
829
1091
  category: "action",
1092
+ ...componentInfo ?? {},
830
1093
  message: typeof event.error === "string" && event.error.length > 0 ? `${functionName} failed: ${event.error}` : `${functionName} completed`,
831
1094
  event
832
1095
  };
@@ -848,6 +1111,7 @@ function normalizeRuntimeEvent(event, runtimeEntry) {
848
1111
  ...entryBase,
849
1112
  eventType: event.type,
850
1113
  category: "system",
1114
+ ...componentInfo ?? {},
851
1115
  message: `${typeof event.operation === "string" ? event.operation : "update"} ${typeof event.storageId === "string" ? event.storageId : "storage"}`,
852
1116
  event
853
1117
  };
@@ -855,6 +1119,7 @@ function normalizeRuntimeEvent(event, runtimeEntry) {
855
1119
  ...entryBase,
856
1120
  eventType: event.type,
857
1121
  category: "system",
1122
+ ...componentInfo ?? {},
858
1123
  message: event.type === "log" ? logMessage : humanizeRuntimeEvent(event),
859
1124
  event
860
1125
  };
@@ -880,6 +1145,22 @@ function formatInvalidatedQueryId(queryId) {
880
1145
  if (separatorIndex === -1) return queryId;
881
1146
  return queryId.slice(0, separatorIndex);
882
1147
  }
1148
+ function resolveComponentInfoFromFunctionName(functionName) {
1149
+ const match = /^components\/(.+)\/(public|internal)\/(.+)$/.exec(functionName);
1150
+ if (!match) return;
1151
+ return {
1152
+ owner: "component",
1153
+ componentPath: match[1] ?? ""
1154
+ };
1155
+ }
1156
+ function resolveComponentInfoFromScopedValue(value) {
1157
+ const match = /^component:([^:]+):(.+)$/.exec(value);
1158
+ if (!match) return;
1159
+ return {
1160
+ owner: "component",
1161
+ componentPath: match[1] ?? ""
1162
+ };
1163
+ }
883
1164
  function humanizeRuntimeEvent(event) {
884
1165
  if (event.type === "scheduler.tick" && Array.isArray(event.executedJobIds)) return `scheduler tick (${event.executedJobIds.length} job(s))`;
885
1166
  return event.type.replaceAll(".", " ");