@zveltio/sdk 2.0.7 → 3.0.0-beta.1

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 (64) hide show
  1. package/dist/build/index.d.ts +77 -0
  2. package/dist/build/index.d.ts.map +1 -0
  3. package/dist/build/index.js +157 -0
  4. package/dist/client/QueryBuilder.d.ts.map +1 -1
  5. package/dist/client/QueryBuilder.js +12 -3
  6. package/dist/client/RealtimeClient.d.ts.map +1 -1
  7. package/dist/client/RealtimeClient.js +3 -1
  8. package/dist/client/ZveltioClient.d.ts.map +1 -1
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js +16 -5
  11. package/dist/core.d.ts.map +1 -1
  12. package/dist/ddl/index.d.ts +153 -0
  13. package/dist/ddl/index.d.ts.map +1 -0
  14. package/dist/ddl/index.js +33 -0
  15. package/dist/extension/index.d.ts +522 -10
  16. package/dist/extension/index.d.ts.map +1 -1
  17. package/dist/extension/index.js +1 -1
  18. package/dist/extension/permission-gate.d.ts +37 -0
  19. package/dist/extension/permission-gate.d.ts.map +1 -0
  20. package/dist/extension/permission-gate.js +93 -0
  21. package/dist/generate-types.d.ts +37 -0
  22. package/dist/generate-types.d.ts.map +1 -0
  23. package/dist/generate-types.js +157 -0
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/local-store.d.ts.map +1 -1
  27. package/dist/local-store.js +1 -3
  28. package/dist/offline/index.d.ts +105 -0
  29. package/dist/offline/index.d.ts.map +1 -0
  30. package/dist/offline/index.js +235 -0
  31. package/dist/publish/index.d.ts +94 -0
  32. package/dist/publish/index.d.ts.map +1 -0
  33. package/dist/publish/index.js +148 -0
  34. package/dist/realtime.d.ts.map +1 -1
  35. package/dist/realtime.js +6 -2
  36. package/dist/rpc/index.d.ts +68 -0
  37. package/dist/rpc/index.d.ts.map +1 -0
  38. package/dist/rpc/index.js +61 -0
  39. package/dist/schema-codegen.d.ts +60 -0
  40. package/dist/schema-codegen.d.ts.map +1 -0
  41. package/dist/schema-codegen.js +369 -0
  42. package/dist/schema-watcher.d.ts.map +1 -1
  43. package/dist/schema-watcher.js +1 -3
  44. package/dist/studio/index.d.ts +84 -0
  45. package/dist/studio/index.d.ts.map +1 -0
  46. package/dist/studio/index.js +180 -0
  47. package/dist/svelte.d.ts.map +1 -1
  48. package/dist/sync-manager.d.ts.map +1 -1
  49. package/dist/sync-manager.js +4 -11
  50. package/dist/testing/index.d.ts +121 -0
  51. package/dist/testing/index.d.ts.map +1 -0
  52. package/dist/testing/index.js +283 -0
  53. package/dist/testing/with-test-db.d.ts +119 -0
  54. package/dist/testing/with-test-db.d.ts.map +1 -0
  55. package/dist/testing/with-test-db.js +313 -0
  56. package/dist/utils.d.ts.map +1 -1
  57. package/dist/utils.js +3 -1
  58. package/dist/validate/index.d.ts +95 -0
  59. package/dist/validate/index.d.ts.map +1 -0
  60. package/dist/validate/index.js +201 -0
  61. package/dist/validate/migration-parse.d.ts +17 -0
  62. package/dist/validate/migration-parse.d.ts.map +1 -0
  63. package/dist/validate/migration-parse.js +11 -0
  64. package/package.json +53 -1
@@ -0,0 +1,60 @@
1
+ /**
2
+ * SQL → TypeScript codegen for extension-owned tables (S4-01).
3
+ *
4
+ * The aim: extensions' `ctx.db` is currently typed `any`, so a column rename
5
+ * is a silent runtime failure. This module parses an extension's migration
6
+ * SQL files and emits a `.d.ts` describing every table the extension owns,
7
+ * keyed for Kysely. Result: `ctx.db.selectFrom('zv_forms')` autocompletes
8
+ * column names in editors and `tsc` catches typos.
9
+ *
10
+ * Scope intentionally narrow:
11
+ * - Handles `CREATE TABLE [IF NOT EXISTS]` and `ALTER TABLE … ADD COLUMN`.
12
+ * - Maps the common Postgres types we see in extension migrations to TS.
13
+ * - Ignores constraint-only ALTER TABLE statements (PRIMARY KEY, FK).
14
+ * - Ignores CREATE INDEX, CREATE TYPE, CREATE FUNCTION, etc.
15
+ *
16
+ * Anything more exotic falls back to `unknown` so downstream code still
17
+ * compiles. The migration author is free to widen this module's regex when
18
+ * a real case arises.
19
+ */
20
+ export interface Column {
21
+ name: string;
22
+ /** TypeScript type (e.g. 'string', 'number | null', 'Record<string, unknown>'). */
23
+ tsType: string;
24
+ /** The raw Postgres type as parsed — useful for codegen comments. */
25
+ pgType: string;
26
+ nullable: boolean;
27
+ }
28
+ export interface Table {
29
+ name: string;
30
+ columns: Column[];
31
+ }
32
+ export interface ParsedSchema {
33
+ tables: Table[];
34
+ }
35
+ /**
36
+ * Parse a single CREATE TABLE body — the text between the outer parens —
37
+ * into a list of columns. Constraint-only clauses (PRIMARY KEY (…), FOREIGN
38
+ * KEY (…), CHECK (…), UNIQUE (…), CONSTRAINT …) are skipped.
39
+ */
40
+ export declare function parseColumnList(body: string): Column[];
41
+ /**
42
+ * Parse one or more migration SQL strings into a normalized schema. Tables
43
+ * are merged across files: `CREATE TABLE foo (...)` followed by
44
+ * `ALTER TABLE foo ADD COLUMN bar text` produces a single `foo` table with
45
+ * `bar` appended. Last write wins for column re-declarations.
46
+ */
47
+ export declare function parseSchema(sqlChunks: string[]): ParsedSchema;
48
+ export interface CodegenOptions {
49
+ /** Top-level interface name (default: `ExtensionSchema`). */
50
+ interfaceName?: string;
51
+ /** Optional banner line above the file body (e.g. provenance comment). */
52
+ banner?: string;
53
+ }
54
+ /**
55
+ * Emit a Kysely-friendly `.d.ts`-style module body for a parsed schema.
56
+ * The result is plain TypeScript; callers write it wherever (typically
57
+ * `<extension>/.zveltio/db.d.ts`).
58
+ */
59
+ export declare function emitTypeScript(schema: ParsedSchema, opts?: CodegenOptions): string;
60
+ //# sourceMappingURL=schema-codegen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-codegen.d.ts","sourceRoot":"","sources":["../src/schema-codegen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAsMD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA+BtD;AA6BD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,YAAY,CA+C7D;AAID,MAAM,WAAW,cAAc;IAC7B,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,GAAE,cAAmB,GAAG,MAAM,CA0BtF"}
@@ -0,0 +1,369 @@
1
+ /**
2
+ * SQL → TypeScript codegen for extension-owned tables (S4-01).
3
+ *
4
+ * The aim: extensions' `ctx.db` is currently typed `any`, so a column rename
5
+ * is a silent runtime failure. This module parses an extension's migration
6
+ * SQL files and emits a `.d.ts` describing every table the extension owns,
7
+ * keyed for Kysely. Result: `ctx.db.selectFrom('zv_forms')` autocompletes
8
+ * column names in editors and `tsc` catches typos.
9
+ *
10
+ * Scope intentionally narrow:
11
+ * - Handles `CREATE TABLE [IF NOT EXISTS]` and `ALTER TABLE … ADD COLUMN`.
12
+ * - Maps the common Postgres types we see in extension migrations to TS.
13
+ * - Ignores constraint-only ALTER TABLE statements (PRIMARY KEY, FK).
14
+ * - Ignores CREATE INDEX, CREATE TYPE, CREATE FUNCTION, etc.
15
+ *
16
+ * Anything more exotic falls back to `unknown` so downstream code still
17
+ * compiles. The migration author is free to widen this module's regex when
18
+ * a real case arises.
19
+ */
20
+ // ─── Postgres → TypeScript type map ────────────────────────────────────────
21
+ const NUMERIC_TYPES = new Set([
22
+ 'int',
23
+ 'int2',
24
+ 'int4',
25
+ 'int8',
26
+ 'integer',
27
+ 'bigint',
28
+ 'smallint',
29
+ 'serial',
30
+ 'bigserial',
31
+ 'smallserial',
32
+ 'numeric',
33
+ 'decimal',
34
+ 'real',
35
+ 'double precision',
36
+ 'float',
37
+ 'float4',
38
+ 'float8',
39
+ ]);
40
+ const STRING_TYPES = new Set([
41
+ 'text',
42
+ 'varchar',
43
+ 'char',
44
+ 'character',
45
+ 'character varying',
46
+ 'name',
47
+ 'uuid',
48
+ 'citext',
49
+ 'inet',
50
+ 'cidr',
51
+ 'macaddr',
52
+ ]);
53
+ const BOOLEAN_TYPES = new Set(['boolean', 'bool']);
54
+ const DATE_TYPES = new Set([
55
+ 'date',
56
+ 'time',
57
+ 'timetz',
58
+ 'timestamp',
59
+ 'timestamptz',
60
+ 'timestamp without time zone',
61
+ 'timestamp with time zone',
62
+ 'time without time zone',
63
+ 'time with time zone',
64
+ ]);
65
+ const JSON_TYPES = new Set(['json', 'jsonb']);
66
+ const BYTES_TYPES = new Set(['bytea']);
67
+ function mapPgTypeToTs(pgType) {
68
+ // Normalize: lower-case, strip array brackets (handled separately), trim modifiers
69
+ const lower = pgType.toLowerCase().trim();
70
+ const isArray = /\[\]\s*$/.test(lower);
71
+ const base = lower
72
+ .replace(/\[\]\s*$/, '')
73
+ .replace(/\(.*\)/, '')
74
+ .trim();
75
+ let tsBase;
76
+ if (NUMERIC_TYPES.has(base))
77
+ tsBase = 'number';
78
+ else if (BOOLEAN_TYPES.has(base))
79
+ tsBase = 'boolean';
80
+ else if (DATE_TYPES.has(base))
81
+ tsBase = 'Date';
82
+ else if (JSON_TYPES.has(base))
83
+ tsBase = 'Record<string, unknown>';
84
+ else if (BYTES_TYPES.has(base))
85
+ tsBase = 'Uint8Array';
86
+ else if (STRING_TYPES.has(base))
87
+ tsBase = 'string';
88
+ else
89
+ tsBase = 'unknown';
90
+ return isArray ? `${tsBase}[]` : tsBase;
91
+ }
92
+ // ─── SQL parsing ───────────────────────────────────────────────────────────
93
+ /**
94
+ * Strip line comments (`-- …`) and block comments (`/* … * /`) so they don't
95
+ * confuse the table/column regexes. Dollar-quoted blocks are preserved
96
+ * verbatim (function bodies).
97
+ */
98
+ function stripComments(sql) {
99
+ let out = '';
100
+ let i = 0;
101
+ const n = sql.length;
102
+ let inSingle = false;
103
+ let dollarTag = null;
104
+ while (i < n) {
105
+ const ch = sql[i];
106
+ const next = sql[i + 1];
107
+ if (dollarTag !== null) {
108
+ out += ch;
109
+ if (sql.startsWith(dollarTag, i)) {
110
+ out += sql.slice(i + 1, i + dollarTag.length);
111
+ i += dollarTag.length;
112
+ dollarTag = null;
113
+ }
114
+ else {
115
+ i++;
116
+ }
117
+ continue;
118
+ }
119
+ if (inSingle) {
120
+ out += ch;
121
+ if (ch === "'" && next === "'") {
122
+ out += next;
123
+ i += 2;
124
+ }
125
+ else if (ch === "'") {
126
+ inSingle = false;
127
+ i++;
128
+ }
129
+ else
130
+ i++;
131
+ continue;
132
+ }
133
+ if (ch === '-' && next === '-') {
134
+ // line comment until \n
135
+ while (i < n && sql[i] !== '\n')
136
+ i++;
137
+ continue;
138
+ }
139
+ if (ch === '/' && next === '*') {
140
+ i += 2;
141
+ while (i < n && !(sql[i] === '*' && sql[i + 1] === '/'))
142
+ i++;
143
+ i += 2;
144
+ continue;
145
+ }
146
+ if (ch === '$') {
147
+ // detect dollar tag like $$ or $body$
148
+ const m = sql.substring(i).match(/^\$[a-zA-Z_]*\$/);
149
+ if (m) {
150
+ dollarTag = m[0];
151
+ out += dollarTag;
152
+ i += dollarTag.length;
153
+ continue;
154
+ }
155
+ }
156
+ if (ch === "'") {
157
+ inSingle = true;
158
+ out += ch;
159
+ i++;
160
+ continue;
161
+ }
162
+ out += ch;
163
+ i++;
164
+ }
165
+ return out;
166
+ }
167
+ /**
168
+ * Find the matching closing `)` for the `(` at `openIdx`. Returns the index
169
+ * of the closing paren, or -1 if unbalanced. Skips parens inside string
170
+ * literals and dollar-quoted blocks.
171
+ */
172
+ function findMatchingParen(sql, openIdx) {
173
+ let depth = 0;
174
+ let i = openIdx;
175
+ const n = sql.length;
176
+ let inSingle = false;
177
+ let dollarTag = null;
178
+ while (i < n) {
179
+ const ch = sql[i];
180
+ if (dollarTag !== null) {
181
+ if (sql.startsWith(dollarTag, i)) {
182
+ dollarTag = null;
183
+ i += sql.substring(i).match(/^\$[a-zA-Z_]*\$/)[0].length;
184
+ continue;
185
+ }
186
+ i++;
187
+ continue;
188
+ }
189
+ if (inSingle) {
190
+ if (ch === "'" && sql[i + 1] === "'")
191
+ i += 2;
192
+ else if (ch === "'") {
193
+ inSingle = false;
194
+ i++;
195
+ }
196
+ else
197
+ i++;
198
+ continue;
199
+ }
200
+ if (ch === "'") {
201
+ inSingle = true;
202
+ i++;
203
+ continue;
204
+ }
205
+ if (ch === '$') {
206
+ const m = sql.substring(i).match(/^\$[a-zA-Z_]*\$/);
207
+ if (m) {
208
+ dollarTag = m[0];
209
+ i += dollarTag.length;
210
+ continue;
211
+ }
212
+ }
213
+ if (ch === '(')
214
+ depth++;
215
+ else if (ch === ')') {
216
+ depth--;
217
+ if (depth === 0)
218
+ return i;
219
+ }
220
+ i++;
221
+ }
222
+ return -1;
223
+ }
224
+ /**
225
+ * Parse a single CREATE TABLE body — the text between the outer parens —
226
+ * into a list of columns. Constraint-only clauses (PRIMARY KEY (…), FOREIGN
227
+ * KEY (…), CHECK (…), UNIQUE (…), CONSTRAINT …) are skipped.
228
+ */
229
+ export function parseColumnList(body) {
230
+ const columns = [];
231
+ // Split on top-level commas; tracking paren depth so types like `numeric(10,2)`
232
+ // don't confuse us.
233
+ const parts = [];
234
+ let depth = 0;
235
+ let buf = '';
236
+ for (let i = 0; i < body.length; i++) {
237
+ const ch = body[i];
238
+ if (ch === '(')
239
+ depth++;
240
+ else if (ch === ')')
241
+ depth--;
242
+ if (ch === ',' && depth === 0) {
243
+ parts.push(buf);
244
+ buf = '';
245
+ continue;
246
+ }
247
+ buf += ch;
248
+ }
249
+ if (buf.trim())
250
+ parts.push(buf);
251
+ for (const rawPart of parts) {
252
+ const part = rawPart.trim();
253
+ if (!part)
254
+ continue;
255
+ // Skip table-level constraints
256
+ if (/^(constraint|primary\s+key|foreign\s+key|unique|check|exclude|like)\b/i.test(part))
257
+ continue;
258
+ const col = parseColumnDef(part);
259
+ if (col)
260
+ columns.push(col);
261
+ }
262
+ return columns;
263
+ }
264
+ function parseColumnDef(line) {
265
+ // Match: <identifier> <type>(...)? <rest>
266
+ // Identifier can be quoted ("col name") or unquoted (col_name).
267
+ const m = line.match(/^\s*(?:"([^"]+)"|([a-zA-Z_][a-zA-Z0-9_]*))\s+(.+)$/);
268
+ if (!m)
269
+ return null;
270
+ const name = m[1] ?? m[2];
271
+ if (!name || name === '')
272
+ return null;
273
+ // The type can have a length modifier in parens, possibly followed by [] for arrays.
274
+ // Examples: TEXT, VARCHAR(255), NUMERIC(10,2), TIMESTAMPTZ, INT[], GEOGRAPHY(POINT, 4326)
275
+ const rest = m[3];
276
+ const typeMatch = rest.match(/^((?:character\s+varying|character|double\s+precision|timestamp\s+(?:without|with)\s+time\s+zone|time\s+(?:without|with)\s+time\s+zone|[a-zA-Z_][a-zA-Z0-9_]*)(?:\s*\([^)]*\))?(?:\s*\[\s*\])?)\s*(.*)$/i);
277
+ if (!typeMatch)
278
+ return null;
279
+ const pgType = typeMatch[1].trim();
280
+ const tail = typeMatch[2] ?? '';
281
+ const upperTail = ' ' + tail.toUpperCase() + ' ';
282
+ // NOT NULL ⇒ non-nullable. PRIMARY KEY also implies NOT NULL.
283
+ const notNull = / NOT NULL /.test(upperTail) || / PRIMARY KEY /.test(upperTail);
284
+ const tsBase = mapPgTypeToTs(pgType);
285
+ const tsType = notNull ? tsBase : `${tsBase} | null`;
286
+ return { name, pgType, tsType, nullable: !notNull };
287
+ }
288
+ /**
289
+ * Parse one or more migration SQL strings into a normalized schema. Tables
290
+ * are merged across files: `CREATE TABLE foo (...)` followed by
291
+ * `ALTER TABLE foo ADD COLUMN bar text` produces a single `foo` table with
292
+ * `bar` appended. Last write wins for column re-declarations.
293
+ */
294
+ export function parseSchema(sqlChunks) {
295
+ const tables = new Map();
296
+ const order = [];
297
+ const ensureTable = (name) => {
298
+ if (!tables.has(name)) {
299
+ tables.set(name, new Map());
300
+ order.push(name);
301
+ }
302
+ return tables.get(name);
303
+ };
304
+ const allSql = sqlChunks.map(stripComments).join('\n');
305
+ // CREATE TABLE [IF NOT EXISTS] <name> ( <body> )
306
+ const createRe = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:"([^"]+)"|([a-zA-Z_][a-zA-Z0-9_]*))\s*\(/gi;
307
+ let m;
308
+ while ((m = createRe.exec(allSql)) !== null) {
309
+ const tableName = m[1] ?? m[2];
310
+ const openIdx = createRe.lastIndex - 1; // position of '('
311
+ const closeIdx = findMatchingParen(allSql, openIdx);
312
+ if (closeIdx < 0)
313
+ continue; // unbalanced — skip
314
+ const body = allSql.substring(openIdx + 1, closeIdx);
315
+ const columns = parseColumnList(body);
316
+ const tableCols = ensureTable(tableName);
317
+ for (const c of columns)
318
+ tableCols.set(c.name, c);
319
+ createRe.lastIndex = closeIdx + 1;
320
+ }
321
+ // ALTER TABLE <name> ADD COLUMN [IF NOT EXISTS] <col> <type> <rest>
322
+ const alterRe = /ALTER\s+TABLE\s+(?:"([^"]+)"|([a-zA-Z_][a-zA-Z0-9_]*))\s+ADD\s+COLUMN\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:"([^"]+)"|([a-zA-Z_][a-zA-Z0-9_]*))\s+([^;]+?)(?=;|$)/gi;
323
+ while ((m = alterRe.exec(allSql)) !== null) {
324
+ const tableName = m[1] ?? m[2];
325
+ const colName = m[3] ?? m[4];
326
+ const tail = m[5];
327
+ const col = parseColumnDef(`${colName} ${tail}`);
328
+ if (col) {
329
+ const tableCols = ensureTable(tableName);
330
+ tableCols.set(col.name, col);
331
+ }
332
+ }
333
+ return {
334
+ tables: order.map((name) => ({ name, columns: [...tables.get(name).values()] })),
335
+ };
336
+ }
337
+ /**
338
+ * Emit a Kysely-friendly `.d.ts`-style module body for a parsed schema.
339
+ * The result is plain TypeScript; callers write it wherever (typically
340
+ * `<extension>/.zveltio/db.d.ts`).
341
+ */
342
+ export function emitTypeScript(schema, opts = {}) {
343
+ const interfaceName = opts.interfaceName ?? 'ExtensionSchema';
344
+ const lines = [];
345
+ lines.push('// AUTO-GENERATED by zveltio CLI — do NOT edit by hand.');
346
+ lines.push('// Regenerate with: zveltio extension types');
347
+ if (opts.banner)
348
+ lines.push(`// ${opts.banner}`);
349
+ lines.push('');
350
+ lines.push('/* eslint-disable */');
351
+ lines.push('');
352
+ lines.push(`export interface ${interfaceName} {`);
353
+ if (schema.tables.length === 0) {
354
+ lines.push(' // No tables parsed from migrations.');
355
+ }
356
+ for (const t of schema.tables) {
357
+ lines.push(` ${quoteIdentifierIfNeeded(t.name)}: {`);
358
+ for (const c of t.columns) {
359
+ lines.push(` ${quoteIdentifierIfNeeded(c.name)}: ${c.tsType}; // ${c.pgType}`);
360
+ }
361
+ lines.push(' };');
362
+ }
363
+ lines.push('}');
364
+ lines.push('');
365
+ return lines.join('\n');
366
+ }
367
+ function quoteIdentifierIfNeeded(name) {
368
+ return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name) ? name : `'${name.replace(/'/g, "\\'")}'`;
369
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"schema-watcher.d.ts","sourceRoot":"","sources":["../src/schema-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IACrD,kCAAkC;IAClC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAChC;AA4GD;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,MAAM,IAAI,CAAC,CAyErB;AAED,iDAAiD;AACjD,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAKf"}
1
+ {"version":3,"file":"schema-watcher.d.ts","sourceRoot":"","sources":["../src/schema-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IACrD,kCAAkC;IAClC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAChC;AA0GD;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,MAAM,IAAI,CAAC,CAyErB;AAED,iDAAiD;AACjD,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAKf"}
@@ -62,9 +62,7 @@ function generateTypesFile(collections) {
62
62
  `;
63
63
  const interfaces = collections.map(collectionToInterface).join('\n\n');
64
64
  const collectionNames = collections.map((c) => `'${c.name}'`).join(' | ');
65
- const collectionMap = collections
66
- .map((c) => ` ${c.name}: ${toPascalCase(c.name)};`)
67
- .join('\n');
65
+ const collectionMap = collections.map((c) => ` ${c.name}: ${toPascalCase(c.name)};`).join('\n');
68
66
  const clientType = `
69
67
  export type CollectionName = ${collectionNames || 'never'};
70
68
 
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Studio SDK surface (S3-02 + S3-03 + early S4-07 prep).
3
+ *
4
+ * Pure helpers for Studio extension authors:
5
+ * - `makeFormProxy` / `applyFormAlterHooks` — form-alter pipeline that
6
+ * `<Form>` renderers in core Studio call before render. Hooks add
7
+ * fields, hide fields, reorder, attach validators.
8
+ * - `sortSlotContributions` — stable priority-asc sort used by
9
+ * `<Slot name>` to order extension contributions.
10
+ *
11
+ * The reactive registries themselves (Svelte 5 `$state`) live in
12
+ * `packages/studio/src/lib/extension-api.svelte.ts`. They import this
13
+ * module so the algorithm is one source of truth, while the studio side
14
+ * owns the runtime state.
15
+ *
16
+ * Exported via `@zveltio/sdk/studio`. Extension Svelte bundles can
17
+ * import-and-call too — useful for advanced authors who want to apply
18
+ * alters against their own custom forms.
19
+ */
20
+ import type { FormAlterAPI, FormAlterHook, FormSchema, SlotContribution, StudioRoute, StudioFieldType, AssetPreviewHandler } from '../extension/index.js';
21
+ interface FormProxy extends FormAlterAPI {
22
+ /** Commit the mutated schema after all hooks ran. */
23
+ commit(): FormSchema;
24
+ }
25
+ export declare function makeFormProxy(initial: FormSchema): FormProxy;
26
+ /**
27
+ * Run every registered hook against the schema and return the result.
28
+ * Exception-safe: a throwing hook is logged and skipped, the rest still run.
29
+ */
30
+ export declare function applyFormAlterHooks(hooks: FormAlterHook[], schema: FormSchema, ctx?: Record<string, unknown>): FormSchema;
31
+ /**
32
+ * Sort slot contributions deterministically: priority asc (lower first),
33
+ * then registration order (stable sort).
34
+ */
35
+ export declare function sortSlotContributions<T extends {
36
+ priority?: number;
37
+ }>(items: T[]): T[];
38
+ /** Register a top-level Studio page contributed by this extension. */
39
+ export declare function registerRoute(route: StudioRoute): void;
40
+ /**
41
+ * Register a Svelte component into a named composition slot (S3-03).
42
+ *
43
+ * @example
44
+ * import { registerSlot } from '@zveltio/sdk/studio';
45
+ * import RevenueWidget from './widgets/RevenueWidget.svelte';
46
+ *
47
+ * registerSlot('dashboard.widgets', {
48
+ * component: RevenueWidget,
49
+ * priority: 10,
50
+ * visible: (ctx) => (ctx.user as any)?.roles?.includes('finance'),
51
+ * });
52
+ */
53
+ export declare function registerSlot(name: string, contribution: SlotContribution): void;
54
+ /**
55
+ * Register a Drupal-style form-alter hook (S3-02).
56
+ *
57
+ * @example
58
+ * import { registerFormAlter } from '@zveltio/sdk/studio';
59
+ *
60
+ * registerFormAlter('core:user-edit', (form) => {
61
+ * form.addField({ after: 'email', field: { name: 'phone', type: 'tel' } });
62
+ * form.hideField('legacy_pin');
63
+ * });
64
+ */
65
+ export declare function registerFormAlter(formId: string, hook: FormAlterHook): void;
66
+ /** Register a custom Studio field type contributed by this extension. */
67
+ export declare function registerFieldType(ft: StudioFieldType): void;
68
+ /**
69
+ * Register a custom preview handler for asset URLs / MIME types.
70
+ *
71
+ * @example
72
+ * import { registerAssetPreview } from '@zveltio/sdk/studio';
73
+ * import PdfPreview from './PdfPreview.svelte';
74
+ *
75
+ * registerAssetPreview({
76
+ * match: (a) => a.mimeType === 'application/pdf',
77
+ * component: PdfPreview,
78
+ * });
79
+ */
80
+ export declare function registerAssetPreview(handler: AssetPreviewHandler): void;
81
+ /** Engine base URL — pre-installed by Studio for cross-origin fetches. */
82
+ export declare function getEngineUrl(): string;
83
+ export type { StudioExtensionAPI, StudioRoute, StudioFieldType, AssetPreviewHandler, SlotContribution, FormAlterHook, FormAlterAPI, FormSchema, FormField, } from '../extension/index.js';
84
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/studio/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,UAAU,EAEV,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAE/B,UAAU,SAAU,SAAQ,YAAY;IACtC,qDAAqD;IACrD,MAAM,IAAI,UAAU,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,SAAS,CAoE5D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,aAAa,EAAE,EACtB,MAAM,EAAE,UAAU,EAClB,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAChC,UAAU,CAWZ;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAGtF;AAoCD,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAEtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,gBAAgB,GAAG,IAAI,CAE/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI,CAE3E;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,CAE3D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAEvE;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAID,YAAY,EACV,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,UAAU,EACV,SAAS,GACV,MAAM,uBAAuB,CAAC"}