syncorejs 0.1.0 → 0.2.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 (200) hide show
  1. package/dist/{core/src/cli.d.ts → _vendor/cli/app.d.mts} +4 -2
  2. package/dist/_vendor/cli/app.d.mts.map +1 -0
  3. package/dist/_vendor/cli/app.mjs +997 -0
  4. package/dist/_vendor/cli/app.mjs.map +1 -0
  5. package/dist/_vendor/cli/context.mjs +180 -0
  6. package/dist/_vendor/cli/context.mjs.map +1 -0
  7. package/dist/_vendor/cli/dev-session.mjs +49 -0
  8. package/dist/_vendor/cli/dev-session.mjs.map +1 -0
  9. package/dist/_vendor/cli/doctor.mjs +80 -0
  10. package/dist/_vendor/cli/doctor.mjs.map +1 -0
  11. package/dist/_vendor/cli/errors.mjs +22 -0
  12. package/dist/_vendor/cli/errors.mjs.map +1 -0
  13. package/dist/_vendor/cli/help.mjs +26 -0
  14. package/dist/_vendor/cli/help.mjs.map +1 -0
  15. package/dist/_vendor/cli/index.d.mts +2 -0
  16. package/dist/_vendor/cli/index.mjs +23 -0
  17. package/dist/_vendor/cli/index.mjs.map +1 -0
  18. package/dist/_vendor/cli/messages.mjs +32 -0
  19. package/dist/_vendor/cli/messages.mjs.map +1 -0
  20. package/dist/_vendor/cli/preflight.mjs +35 -0
  21. package/dist/_vendor/cli/preflight.mjs.map +1 -0
  22. package/dist/_vendor/cli/project.mjs +583 -0
  23. package/dist/_vendor/cli/project.mjs.map +1 -0
  24. package/dist/_vendor/cli/render.mjs +133 -0
  25. package/dist/_vendor/cli/render.mjs.map +1 -0
  26. package/dist/_vendor/cli/targets.mjs +87 -0
  27. package/dist/_vendor/cli/targets.mjs.map +1 -0
  28. package/dist/_vendor/core/cli.d.mts +59 -1
  29. package/dist/_vendor/core/cli.d.mts.map +1 -1
  30. package/dist/_vendor/core/cli.mjs +528 -75
  31. package/dist/_vendor/core/cli.mjs.map +1 -1
  32. package/dist/_vendor/core/index.d.mts +12 -4
  33. package/dist/_vendor/core/index.d.mts.map +1 -0
  34. package/dist/_vendor/core/index.mjs +4 -3
  35. package/dist/_vendor/core/index.mjs.map +1 -1
  36. package/dist/_vendor/core/runtime/devtools.d.mts +32 -6
  37. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  38. package/dist/_vendor/core/runtime/devtools.mjs +397 -182
  39. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  40. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  41. package/dist/_vendor/core/runtime/runtime.d.mts +89 -7
  42. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  43. package/dist/_vendor/core/runtime/runtime.mjs +303 -32
  44. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  45. package/dist/_vendor/devtools-protocol/index.d.ts +189 -82
  46. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  47. package/dist/_vendor/devtools-protocol/index.js +39 -0
  48. package/dist/_vendor/devtools-protocol/index.js.map +1 -0
  49. package/dist/_vendor/next/config.d.ts.map +1 -1
  50. package/dist/_vendor/next/config.js +2 -5
  51. package/dist/_vendor/next/config.js.map +1 -1
  52. package/dist/_vendor/platform-expo/index.d.ts +15 -5
  53. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  54. package/dist/_vendor/platform-expo/index.js +33 -3
  55. package/dist/_vendor/platform-expo/index.js.map +1 -1
  56. package/dist/_vendor/platform-expo/react.js.map +1 -1
  57. package/dist/_vendor/platform-node/index.d.mts +10 -5
  58. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  59. package/dist/_vendor/platform-node/index.mjs +145 -35
  60. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  61. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  62. package/dist/_vendor/platform-web/external-change.d.ts +39 -0
  63. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -0
  64. package/dist/_vendor/platform-web/external-change.js +61 -0
  65. package/dist/_vendor/platform-web/external-change.js.map +1 -0
  66. package/dist/_vendor/platform-web/index.d.ts +27 -5
  67. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  68. package/dist/_vendor/platform-web/index.js +310 -44
  69. package/dist/_vendor/platform-web/index.js.map +1 -1
  70. package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
  71. package/dist/_vendor/platform-web/opfs.js.map +1 -1
  72. package/dist/_vendor/platform-web/persistence.js.map +1 -1
  73. package/dist/_vendor/platform-web/react.js.map +1 -1
  74. package/dist/_vendor/platform-web/sqljs.js +22 -2
  75. package/dist/_vendor/platform-web/sqljs.js.map +1 -1
  76. package/dist/_vendor/schema/definition.js.map +1 -1
  77. package/dist/_vendor/schema/planner.js.map +1 -1
  78. package/dist/_vendor/schema/validators.js.map +1 -1
  79. package/dist/browser-react.d.ts +1 -1
  80. package/dist/browser-react.js +1 -1
  81. package/dist/browser.d.ts +6 -7
  82. package/dist/browser.d.ts.map +1 -1
  83. package/dist/browser.js +4 -5
  84. package/dist/browser.js.map +1 -1
  85. package/dist/cli.d.ts +1 -1
  86. package/dist/cli.js +12 -3
  87. package/dist/cli.js.map +1 -1
  88. package/dist/expo-react.d.ts +1 -1
  89. package/dist/expo-react.js +1 -1
  90. package/dist/expo.d.ts +1 -2
  91. package/dist/expo.js +1 -2
  92. package/dist/index.d.ts +3 -7
  93. package/dist/index.js +3 -8
  94. package/dist/next-config.d.ts +1 -2
  95. package/dist/next-config.js +1 -2
  96. package/dist/next.d.ts +1 -3
  97. package/dist/next.js +1 -3
  98. package/dist/node-ipc-react.d.ts +1 -1
  99. package/dist/node-ipc-react.js +1 -1
  100. package/dist/node-ipc.d.ts +1 -2
  101. package/dist/node-ipc.js +1 -2
  102. package/dist/node.d.ts +1 -4
  103. package/dist/node.js +1 -3
  104. package/dist/react.d.ts +1 -2
  105. package/dist/react.js +1 -2
  106. package/dist/svelte.d.ts +1 -2
  107. package/dist/svelte.js +1 -2
  108. package/package.json +6 -3
  109. package/dist/core/src/cli.d.ts.map +0 -1
  110. package/dist/core/src/cli.js +0 -1196
  111. package/dist/core/src/cli.js.map +0 -1
  112. package/dist/core/src/index.js +0 -7
  113. package/dist/core/src/runtime/devtools.d.ts +0 -7
  114. package/dist/core/src/runtime/devtools.d.ts.map +0 -1
  115. package/dist/core/src/runtime/devtools.js +0 -300
  116. package/dist/core/src/runtime/devtools.js.map +0 -1
  117. package/dist/core/src/runtime/functions.d.ts +0 -123
  118. package/dist/core/src/runtime/functions.d.ts.map +0 -1
  119. package/dist/core/src/runtime/functions.js +0 -71
  120. package/dist/core/src/runtime/functions.js.map +0 -1
  121. package/dist/core/src/runtime/id.d.ts +0 -13
  122. package/dist/core/src/runtime/id.d.ts.map +0 -1
  123. package/dist/core/src/runtime/id.js +0 -28
  124. package/dist/core/src/runtime/id.js.map +0 -1
  125. package/dist/core/src/runtime/runtime.d.ts +0 -371
  126. package/dist/core/src/runtime/runtime.d.ts.map +0 -1
  127. package/dist/core/src/runtime/runtime.js +0 -1143
  128. package/dist/core/src/runtime/runtime.js.map +0 -1
  129. package/dist/devtools-protocol/src/index.d.ts +0 -201
  130. package/dist/devtools-protocol/src/index.d.ts.map +0 -1
  131. package/dist/next/src/config.d.ts +0 -17
  132. package/dist/next/src/config.d.ts.map +0 -1
  133. package/dist/next/src/config.js +0 -73
  134. package/dist/next/src/config.js.map +0 -1
  135. package/dist/next/src/index.d.ts +0 -80
  136. package/dist/next/src/index.d.ts.map +0 -1
  137. package/dist/next/src/index.js +0 -82
  138. package/dist/next/src/index.js.map +0 -1
  139. package/dist/platform-expo/src/index.d.ts +0 -96
  140. package/dist/platform-expo/src/index.d.ts.map +0 -1
  141. package/dist/platform-expo/src/index.js +0 -198
  142. package/dist/platform-expo/src/index.js.map +0 -1
  143. package/dist/platform-expo/src/react.d.ts +0 -26
  144. package/dist/platform-expo/src/react.d.ts.map +0 -1
  145. package/dist/platform-expo/src/react.js +0 -30
  146. package/dist/platform-expo/src/react.js.map +0 -1
  147. package/dist/platform-node/src/index.d.ts +0 -145
  148. package/dist/platform-node/src/index.d.ts.map +0 -1
  149. package/dist/platform-node/src/index.js +0 -407
  150. package/dist/platform-node/src/index.js.map +0 -1
  151. package/dist/platform-node/src/ipc-react.d.ts +0 -25
  152. package/dist/platform-node/src/ipc-react.d.ts.map +0 -1
  153. package/dist/platform-node/src/ipc-react.js +0 -21
  154. package/dist/platform-node/src/ipc-react.js.map +0 -1
  155. package/dist/platform-node/src/ipc.d.ts +0 -76
  156. package/dist/platform-node/src/ipc.d.ts.map +0 -1
  157. package/dist/platform-node/src/ipc.js +0 -344
  158. package/dist/platform-node/src/ipc.js.map +0 -1
  159. package/dist/platform-web/src/index.d.ts +0 -106
  160. package/dist/platform-web/src/index.d.ts.map +0 -1
  161. package/dist/platform-web/src/index.js +0 -311
  162. package/dist/platform-web/src/index.js.map +0 -1
  163. package/dist/platform-web/src/indexeddb.js +0 -125
  164. package/dist/platform-web/src/indexeddb.js.map +0 -1
  165. package/dist/platform-web/src/opfs.js +0 -146
  166. package/dist/platform-web/src/opfs.js.map +0 -1
  167. package/dist/platform-web/src/persistence.d.ts +0 -20
  168. package/dist/platform-web/src/persistence.d.ts.map +0 -1
  169. package/dist/platform-web/src/persistence.js +0 -23
  170. package/dist/platform-web/src/persistence.js.map +0 -1
  171. package/dist/platform-web/src/react.d.ts +0 -35
  172. package/dist/platform-web/src/react.d.ts.map +0 -1
  173. package/dist/platform-web/src/react.js +0 -42
  174. package/dist/platform-web/src/react.js.map +0 -1
  175. package/dist/platform-web/src/sqljs.js +0 -133
  176. package/dist/platform-web/src/sqljs.js.map +0 -1
  177. package/dist/platform-web/src/worker.d.ts +0 -79
  178. package/dist/platform-web/src/worker.d.ts.map +0 -1
  179. package/dist/platform-web/src/worker.js +0 -308
  180. package/dist/platform-web/src/worker.js.map +0 -1
  181. package/dist/react/src/index.d.ts +0 -59
  182. package/dist/react/src/index.d.ts.map +0 -1
  183. package/dist/react/src/index.js +0 -151
  184. package/dist/react/src/index.js.map +0 -1
  185. package/dist/schema/src/definition.d.ts +0 -98
  186. package/dist/schema/src/definition.d.ts.map +0 -1
  187. package/dist/schema/src/definition.js +0 -84
  188. package/dist/schema/src/definition.js.map +0 -1
  189. package/dist/schema/src/planner.d.ts +0 -42
  190. package/dist/schema/src/planner.d.ts.map +0 -1
  191. package/dist/schema/src/planner.js +0 -131
  192. package/dist/schema/src/planner.js.map +0 -1
  193. package/dist/schema/src/validators.d.ts +0 -194
  194. package/dist/schema/src/validators.d.ts.map +0 -1
  195. package/dist/schema/src/validators.js +0 -158
  196. package/dist/schema/src/validators.js.map +0 -1
  197. package/dist/svelte/src/index.d.ts +0 -44
  198. package/dist/svelte/src/index.d.ts.map +0 -1
  199. package/dist/svelte/src/index.js +0 -75
  200. package/dist/svelte/src/index.js.map +0 -1
@@ -1,261 +1,416 @@
1
1
  import { createFunctionReference } from "./runtime.mjs";
2
2
  import { describeValidator } from "../../schema/index.js";
3
3
  //#region src/runtime/devtools.ts
4
- function createDevtoolsRequestHandler(deps) {
5
- const { driver, schema, functions, runtime } = deps;
4
+ function createDevtoolsCommandHandler(deps) {
5
+ const { driver, runtime, sql } = deps;
6
6
  return async (payload) => {
7
+ await runtime.prepareForDirectAccess();
7
8
  switch (payload.kind) {
8
- case "fn.list": return {
9
- kind: "fn.list.result",
10
- functions: Object.entries(functions).filter((entry) => entry[1] !== void 0).map(([name, fn]) => {
11
- const def = {
12
- name,
13
- type: fn.kind,
14
- file: inferFileFromFunctionName(name)
15
- };
16
- const argsDesc = describeValidator(fn.argsValidator);
17
- if (argsDesc.kind === "object") def.args = argsDesc.shape;
18
- return def;
19
- })
20
- };
21
9
  case "fn.run": {
22
10
  const start = performance.now();
23
11
  try {
24
12
  let result;
25
13
  switch (payload.functionType) {
26
- case "query": {
27
- const ref = createFunctionReference("query", payload.functionName);
28
- result = await runtime.runQuery(ref, payload.args);
14
+ case "query":
15
+ result = await runtime.runQuery(createFunctionReference("query", payload.functionName), payload.args, { origin: "dashboard" });
29
16
  break;
30
- }
31
- case "mutation": {
32
- const ref = createFunctionReference("mutation", payload.functionName);
33
- result = await runtime.runMutation(ref, payload.args);
17
+ case "mutation":
18
+ result = await runtime.runMutation(createFunctionReference("mutation", payload.functionName), payload.args, { origin: "dashboard" });
34
19
  break;
35
- }
36
- case "action": {
37
- const ref = createFunctionReference("action", payload.functionName);
38
- result = await runtime.runAction(ref, payload.args);
20
+ case "action":
21
+ result = await runtime.runAction(createFunctionReference("action", payload.functionName), payload.args, { origin: "dashboard" });
39
22
  break;
40
- }
41
23
  }
42
24
  return {
43
25
  kind: "fn.run.result",
44
26
  result,
45
27
  durationMs: performance.now() - start
46
28
  };
47
- } catch (err) {
29
+ } catch (error) {
48
30
  return {
49
31
  kind: "fn.run.result",
50
- error: err instanceof Error ? err.message : String(err),
32
+ error: error instanceof Error ? error.message : String(error),
51
33
  durationMs: performance.now() - start
52
34
  };
53
35
  }
54
36
  }
55
- case "schema.get": {
56
- const tableNames = schema.tableNames();
57
- return {
58
- kind: "schema.result",
59
- tables: await Promise.all(tableNames.map(async (name) => {
60
- const table = schema.getTable(name);
61
- const validatorDesc = describeValidator(table.validator);
62
- const fields = validatorDesc.kind === "object" ? Object.entries(validatorDesc.shape).map(([fieldName, fieldDesc]) => {
63
- const desc = fieldDesc;
64
- const optional = desc.kind === "optional";
65
- return {
66
- name: fieldName,
67
- type: optional ? desc.inner?.kind ?? "any" : desc.kind,
68
- optional
69
- };
70
- }) : [];
71
- fields.unshift({
72
- name: "_id",
73
- type: "string",
74
- optional: false
75
- }, {
76
- name: "_creationTime",
77
- type: "number",
78
- optional: false
79
- });
80
- let documentCount = 0;
81
- try {
82
- const countRow = await driver.get(`SELECT COUNT(*) as count FROM "${name}"`);
83
- if (countRow) documentCount = countRow.count;
84
- } catch {}
85
- return {
86
- name,
87
- fields,
88
- indexes: table.indexes.map((idx) => ({
89
- name: idx.name,
90
- fields: idx.fields,
91
- unique: false
92
- })),
93
- documentCount
94
- };
95
- }))
96
- };
97
- }
98
- case "data.query": try {
99
- let sql = `SELECT _id, _creationTime, _json FROM "${payload.table}"`;
100
- const params = [];
101
- if (payload.filters && payload.filters.length > 0) {
102
- const conditions = payload.filters.map((filter) => {
103
- const op = filterOperatorToSql(filter.operator);
104
- params.push(normalizeFilterValue(filter));
105
- return `json_extract(_json, '$.${filter.field}') ${op} ?`;
106
- });
107
- sql += ` WHERE ${conditions.join(" AND ")}`;
108
- }
109
- sql += " ORDER BY _creationTime DESC";
110
- if (payload.limit) sql += ` LIMIT ${payload.limit}`;
111
- return {
112
- kind: "data.result",
113
- rows: (await driver.all(sql, params)).map((row) => ({
114
- _id: row._id,
115
- _creationTime: row._creationTime,
116
- ...JSON.parse(row._json)
117
- })),
118
- totalCount: (await driver.get(`SELECT COUNT(*) as count FROM "${payload.table}"`))?.count ?? 0
119
- };
120
- } catch (err) {
121
- return {
122
- kind: "error",
123
- message: err instanceof Error ? err.message : String(err)
124
- };
125
- }
126
37
  case "data.insert": try {
127
- const id = generateId();
128
- const now = Date.now();
129
- await driver.run(`INSERT INTO "${payload.table}" (_id, _creationTime, _json) VALUES (?, ?, ?)`, [
130
- id,
131
- now,
132
- JSON.stringify(payload.document)
133
- ]);
134
38
  return {
135
39
  kind: "data.mutate.result",
136
40
  success: true,
137
- id
41
+ id: await runDevtoolsMutation(runtime, async (ctx) => ctx.db.insert(payload.table, payload.document), { origin: "dashboard" })
138
42
  };
139
- } catch (err) {
43
+ } catch (error) {
140
44
  return {
141
45
  kind: "data.mutate.result",
142
46
  success: false,
143
- error: err instanceof Error ? err.message : String(err)
47
+ error: error instanceof Error ? error.message : String(error)
144
48
  };
145
49
  }
146
50
  case "data.patch": try {
147
- const existing = await driver.get(`SELECT _json FROM "${payload.table}" WHERE _id = ?`, [payload.id]);
148
- if (!existing) return {
149
- kind: "data.mutate.result",
150
- success: false,
151
- error: `Document ${payload.id} not found`
152
- };
153
- const doc = {
154
- ...JSON.parse(existing._json),
155
- ...payload.fields
156
- };
157
- await driver.run(`UPDATE "${payload.table}" SET _json = ? WHERE _id = ?`, [JSON.stringify(doc), payload.id]);
51
+ await runDevtoolsMutation(runtime, async (ctx) => {
52
+ await ctx.db.patch(payload.table, payload.id, payload.fields);
53
+ return null;
54
+ }, { origin: "dashboard" });
158
55
  return {
159
56
  kind: "data.mutate.result",
160
57
  success: true,
161
58
  id: payload.id
162
59
  };
163
- } catch (err) {
60
+ } catch (error) {
164
61
  return {
165
62
  kind: "data.mutate.result",
166
63
  success: false,
167
- error: err instanceof Error ? err.message : String(err)
64
+ error: error instanceof Error ? error.message : String(error)
168
65
  };
169
66
  }
170
67
  case "data.delete": try {
171
- await driver.run(`DELETE FROM "${payload.table}" WHERE _id = ?`, [payload.id]);
68
+ await runDevtoolsMutation(runtime, async (ctx) => {
69
+ await ctx.db.delete(payload.table, payload.id);
70
+ return null;
71
+ }, { origin: "dashboard" });
172
72
  return {
173
73
  kind: "data.mutate.result",
174
74
  success: true
175
75
  };
176
- } catch (err) {
76
+ } catch (error) {
177
77
  return {
178
78
  kind: "data.mutate.result",
179
79
  success: false,
180
- error: err instanceof Error ? err.message : String(err)
80
+ error: error instanceof Error ? error.message : String(error)
181
81
  };
182
82
  }
183
- case "sql.execute": try {
184
- const trimmed = payload.query.trim().toUpperCase();
185
- if (trimmed.startsWith("SELECT") || trimmed.startsWith("PRAGMA") || trimmed.startsWith("EXPLAIN")) {
186
- const rows = await driver.all(payload.query);
187
- const columns = rows.length > 0 ? Object.keys(rows[0]) : [];
188
- return {
189
- kind: "sql.result",
190
- columns,
191
- rows: rows.map((row) => columns.map((col) => row[col])),
192
- rowsAffected: 0
193
- };
194
- }
83
+ case "sql.read": try {
84
+ const sqlSupport = requireDevtoolsSqlSupport(sql);
85
+ const databasePath = runtime.getDriverDatabasePath();
86
+ if (!databasePath) throw new Error("SQL Read requires a file-backed database path.");
87
+ const { columns, rows } = sqlSupport.runReadonlyQuery(databasePath, payload.query);
195
88
  return {
196
- kind: "sql.result",
197
- columns: [],
198
- rows: [],
199
- rowsAffected: (await driver.run(payload.query)).changes
89
+ kind: "sql.read.result",
90
+ columns,
91
+ rows
200
92
  };
201
- } catch (err) {
93
+ } catch (error) {
202
94
  return {
203
- kind: "sql.result",
95
+ kind: "sql.read.result",
204
96
  columns: [],
205
97
  rows: [],
206
- rowsAffected: 0,
207
- error: err instanceof Error ? err.message : String(err)
98
+ error: error instanceof Error ? error.message : String(error)
208
99
  };
209
100
  }
210
- case "scheduler.list": try {
101
+ case "sql.write": try {
102
+ const analysis = requireDevtoolsSqlSupport(sql).analyzeSqlStatement(payload.query);
103
+ if (analysis.mode === "read") throw new Error("Use SQL Read or SQL Live for read-only statements.");
104
+ const result = await driver.run(payload.query);
105
+ runtime.notifyDevtoolsScopes(analysis.observedScopes);
106
+ await runtime.forceRefreshDevtools("SQL write executed from devtools dashboard.", { origin: "dashboard" });
211
107
  return {
212
- kind: "scheduler.list.result",
213
- jobs: (await driver.all(`SELECT * FROM "_scheduled_functions" ORDER BY run_at DESC LIMIT 200`)).map((row) => {
214
- const job = {
215
- id: row.id,
216
- functionName: row.function_name,
217
- args: JSON.parse(row.args_json),
218
- scheduledAt: row.created_at,
219
- runAt: row.run_at,
220
- status: mapJobStatus(row.status)
221
- };
222
- if (row.schedule_json) try {
223
- const schedule = JSON.parse(row.schedule_json);
224
- if (schedule.cron) job.cronSchedule = schedule.cron;
225
- } catch {}
226
- if (row.status === "completed" || row.status === "failed") job.completedAt = row.updated_at;
227
- return job;
228
- })
108
+ kind: "sql.write.result",
109
+ rowsAffected: result.changes,
110
+ invalidationScopes: [...analysis.observedScopes]
229
111
  };
230
- } catch {
112
+ } catch (error) {
231
113
  return {
232
- kind: "scheduler.list.result",
233
- jobs: []
114
+ kind: "sql.write.result",
115
+ rowsAffected: 0,
116
+ invalidationScopes: [],
117
+ error: error instanceof Error ? error.message : String(error)
234
118
  };
235
119
  }
236
120
  case "scheduler.cancel": try {
237
- await driver.run(`UPDATE "_scheduled_functions" SET status = 'cancelled', updated_at = ? WHERE id = ? AND status = 'scheduled'`, [Date.now(), payload.jobId]);
238
121
  return {
239
122
  kind: "scheduler.cancel.result",
240
- success: true
123
+ success: true,
124
+ cancelled: await runtime.cancelScheduledJob(payload.jobId)
241
125
  };
242
- } catch (err) {
126
+ } catch (error) {
243
127
  return {
244
128
  kind: "scheduler.cancel.result",
245
129
  success: false,
246
- error: err instanceof Error ? err.message : String(err)
130
+ cancelled: false,
131
+ error: error instanceof Error ? error.message : String(error)
132
+ };
133
+ }
134
+ case "scheduler.update": try {
135
+ const updated = await runtime.updateScheduledJob({
136
+ id: payload.jobId,
137
+ schedule: payload.schedule,
138
+ args: payload.args,
139
+ misfirePolicy: payload.misfirePolicy,
140
+ ...payload.runAt !== void 0 ? { runAt: payload.runAt } : {}
141
+ });
142
+ const updatedJob = (updated ? await listSchedulerJobs(driver) : []).find((job) => job.id === payload.jobId);
143
+ return {
144
+ kind: "scheduler.update.result",
145
+ success: true,
146
+ updated,
147
+ ...updated && updatedJob ? { job: updatedJob } : {}
148
+ };
149
+ } catch (error) {
150
+ return {
151
+ kind: "scheduler.update.result",
152
+ success: false,
153
+ updated: false,
154
+ error: error instanceof Error ? error.message : String(error)
247
155
  };
248
156
  }
249
157
  default: return {
250
158
  kind: "error",
251
- message: `Unknown request kind: ${payload.kind}`
159
+ message: `Unknown devtools command: ${payload.kind}`
160
+ };
161
+ }
162
+ };
163
+ }
164
+ function createDevtoolsSubscriptionHost(deps) {
165
+ const { driver, schema, functions, runtime } = deps;
166
+ const subscriptions = /* @__PURE__ */ new Map();
167
+ const emit = async (payload, listener) => {
168
+ listener(await resolveSubscriptionPayload(payload, {
169
+ driver,
170
+ schema,
171
+ functions,
172
+ runtime,
173
+ ...deps.sql ? { sql: deps.sql } : {}
174
+ }));
175
+ };
176
+ const handleInvalidation = async (scopes) => {
177
+ for (const record of subscriptions.values()) {
178
+ if (!intersects(scopes, record.scopes)) continue;
179
+ await emit(record.payload, record.listener);
180
+ }
181
+ };
182
+ return {
183
+ async subscribe(subscriptionId, payload, listener) {
184
+ if (payload.kind === "fn.watch") {
185
+ const definition = functions[payload.functionName];
186
+ if (!definition || definition.kind !== "query") {
187
+ listener({
188
+ kind: "fn.watch.result",
189
+ error: `Unknown query function: ${payload.functionName}`
190
+ });
191
+ return;
192
+ }
193
+ const watch = runtime.createClient().watchQuery(createFunctionReference("query", payload.functionName), payload.args);
194
+ const emitWatchResult = () => {
195
+ const error = watch.localQueryError();
196
+ listener({
197
+ kind: "fn.watch.result",
198
+ ...error ? { error: error.message } : { result: watch.localQueryResult() }
199
+ });
200
+ };
201
+ const unsubscribeUpdates = watch.onUpdate(emitWatchResult);
202
+ subscriptions.set(subscriptionId, {
203
+ payload,
204
+ listener,
205
+ unsubscribeRuntime: () => {
206
+ unsubscribeUpdates();
207
+ watch.dispose?.();
208
+ },
209
+ scopes: new Set(["all"])
210
+ });
211
+ emitWatchResult();
212
+ return;
213
+ }
214
+ const unsubscribeRuntime = runtime.subscribeToDevtoolsInvalidations((scopes) => {
215
+ handleInvalidation(scopes);
216
+ });
217
+ const unsubscribeEvents = runtime.subscribeToDevtoolsEvents((event) => {
218
+ if (event.type === "runtime.disconnected") emit(payload, listener);
219
+ });
220
+ subscriptions.set(subscriptionId, {
221
+ payload,
222
+ listener,
223
+ unsubscribeRuntime: () => {
224
+ unsubscribeRuntime();
225
+ unsubscribeEvents();
226
+ },
227
+ scopes: scopesForSubscription(payload, deps.sql)
228
+ });
229
+ await emit(payload, listener);
230
+ },
231
+ unsubscribe(subscriptionId) {
232
+ const record = subscriptions.get(subscriptionId);
233
+ if (!record) return;
234
+ record.unsubscribeRuntime();
235
+ subscriptions.delete(subscriptionId);
236
+ },
237
+ dispose() {
238
+ for (const [subscriptionId, record] of subscriptions) {
239
+ record.unsubscribeRuntime();
240
+ subscriptions.delete(subscriptionId);
241
+ }
242
+ }
243
+ };
244
+ }
245
+ async function resolveSubscriptionPayload(payload, deps) {
246
+ const { driver, schema, functions, runtime } = deps;
247
+ await runtime.prepareForDirectAccess();
248
+ switch (payload.kind) {
249
+ case "runtime.summary": return {
250
+ kind: "runtime.summary.result",
251
+ summary: runtime.getRuntimeSummary()
252
+ };
253
+ case "runtime.activeQueries": return {
254
+ kind: "runtime.activeQueries.result",
255
+ activeQueries: runtime.getActiveQueryInfos()
256
+ };
257
+ case "fn.watch": throw new Error("Function watches are pushed incrementally and have no snapshot payload.");
258
+ case "schema.tables": {
259
+ const tables = await getSchemaTables(driver, schema);
260
+ console.debug("[devtools] schema.tables", {
261
+ runtimeId: runtime.getRuntimeId(),
262
+ tables: tables.map((table) => ({
263
+ name: table.name,
264
+ documentCount: table.documentCount
265
+ }))
266
+ });
267
+ return {
268
+ kind: "schema.tables.result",
269
+ tables
270
+ };
271
+ }
272
+ case "data.table": {
273
+ const result = await queryTable(driver, payload.table, payload.filters, payload.limit);
274
+ console.debug("[devtools] data.table", {
275
+ runtimeId: runtime.getRuntimeId(),
276
+ table: payload.table,
277
+ filters: payload.filters ?? [],
278
+ limit: payload.limit,
279
+ totalCount: result.totalCount,
280
+ rowCount: result.rows.length,
281
+ firstRow: result.rows[0] ?? null
282
+ });
283
+ return {
284
+ kind: "data.table.result",
285
+ rows: result.rows,
286
+ totalCount: result.totalCount,
287
+ ...result.cursor ? { cursor: result.cursor } : {}
288
+ };
289
+ }
290
+ case "scheduler.jobs": return {
291
+ kind: "scheduler.jobs.result",
292
+ jobs: await listSchedulerJobs(driver)
293
+ };
294
+ case "functions.catalog": return {
295
+ kind: "functions.catalog.result",
296
+ functions: listFunctions(functions)
297
+ };
298
+ case "sql.watch": {
299
+ const sqlSupport = requireDevtoolsSqlSupport(deps.sql);
300
+ const databasePath = runtime.getDriverDatabasePath();
301
+ if (!databasePath) throw new Error("SQL Live requires a file-backed database path.");
302
+ const { columns, rows, observedTables } = sqlSupport.runReadonlyQuery(databasePath, payload.query);
303
+ return {
304
+ kind: "sql.watch.result",
305
+ columns,
306
+ rows,
307
+ observedTables
252
308
  };
253
309
  }
310
+ }
311
+ }
312
+ async function queryTable(driver, table, filters, limit) {
313
+ let sql = `SELECT _id, _creationTime, _json FROM "${table}"`;
314
+ const params = [];
315
+ if (filters && filters.length > 0) sql += ` WHERE ${filters.map((filter) => {
316
+ params.push(normalizeFilterValue(filter));
317
+ return `json_extract(_json, '$.${filter.field}') ${filterOperatorToSql(filter.operator)} ?`;
318
+ }).join(" AND ")}`;
319
+ sql += " ORDER BY _creationTime DESC";
320
+ if (limit) sql += ` LIMIT ${limit}`;
321
+ return {
322
+ rows: (await driver.all(sql, params)).map((row) => ({
323
+ _id: row._id,
324
+ _creationTime: row._creationTime,
325
+ ...JSON.parse(row._json)
326
+ })),
327
+ totalCount: (await driver.get(`SELECT COUNT(*) as count FROM "${table}"`))?.count ?? 0
254
328
  };
255
329
  }
330
+ async function getSchemaTables(driver, schema) {
331
+ return Promise.all(schema.tableNames().map(async (name) => {
332
+ const table = schema.getTable(name);
333
+ const validatorDesc = describeValidator(table.validator);
334
+ const fields = validatorDesc.kind === "object" ? Object.entries(validatorDesc.shape).map(([fieldName, fieldDesc]) => {
335
+ const desc = fieldDesc;
336
+ const optional = desc.kind === "optional";
337
+ return {
338
+ name: fieldName,
339
+ type: optional ? desc.inner?.kind ?? "any" : desc.kind,
340
+ optional
341
+ };
342
+ }) : [];
343
+ fields.unshift({
344
+ name: "_id",
345
+ type: "string",
346
+ optional: false
347
+ }, {
348
+ name: "_creationTime",
349
+ type: "number",
350
+ optional: false
351
+ });
352
+ let documentCount = 0;
353
+ try {
354
+ documentCount = (await driver.get(`SELECT COUNT(*) as count FROM "${name}"`))?.count ?? 0;
355
+ } catch {
356
+ documentCount = 0;
357
+ }
358
+ return {
359
+ name,
360
+ fields,
361
+ indexes: table.indexes.map((index) => ({
362
+ name: index.name,
363
+ fields: index.fields,
364
+ unique: false
365
+ })),
366
+ documentCount
367
+ };
368
+ }));
369
+ }
370
+ async function listSchedulerJobs(driver) {
371
+ try {
372
+ return (await driver.all(`SELECT * FROM "_scheduled_functions" ORDER BY run_at DESC LIMIT 200`)).map((row) => {
373
+ const schedule = safeReadSchedule(row.schedule_json);
374
+ const scheduleLabel = schedule ? formatScheduleLabel(schedule) : void 0;
375
+ return {
376
+ id: row.id,
377
+ functionName: row.function_name,
378
+ args: JSON.parse(row.args_json),
379
+ scheduledAt: row.created_at,
380
+ runAt: row.run_at,
381
+ status: mapJobStatus(row.status),
382
+ ...row.status === "completed" || row.status === "failed" ? { completedAt: row.updated_at } : {},
383
+ ...row.recurring_name ? { recurringName: row.recurring_name } : {},
384
+ ...schedule ? { schedule } : {},
385
+ ...scheduleLabel ? {
386
+ scheduleLabel,
387
+ cronSchedule: scheduleLabel
388
+ } : {},
389
+ ...row.timezone ? { timezone: row.timezone } : {},
390
+ ...row.last_run_at !== null ? { lastRunAt: row.last_run_at } : {},
391
+ ...row.updated_at ? { updatedAt: row.updated_at } : {},
392
+ misfirePolicy: readMisfirePolicy(row.misfire_policy, row.window_ms)
393
+ };
394
+ });
395
+ } catch {
396
+ return [];
397
+ }
398
+ }
399
+ function listFunctions(functions) {
400
+ return Object.entries(functions).filter((entry) => entry[1] !== void 0).map(([name, fn]) => {
401
+ const descriptor = {
402
+ name,
403
+ type: fn.kind,
404
+ file: inferFileFromFunctionName(name)
405
+ };
406
+ const argsDesc = describeValidator(fn.argsValidator);
407
+ if (argsDesc.kind === "object") descriptor.args = argsDesc.shape;
408
+ return descriptor;
409
+ });
410
+ }
256
411
  function inferFileFromFunctionName(name) {
257
412
  const parts = name.split(":");
258
- if (parts.length > 1) return parts[0] + ".ts";
413
+ if (parts.length > 1) return `${parts[0]}.ts`;
259
414
  return "unknown";
260
415
  }
261
416
  function normalizeFilterValue(filter) {
@@ -265,15 +420,15 @@ function normalizeFilterValue(filter) {
265
420
  default: return filter.value;
266
421
  }
267
422
  }
268
- function filterOperatorToSql(op) {
269
- switch (op) {
423
+ function filterOperatorToSql(operator) {
424
+ switch (operator) {
270
425
  case "eq": return "=";
271
426
  case "neq": return "!=";
272
427
  case "gt": return ">";
273
428
  case "gte": return ">=";
274
429
  case "lt": return "<";
275
430
  case "lte": return "<=";
276
- case "contains": return "LIKE";
431
+ case "contains":
277
432
  case "startsWith": return "LIKE";
278
433
  default: return "=";
279
434
  }
@@ -288,13 +443,73 @@ function mapJobStatus(status) {
288
443
  default: return "pending";
289
444
  }
290
445
  }
291
- function generateId() {
292
- const chars = "0123456789abcdefghijklmnopqrstuvwxyz";
293
- let id = "";
294
- for (let i = 0; i < 24; i++) id += chars[Math.floor(Math.random() * 36)];
295
- return id;
446
+ function safeReadSchedule(scheduleJson) {
447
+ if (!scheduleJson) return;
448
+ try {
449
+ return JSON.parse(scheduleJson);
450
+ } catch {
451
+ return;
452
+ }
453
+ }
454
+ function formatScheduleLabel(schedule) {
455
+ switch (schedule.type) {
456
+ case "interval": {
457
+ const parts = [];
458
+ if (schedule.hours) parts.push(`${schedule.hours}h`);
459
+ if (schedule.minutes) parts.push(`${schedule.minutes}m`);
460
+ if (schedule.seconds) parts.push(`${schedule.seconds}s`);
461
+ return parts.length > 0 ? `Every ${parts.join(" ")}` : "Recurring";
462
+ }
463
+ case "daily": return `Daily ${padNumber(schedule.hour)}:${padNumber(schedule.minute)}${schedule.timezone ? ` ${schedule.timezone}` : ""}`;
464
+ case "weekly": return `Weekly ${capitalize(schedule.dayOfWeek)} ${padNumber(schedule.hour)}:${padNumber(schedule.minute)}${schedule.timezone ? ` ${schedule.timezone}` : ""}`;
465
+ }
466
+ }
467
+ function readMisfirePolicy(type, windowMs) {
468
+ if (type === "windowed") return {
469
+ type,
470
+ windowMs: windowMs ?? 0
471
+ };
472
+ if (type === "skip" || type === "run_once_if_missed") return { type };
473
+ return { type: "catch_up" };
474
+ }
475
+ function padNumber(value) {
476
+ return String(value).padStart(2, "0");
477
+ }
478
+ function capitalize(value) {
479
+ return value.slice(0, 1).toUpperCase() + value.slice(1);
480
+ }
481
+ async function runDevtoolsMutation(runtime, callback, meta) {
482
+ return runtime.runDevtoolsMutation(callback, meta);
483
+ }
484
+ function scopesForSubscription(payload, sql) {
485
+ switch (payload.kind) {
486
+ case "runtime.summary": return new Set(["runtime.summary"]);
487
+ case "runtime.activeQueries": return new Set(["runtime.activeQueries"]);
488
+ case "fn.watch": return new Set(["all"]);
489
+ case "schema.tables": return new Set(["schema.tables"]);
490
+ case "data.table": return new Set([`table:${payload.table}`]);
491
+ case "scheduler.jobs": return new Set(["scheduler.jobs"]);
492
+ case "functions.catalog": return new Set(["all"]);
493
+ case "sql.watch": try {
494
+ const sqlSupport = requireDevtoolsSqlSupport(sql);
495
+ const analysis = sqlSupport.analyzeSqlStatement(payload.query);
496
+ sqlSupport.ensureSqlMode(analysis, "watch");
497
+ return new Set(analysis.observedScopes);
498
+ } catch {
499
+ return new Set(["all"]);
500
+ }
501
+ }
502
+ }
503
+ function intersects(a, b) {
504
+ if (a.has("all") || b.has("all")) return true;
505
+ for (const value of a) if (b.has(value)) return true;
506
+ return false;
507
+ }
508
+ function requireDevtoolsSqlSupport(sql) {
509
+ if (!sql) throw new Error("SQL devtools are only available in Node-hosted runtimes.");
510
+ return sql;
296
511
  }
297
512
  //#endregion
298
- export { createDevtoolsRequestHandler };
513
+ export { createDevtoolsCommandHandler, createDevtoolsSubscriptionHost };
299
514
 
300
515
  //# sourceMappingURL=devtools.mjs.map