erdraft 0.0.0-stage → 0.0.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 (34) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +204 -2
  3. package/dist/chunk-SSLBX2OF.js +2162 -0
  4. package/dist/chunk-VSN3XJPO.js +99 -0
  5. package/dist/cli.js +507 -0
  6. package/dist/inspect/index.js +373 -0
  7. package/dist/inspect/logger.js +64 -0
  8. package/dist/read-schema-child.js +238 -0
  9. package/dist/studio-config-TIVT4MNY.js +32 -0
  10. package/dist/types/core/fields.d.ts +39 -0
  11. package/dist/types/inspect/field-of.d.ts +3 -0
  12. package/dist/types/inspect/holds-value.d.ts +2 -0
  13. package/dist/types/inspect/inspect-logger.d.ts +2 -0
  14. package/dist/types/inspect/inspector.d.ts +5 -0
  15. package/dist/types/inspect/listen-on-document.d.ts +5 -0
  16. package/dist/types/inspect/read-write.d.ts +5 -0
  17. package/dist/types/inspect/use-field-changes.d.ts +6 -0
  18. package/dist/types/inspect/watch-storage.d.ts +8 -0
  19. package/dist/window/apple-touch-icon.png +0 -0
  20. package/dist/window/assets/index-B5sqm8jk.css +2 -0
  21. package/dist/window/assets/index-Dk6ZQwtE.js +132 -0
  22. package/dist/window/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  23. package/dist/window/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  24. package/dist/window/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  25. package/dist/window/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  26. package/dist/window/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  27. package/dist/window/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  28. package/dist/window/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  29. package/dist/window/favicon.ico +0 -0
  30. package/dist/window/favicon.png +0 -0
  31. package/dist/window/favicon.svg +1 -0
  32. package/dist/window/index.html +23 -0
  33. package/package.json +91 -3
  34. package/skill/SKILL.md +47 -0
@@ -0,0 +1,373 @@
1
+ 'use client'
2
+
3
+ // src/inspect/inspector.tsx
4
+ import { z as z2 } from "zod";
5
+ import {
6
+ useRef as useRef2,
7
+ useState,
8
+ useEffect as useEffect2,
9
+ useEffectEvent
10
+ } from "react";
11
+
12
+ // src/core/fields.ts
13
+ import { z } from "zod";
14
+ var STORAGE_AREAS = ["local", "session"];
15
+ var destinationSchema = z.discriminatedUnion("kind", [
16
+ z.object({
17
+ kind: z.literal("column"),
18
+ table: z.string(),
19
+ column: z.string()
20
+ }),
21
+ z.object({
22
+ kind: z.literal("storage"),
23
+ area: z.enum(STORAGE_AREAS),
24
+ key: z.string()
25
+ }),
26
+ z.object({ kind: z.literal("none") })
27
+ ]);
28
+ var fieldSchema = z.object({
29
+ page: z.string().min(1),
30
+ label: z.string().min(1),
31
+ destination: destinationSchema,
32
+ tracedAt: z.string()
33
+ });
34
+ function describeDestination(destination) {
35
+ if (destination.kind === "column") {
36
+ return `${destination.table}.${destination.column}`;
37
+ }
38
+ if (destination.kind === "storage") {
39
+ return `${destination.area}Storage "${destination.key}"`;
40
+ }
41
+ return "kept nowhere since it changed: not saved yet, or kept on the page only";
42
+ }
43
+
44
+ // src/inspect/use-field-changes.ts
45
+ import { useRef, useEffect } from "react";
46
+
47
+ // src/inspect/field-of.ts
48
+ var FIELD = 'input, textarea, select, [role="radiogroup"]';
49
+ var UNTRACED = /* @__PURE__ */ new Set([
50
+ "password",
51
+ "hidden",
52
+ "button",
53
+ "submit",
54
+ "reset",
55
+ "checkbox",
56
+ "radio",
57
+ "file",
58
+ "image"
59
+ ]);
60
+ function fieldOf(element) {
61
+ if (!(element instanceof Element)) {
62
+ return null;
63
+ }
64
+ const field = element.closest(FIELD);
65
+ return field instanceof HTMLInputElement && UNTRACED.has(field.type) ? null : field;
66
+ }
67
+ function textOf(ids) {
68
+ return ids.split(/\s+/).map((id) => document.getElementById(id)?.textContent?.trim() ?? "").filter(Boolean).join(" ");
69
+ }
70
+ function labelOf(field) {
71
+ const labelled = field.getAttribute("aria-labelledby");
72
+ const own = field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement ? field.labels?.[0]?.textContent : null;
73
+ return field.getAttribute("aria-label") || (labelled ? textOf(labelled) : "") || own?.trim() || field.getAttribute("placeholder") || field.getAttribute("name") || "";
74
+ }
75
+ function valueIn(field) {
76
+ if (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement) {
77
+ return field.value;
78
+ }
79
+ const checked = field.querySelector('[role="radio"][aria-checked="true"]');
80
+ return checked?.getAttribute("value") ?? "";
81
+ }
82
+
83
+ // src/inspect/listen-on-document.ts
84
+ function listenOnDocument(listeners) {
85
+ for (const [type, listener] of listeners) {
86
+ document.addEventListener(type, listener, true);
87
+ }
88
+ return () => {
89
+ for (const [type, listener] of listeners) {
90
+ document.removeEventListener(type, listener, true);
91
+ }
92
+ };
93
+ }
94
+
95
+ // src/inspect/use-field-changes.ts
96
+ function changeKey(label) {
97
+ return `${window.location.pathname}|${label}`;
98
+ }
99
+ function useFieldChanges() {
100
+ const changes = useRef(/* @__PURE__ */ new Map());
101
+ useEffect(() => {
102
+ let pressed = null;
103
+ const record = (field) => {
104
+ const label = labelOf(field);
105
+ if (label) {
106
+ changes.current.set(changeKey(label), {
107
+ value: valueIn(field),
108
+ at: Date.now()
109
+ });
110
+ }
111
+ };
112
+ const onInput = (event) => {
113
+ const field = fieldOf(event.target);
114
+ if (field) {
115
+ record(field);
116
+ }
117
+ };
118
+ const onPress = (event) => {
119
+ const field = fieldOf(event.target);
120
+ pressed = field?.getAttribute("role") === "radiogroup" ? { field, value: valueIn(field) } : null;
121
+ };
122
+ const onPressed = () => {
123
+ const before = pressed;
124
+ pressed = null;
125
+ if (before) {
126
+ setTimeout(() => {
127
+ if (valueIn(before.field) !== before.value) {
128
+ record(before.field);
129
+ }
130
+ });
131
+ }
132
+ };
133
+ return listenOnDocument([
134
+ ["input", onInput],
135
+ ["change", onInput],
136
+ ["pointerdown", onPress],
137
+ ["keydown", onPress],
138
+ ["click", onPressed],
139
+ ["keyup", onPressed]
140
+ ]);
141
+ }, []);
142
+ return changes;
143
+ }
144
+
145
+ // src/inspect/watch-storage.ts
146
+ function watchStorage(onWrite) {
147
+ const prototype = Storage.prototype;
148
+ const setItem = prototype.setItem;
149
+ prototype.setItem = function watchedSetItem(key, value) {
150
+ setItem.call(this, key, value);
151
+ onWrite({
152
+ area: this === window.sessionStorage ? "session" : "local",
153
+ key,
154
+ value: String(value),
155
+ at: Date.now()
156
+ });
157
+ };
158
+ return () => {
159
+ prototype.setItem = setItem;
160
+ };
161
+ }
162
+
163
+ // src/inspect/holds-value.ts
164
+ var SHORTEST = 2;
165
+ var WITHIN = 4;
166
+ var DIGITS = 4;
167
+ function digitsOf(value) {
168
+ return value.replace(/\D/g, "");
169
+ }
170
+ function holdsValue(kept, typed) {
171
+ const value = typed.trim().toLowerCase();
172
+ if (value.length < SHORTEST || kept === null || kept === void 0) {
173
+ return false;
174
+ }
175
+ const text = (typeof kept === "string" ? kept : JSON.stringify(kept)).toLowerCase();
176
+ const digits = digitsOf(value);
177
+ return text.trim() === value || digits.length >= DIGITS && digits === digitsOf(text) || value.length >= WITHIN && text.includes(value);
178
+ }
179
+
180
+ // src/inspect/inspector.tsx
181
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
182
+ var KEPT_STORAGE = 200;
183
+ var fieldsResponse = z2.object({ fields: z2.array(fieldSchema) });
184
+ var columnsResponse = z2.object({
185
+ columns: z2.array(z2.object({ table: z2.string(), column: z2.string() }))
186
+ });
187
+ var keptResponse = z2.object({ field: fieldSchema });
188
+ var LAYER = 2147483647;
189
+ var INK = "#f5f5f4";
190
+ var GROUND = "rgba(24, 24, 27, 0.94)";
191
+ var FOCUS = "#3b82f6";
192
+ var TEXT = {
193
+ font: "500 12px/16px ui-sans-serif, system-ui, sans-serif",
194
+ color: INK,
195
+ background: GROUND,
196
+ borderRadius: 6,
197
+ padding: "4px 8px",
198
+ boxShadow: "0 4px 16px rgba(0, 0, 0, 0.24)",
199
+ whiteSpace: "nowrap",
200
+ pointerEvents: "none",
201
+ position: "fixed",
202
+ zIndex: LAYER
203
+ };
204
+ function Inspector({ url }) {
205
+ const changes = useFieldChanges();
206
+ const storage = useRef2([]);
207
+ const hovered = useRef2(null);
208
+ const [active, setActive] = useState(false);
209
+ const [box, setBox] = useState(
210
+ null
211
+ );
212
+ const [said, setSaid] = useState(null);
213
+ const [known, setKnown] = useState([]);
214
+ useEffect2(
215
+ () => watchStorage((write) => {
216
+ storage.current = [...storage.current.slice(1 - KEPT_STORAGE), write];
217
+ }),
218
+ []
219
+ );
220
+ useEffect2(() => {
221
+ const onKey = (event) => {
222
+ if (event.code === "KeyD" && event.altKey && event.shiftKey) {
223
+ event.preventDefault();
224
+ setActive((was) => !was);
225
+ } else if (event.key === "Escape") {
226
+ setActive(false);
227
+ }
228
+ };
229
+ window.addEventListener("keydown", onKey);
230
+ return () => window.removeEventListener("keydown", onKey);
231
+ }, []);
232
+ const say = (field, text2) => setSaid({ field, text: text2 });
233
+ const trace = useEffectEvent(async (field) => {
234
+ const label2 = labelOf(field);
235
+ if (!label2) {
236
+ say(field, "This field has no label to be known by");
237
+ return;
238
+ }
239
+ const change = changes.current.get(changeKey(label2));
240
+ const value = change?.value ?? valueIn(field);
241
+ const since = change?.at ?? Math.floor(performance.timeOrigin);
242
+ if (value.trim().length < SHORTEST) {
243
+ say(field, `${label2}: type a longer value and save, then click it again`);
244
+ return;
245
+ }
246
+ say(field, `${label2}: tracing\u2026`);
247
+ try {
248
+ const found = columnsResponse.parse(
249
+ await (await fetch(
250
+ `${url}/api/inspect/find?value=${encodeURIComponent(value)}&since=${since}`
251
+ )).json()
252
+ ).columns;
253
+ const stored = storage.current.filter((write) => write.at >= since && holdsValue(write.value, value)).at(-1);
254
+ const [column] = found;
255
+ const destination = column ? { kind: "column", ...column } : stored ? { kind: "storage", area: stored.area, key: stored.key } : change ? { kind: "none" } : null;
256
+ if (!destination) {
257
+ say(field, `${label2}: change it and save, then click it again`);
258
+ return;
259
+ }
260
+ const kept = keptResponse.parse(
261
+ await (await fetch(`${url}/api/inspect/fields`, {
262
+ method: "POST",
263
+ headers: { "content-type": "application/json" },
264
+ body: JSON.stringify({
265
+ page: window.location.pathname,
266
+ label: label2,
267
+ destination
268
+ })
269
+ })).json()
270
+ ).field;
271
+ const more = found.length > 1 ? ` (and ${found.length - 1} more)` : "";
272
+ setKnown((before) => [
273
+ ...before.filter((each) => each.label !== label2),
274
+ kept
275
+ ]);
276
+ say(field, `${label2} \u2192 ${describeDestination(destination)}${more}`);
277
+ } catch {
278
+ say(field, "Erdraft's window is not open: run `erdraft studio`");
279
+ }
280
+ });
281
+ useEffect2(() => {
282
+ if (!active) {
283
+ hovered.current = null;
284
+ setBox(null);
285
+ setSaid(null);
286
+ return;
287
+ }
288
+ fetch(
289
+ `${url}/api/inspect/fields?page=${encodeURIComponent(window.location.pathname)}`
290
+ ).then(
291
+ async (response) => setKnown(fieldsResponse.parse(await response.json()).fields)
292
+ ).catch(() => setKnown([]));
293
+ const outline = (field) => {
294
+ hovered.current = field;
295
+ setBox(field ? { field, rect: field.getBoundingClientRect() } : null);
296
+ };
297
+ const onMove = (event) => outline(fieldOf(event.target));
298
+ const onScroll = () => outline(hovered.current);
299
+ const onPress = (event) => {
300
+ const field = fieldOf(event.target);
301
+ if (!field) {
302
+ return;
303
+ }
304
+ event.preventDefault();
305
+ event.stopPropagation();
306
+ if (event.type === "click") {
307
+ trace(field);
308
+ }
309
+ };
310
+ return listenOnDocument([
311
+ ["mousemove", onMove],
312
+ ["scroll", onScroll],
313
+ ["pointerdown", onPress],
314
+ ["mousedown", onPress],
315
+ ["click", onPress]
316
+ ]);
317
+ }, [active, url]);
318
+ if (!active) {
319
+ return null;
320
+ }
321
+ const label = box ? labelOf(box.field) : "";
322
+ const traced = known.find((field) => field.label === label);
323
+ const text = box && said?.field === box.field ? said.text : traced ? `${label} \u2192 ${describeDestination(traced.destination)}` : `${label || "This field"}: click to trace where it is kept`;
324
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
325
+ /* @__PURE__ */ jsx(
326
+ "div",
327
+ {
328
+ style: {
329
+ ...TEXT,
330
+ bottom: 16,
331
+ left: "50%",
332
+ transform: "translateX(-50%)"
333
+ },
334
+ children: "Erdraft \xB7 click a field to see where it is kept \xB7 Esc to stop"
335
+ }
336
+ ),
337
+ box ? /* @__PURE__ */ jsxs(Fragment, { children: [
338
+ /* @__PURE__ */ jsx(
339
+ "div",
340
+ {
341
+ style: {
342
+ position: "fixed",
343
+ top: box.rect.top - 2,
344
+ left: box.rect.left - 2,
345
+ width: box.rect.width + 4,
346
+ height: box.rect.height + 4,
347
+ border: `2px solid ${FOCUS}`,
348
+ borderRadius: 6,
349
+ pointerEvents: "none",
350
+ zIndex: LAYER
351
+ }
352
+ }
353
+ ),
354
+ /* @__PURE__ */ jsx(
355
+ "div",
356
+ {
357
+ style: {
358
+ ...TEXT,
359
+ top: Math.max(4, box.rect.top - 30),
360
+ ...box.rect.left > window.innerWidth / 2 ? { right: window.innerWidth - box.rect.right } : { left: box.rect.left },
361
+ maxWidth: "calc(100vw - 16px)",
362
+ overflow: "hidden",
363
+ textOverflow: "ellipsis"
364
+ },
365
+ children: text
366
+ }
367
+ )
368
+ ] }) : null
369
+ ] });
370
+ }
371
+ export {
372
+ Inspector as default
373
+ };
@@ -0,0 +1,64 @@
1
+ // src/inspect/read-write.ts
2
+ var UPDATE = /^update (?:"[^"]+"\.)?"([^"]+)" set (.+?)(?: from | where | returning |$)/s;
3
+ var INSERT = /^insert into (?:"[^"]+"\.)?"([^"]+)" \(([^)]*)\) values (.+?)(?: on conflict | returning |$)/s;
4
+ var ASSIGNMENT = /^"([^"]+)" = \$(\d+)$/;
5
+ var PLACEHOLDER = /^\$(\d+)$/;
6
+ var ROW = /\(([^()]*)\)/g;
7
+ function paramOf(params, placeholder) {
8
+ return params[Number(placeholder) - 1];
9
+ }
10
+ function readWrite(sql, params) {
11
+ const update = UPDATE.exec(sql);
12
+ if (update) {
13
+ const values = {};
14
+ for (const part of String(update[2]).split(", ")) {
15
+ const assignment = ASSIGNMENT.exec(part);
16
+ if (assignment) {
17
+ values[String(assignment[1])] = paramOf(params, String(assignment[2]));
18
+ }
19
+ }
20
+ return [{ table: String(update[1]), values }];
21
+ }
22
+ const insert = INSERT.exec(sql);
23
+ if (!insert) {
24
+ return [];
25
+ }
26
+ const table = String(insert[1]);
27
+ const columns = String(insert[2]).split(", ").map((column) => column.slice(1, -1));
28
+ return [...String(insert[3]).matchAll(ROW)].map((row) => {
29
+ const values = {};
30
+ String(row[1]).split(", ").forEach((token, at) => {
31
+ const placeholder = PLACEHOLDER.exec(token);
32
+ if (placeholder) {
33
+ values[String(columns[at])] = paramOf(params, String(placeholder[1]));
34
+ }
35
+ });
36
+ return { table, values };
37
+ });
38
+ }
39
+
40
+ // src/inspect/inspect-logger.ts
41
+ function jsonable(_key, value) {
42
+ return typeof value === "bigint" ? value.toString() : value;
43
+ }
44
+ function inspectLogger(url, send = fetch) {
45
+ return {
46
+ logQuery(query, params) {
47
+ try {
48
+ const writes = readWrite(query, params);
49
+ if (writes.length === 0) {
50
+ return;
51
+ }
52
+ send(`${url}/api/inspect/writes`, {
53
+ method: "POST",
54
+ headers: { "content-type": "application/json" },
55
+ body: JSON.stringify({ writes }, jsonable)
56
+ }).catch(() => void 0);
57
+ } catch {
58
+ }
59
+ }
60
+ };
61
+ }
62
+ export {
63
+ inspectLogger as default
64
+ };
@@ -0,0 +1,238 @@
1
+ import {
2
+ indexName
3
+ } from "./chunk-VSN3XJPO.js";
4
+
5
+ // src/core/read-schema-child.ts
6
+ import { pathToFileURL } from "url";
7
+
8
+ // src/core/read-drizzle-schema.ts
9
+ import { is, SQL, StringChunk } from "drizzle-orm";
10
+ import {
11
+ PgTable,
12
+ getTableConfig,
13
+ PgDialect,
14
+ isPgEnum
15
+ } from "drizzle-orm/pg-core";
16
+ import { z } from "zod";
17
+
18
+ // src/core/type-of-json-schema.ts
19
+ var PRIMITIVES = {
20
+ string: "string",
21
+ number: "number",
22
+ integer: "number",
23
+ boolean: "boolean",
24
+ null: "null"
25
+ };
26
+ function literal(value) {
27
+ return typeof value === "string" ? `'${value.replaceAll("'", "\\'")}'` : JSON.stringify(value);
28
+ }
29
+ function element(type) {
30
+ let depth = 0;
31
+ let quote = null;
32
+ let joined = false;
33
+ for (const char of type) {
34
+ if (quote) {
35
+ quote = char === quote ? null : quote;
36
+ } else if (char === "'" || char === '"') {
37
+ quote = char;
38
+ } else if ("{[(<".includes(char)) {
39
+ depth += 1;
40
+ } else if ("}])>".includes(char)) {
41
+ depth -= 1;
42
+ } else if (depth === 0 && (char === "|" || char === "&")) {
43
+ joined = true;
44
+ }
45
+ }
46
+ return joined ? `(${type})[]` : `${type}[]`;
47
+ }
48
+ function objectOf(node, of) {
49
+ const required = new Set(node.required);
50
+ const fields = Object.entries(node.properties ?? {}).map(
51
+ ([name, child]) => `${name}${required.has(name) ? "" : "?"}: ${of(child)}`
52
+ );
53
+ const rest = node.additionalProperties;
54
+ if (fields.length === 0 && typeof rest === "object") {
55
+ return `Record<string, ${of(rest)}>`;
56
+ }
57
+ return fields.length === 0 ? "{}" : `{ ${fields.join("; ")} }`;
58
+ }
59
+ function arrayOf(node, of) {
60
+ const { prefixItems, items } = node;
61
+ if (Array.isArray(items)) {
62
+ return `[${items.map(of).join(", ")}]`;
63
+ }
64
+ if (prefixItems) {
65
+ return `[${prefixItems.map(of).join(", ")}]`;
66
+ }
67
+ return element(items === void 0 ? "unknown" : of(items));
68
+ }
69
+ function typeOfJsonSchema(node, root = typeof node === "object" ? node : {}, followed = /* @__PURE__ */ new Set()) {
70
+ if (typeof node === "boolean") {
71
+ return node ? "unknown" : "never";
72
+ }
73
+ const of = (child) => typeOfJsonSchema(child, root, followed);
74
+ if (node.$ref !== void 0) {
75
+ const name = node.$ref.slice(node.$ref.lastIndexOf("/") + 1);
76
+ const definition = root.$defs?.[name];
77
+ if (definition === void 0) {
78
+ return "unknown";
79
+ }
80
+ return followed.has(name) ? name : typeOfJsonSchema(definition, root, /* @__PURE__ */ new Set([...followed, name]));
81
+ }
82
+ if (node.const !== void 0) {
83
+ return literal(node.const);
84
+ }
85
+ if (node.enum) {
86
+ return node.enum.map(literal).join(" | ");
87
+ }
88
+ const union = node.anyOf ?? node.oneOf;
89
+ if (union) {
90
+ return union.map(of).join(" | ");
91
+ }
92
+ if (node.allOf) {
93
+ return node.allOf.map(of).join(" & ");
94
+ }
95
+ if (Array.isArray(node.type)) {
96
+ return node.type.map((type) => of({ ...node, type })).join(" | ");
97
+ }
98
+ if (node.type === "object") {
99
+ return objectOf(node, of);
100
+ }
101
+ if (node.type === "array") {
102
+ return arrayOf(node, of);
103
+ }
104
+ return PRIMITIVES[node.type ?? ""] ?? "unknown";
105
+ }
106
+
107
+ // src/core/read-drizzle-schema.ts
108
+ var dialect = new PgDialect();
109
+ function sqlOf(value) {
110
+ return dialect.sqlToQuery(value, "indexes").sql;
111
+ }
112
+ function defaultOf(column) {
113
+ if (column.generatedIdentity) {
114
+ return "identity";
115
+ }
116
+ if (column.generated || column.defaultFn) {
117
+ return "generated";
118
+ }
119
+ const value = column.default;
120
+ if (value === void 0) {
121
+ return void 0;
122
+ }
123
+ if (is(value, SQL)) {
124
+ const words = value.queryChunks.flatMap(
125
+ (chunk) => is(chunk, StringChunk) ? chunk.value : []
126
+ );
127
+ return words.length === value.queryChunks.length ? words.join("") : "sql";
128
+ }
129
+ return JSON.stringify(value);
130
+ }
131
+ function namedIndex(table, name, columns, unique) {
132
+ return {
133
+ name: name ?? indexName(table, { columns, unique }),
134
+ columns,
135
+ unique
136
+ };
137
+ }
138
+ function nameOf(table) {
139
+ const { schema, name } = getTableConfig(table);
140
+ return schema ? `${schema}.${name}` : name;
141
+ }
142
+ function shapeName(table, column) {
143
+ const words = `${table}_${column}`.split(/[_.]/).filter(Boolean);
144
+ return `${words.map(
145
+ (word, index) => index === 0 ? word : `${word.charAt(0).toUpperCase()}${word.slice(1)}`
146
+ ).join("")}Shape`;
147
+ }
148
+ function tableOf(table, shapeOf) {
149
+ const config = getTableConfig(table);
150
+ const primary = new Set(
151
+ config.primaryKeys.flatMap((key) => key.columns.map((each) => each.name))
152
+ );
153
+ const unique = new Set(
154
+ config.uniqueConstraints.filter((constraint) => constraint.columns.length === 1).flatMap((constraint) => constraint.columns.map((each) => each.name))
155
+ );
156
+ return {
157
+ name: nameOf(table),
158
+ columns: config.columns.map((column) => {
159
+ const shape = shapeOf(nameOf(table), column.name);
160
+ return {
161
+ name: column.name,
162
+ type: column.getSQLType(),
163
+ nullable: !column.notNull,
164
+ primaryKey: column.primary || primary.has(column.name),
165
+ unique: column.isUnique || unique.has(column.name),
166
+ default: defaultOf(column),
167
+ ...shape === void 0 ? {} : { shape }
168
+ };
169
+ }),
170
+ foreignKeys: config.foreignKeys.map((foreignKey) => {
171
+ const reference = foreignKey.reference();
172
+ return {
173
+ name: foreignKey.getName(),
174
+ columns: reference.columns.map((each) => each.name),
175
+ table: nameOf(reference.foreignTable),
176
+ foreignColumns: reference.foreignColumns.map((each) => each.name),
177
+ onDelete: foreignKey.onDelete
178
+ };
179
+ }),
180
+ // Its indexes, and its unique constraints of several columns (one of a single column is its column's `unique`)
181
+ indexes: [
182
+ ...config.indexes.map(
183
+ ({ config: index }) => namedIndex(
184
+ config.name,
185
+ index.name,
186
+ index.columns.map(
187
+ (column) => (
188
+ // Else an indexed column, the only other kind, by its name
189
+ is(column, SQL) ? sqlOf(column) : String(Reflect.get(column, "name"))
190
+ )
191
+ ),
192
+ index.unique
193
+ )
194
+ ),
195
+ ...config.uniqueConstraints.filter((constraint) => constraint.columns.length > 1).map(
196
+ (constraint) => namedIndex(
197
+ config.name,
198
+ constraint.getName(),
199
+ constraint.columns.map((each) => each.name),
200
+ true
201
+ )
202
+ )
203
+ ],
204
+ checks: config.checks.map((check) => ({
205
+ name: check.name,
206
+ expression: sqlOf(check.value)
207
+ }))
208
+ };
209
+ }
210
+ function readDrizzleSchema(module) {
211
+ const values = [...new Set(Object.values(module))];
212
+ const shapeOf = (table, column) => {
213
+ const exported = module[shapeName(table, column)];
214
+ return exported instanceof z.core.$ZodType ? typeOfJsonSchema(
215
+ z.toJSONSchema(exported, { io: "output", unrepresentable: "any" })
216
+ ) : void 0;
217
+ };
218
+ return {
219
+ tables: values.filter((value) => is(value, PgTable)).map((table) => tableOf(table, shapeOf)),
220
+ enums: values.filter(isPgEnum).map((each) => ({
221
+ name: each.enumName,
222
+ values: [...each.enumValues]
223
+ }))
224
+ };
225
+ }
226
+
227
+ // src/core/read-schema-child.ts
228
+ var [path = ""] = process.argv.slice(2);
229
+ try {
230
+ const module = await import(pathToFileURL(path).href);
231
+ const { default: exported } = module;
232
+ const exports = typeof exported === "object" && exported !== null ? { ...exported, ...module } : module;
233
+ process.send?.({ schema: readDrizzleSchema(exports) });
234
+ } catch (error) {
235
+ process.send?.({
236
+ error: error instanceof Error ? error.message : String(error)
237
+ });
238
+ }
@@ -0,0 +1,32 @@
1
+ import {
2
+ createApi
3
+ } from "./chunk-SSLBX2OF.js";
4
+ import "./chunk-VSN3XJPO.js";
5
+
6
+ // src/server/studio-config.ts
7
+ import { fileURLToPath } from "url";
8
+ import react from "@vitejs/plugin-react";
9
+ import tailwind from "@tailwindcss/vite";
10
+ function studioConfig(config) {
11
+ return {
12
+ configFile: false,
13
+ root: fileURLToPath(new URL("../..", import.meta.url)),
14
+ plugins: [
15
+ react(),
16
+ tailwind(),
17
+ {
18
+ name: "erdraft-api",
19
+ configureServer(server) {
20
+ const api = createApi(config);
21
+ server.middlewares.use(api.middleware);
22
+ server.httpServer?.on("close", api.close);
23
+ }
24
+ }
25
+ ],
26
+ server: { host: "127.0.0.1", port: config.port, strictPort: true },
27
+ logLevel: "warn"
28
+ };
29
+ }
30
+ export {
31
+ studioConfig as default
32
+ };