@agent-native/core 0.26.9 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/run-ownership.d.ts +12 -0
- package/dist/agent/run-ownership.d.ts.map +1 -0
- package/dist/agent/run-ownership.js +39 -0
- package/dist/agent/run-ownership.js.map +1 -0
- package/dist/client/db-admin/DataGrid.d.ts +42 -0
- package/dist/client/db-admin/DataGrid.d.ts.map +1 -0
- package/dist/client/db-admin/DataGrid.js +204 -0
- package/dist/client/db-admin/DataGrid.js.map +1 -0
- package/dist/client/db-admin/DbAdminPage.d.ts +2 -0
- package/dist/client/db-admin/DbAdminPage.d.ts.map +1 -0
- package/dist/client/db-admin/DbAdminPage.js +72 -0
- package/dist/client/db-admin/DbAdminPage.js.map +1 -0
- package/dist/client/db-admin/DevDatabaseLink.d.ts +19 -0
- package/dist/client/db-admin/DevDatabaseLink.d.ts.map +1 -0
- package/dist/client/db-admin/DevDatabaseLink.js +25 -0
- package/dist/client/db-admin/DevDatabaseLink.js.map +1 -0
- package/dist/client/db-admin/EditableCell.d.ts +26 -0
- package/dist/client/db-admin/EditableCell.d.ts.map +1 -0
- package/dist/client/db-admin/EditableCell.js +150 -0
- package/dist/client/db-admin/EditableCell.js.map +1 -0
- package/dist/client/db-admin/FilterBar.d.ts +8 -0
- package/dist/client/db-admin/FilterBar.d.ts.map +1 -0
- package/dist/client/db-admin/FilterBar.js +68 -0
- package/dist/client/db-admin/FilterBar.js.map +1 -0
- package/dist/client/db-admin/ResultsGrid.d.ts +6 -0
- package/dist/client/db-admin/ResultsGrid.d.ts.map +1 -0
- package/dist/client/db-admin/ResultsGrid.js +41 -0
- package/dist/client/db-admin/ResultsGrid.js.map +1 -0
- package/dist/client/db-admin/RowSidePanel.d.ts +18 -0
- package/dist/client/db-admin/RowSidePanel.d.ts.map +1 -0
- package/dist/client/db-admin/RowSidePanel.js +104 -0
- package/dist/client/db-admin/RowSidePanel.js.map +1 -0
- package/dist/client/db-admin/SqlEditor.d.ts +8 -0
- package/dist/client/db-admin/SqlEditor.d.ts.map +1 -0
- package/dist/client/db-admin/SqlEditor.js +350 -0
- package/dist/client/db-admin/SqlEditor.js.map +1 -0
- package/dist/client/db-admin/TableBrowser.d.ts +10 -0
- package/dist/client/db-admin/TableBrowser.d.ts.map +1 -0
- package/dist/client/db-admin/TableBrowser.js +61 -0
- package/dist/client/db-admin/TableBrowser.js.map +1 -0
- package/dist/client/db-admin/TableEditor.d.ts +9 -0
- package/dist/client/db-admin/TableEditor.d.ts.map +1 -0
- package/dist/client/db-admin/TableEditor.js +254 -0
- package/dist/client/db-admin/TableEditor.js.map +1 -0
- package/dist/client/db-admin/cell-format.d.ts +55 -0
- package/dist/client/db-admin/cell-format.d.ts.map +1 -0
- package/dist/client/db-admin/cell-format.js +223 -0
- package/dist/client/db-admin/cell-format.js.map +1 -0
- package/dist/client/db-admin/changeset.d.ts +74 -0
- package/dist/client/db-admin/changeset.d.ts.map +1 -0
- package/dist/client/db-admin/changeset.js +169 -0
- package/dist/client/db-admin/changeset.js.map +1 -0
- package/dist/client/db-admin/export-utils.d.ts +15 -0
- package/dist/client/db-admin/export-utils.d.ts.map +1 -0
- package/dist/client/db-admin/export-utils.js +62 -0
- package/dist/client/db-admin/export-utils.js.map +1 -0
- package/dist/client/db-admin/index.d.ts +7 -0
- package/dist/client/db-admin/index.d.ts.map +1 -0
- package/dist/client/db-admin/index.js +8 -0
- package/dist/client/db-admin/index.js.map +1 -0
- package/dist/client/db-admin/sql-storage.d.ts +35 -0
- package/dist/client/db-admin/sql-storage.d.ts.map +1 -0
- package/dist/client/db-admin/sql-storage.js +117 -0
- package/dist/client/db-admin/sql-storage.js.map +1 -0
- package/dist/client/db-admin/storage.d.ts +24 -0
- package/dist/client/db-admin/storage.d.ts.map +1 -0
- package/dist/client/db-admin/storage.js +50 -0
- package/dist/client/db-admin/storage.js.map +1 -0
- package/dist/client/db-admin/useAgentSync.d.ts +22 -0
- package/dist/client/db-admin/useAgentSync.d.ts.map +1 -0
- package/dist/client/db-admin/useAgentSync.js +120 -0
- package/dist/client/db-admin/useAgentSync.js.map +1 -0
- package/dist/client/db-admin/useDbAdmin.d.ts +20 -0
- package/dist/client/db-admin/useDbAdmin.d.ts.map +1 -0
- package/dist/client/db-admin/useDbAdmin.js +154 -0
- package/dist/client/db-admin/useDbAdmin.js.map +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/credentials/index.d.ts.map +1 -1
- package/dist/credentials/index.js +25 -5
- package/dist/credentials/index.js.map +1 -1
- package/dist/db-admin/agent-tools.d.ts +15 -0
- package/dist/db-admin/agent-tools.d.ts.map +1 -0
- package/dist/db-admin/agent-tools.js +147 -0
- package/dist/db-admin/agent-tools.js.map +1 -0
- package/dist/db-admin/operations.d.ts +17 -0
- package/dist/db-admin/operations.d.ts.map +1 -0
- package/dist/db-admin/operations.js +541 -0
- package/dist/db-admin/operations.js.map +1 -0
- package/dist/db-admin/routes.d.ts +5 -0
- package/dist/db-admin/routes.d.ts.map +1 -0
- package/dist/db-admin/routes.js +134 -0
- package/dist/db-admin/routes.js.map +1 -0
- package/dist/db-admin/types.d.ts +85 -0
- package/dist/db-admin/types.d.ts.map +1 -0
- package/dist/db-admin/types.js +9 -0
- package/dist/db-admin/types.js.map +1 -0
- package/dist/extensions/url-safety.d.ts +20 -0
- package/dist/extensions/url-safety.d.ts.map +1 -1
- package/dist/extensions/url-safety.js +43 -0
- package/dist/extensions/url-safety.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts.map +1 -1
- package/dist/file-upload/actions/upload-image.js +6 -1
- package/dist/file-upload/actions/upload-image.js.map +1 -1
- package/dist/integrations/adapters/email.d.ts.map +1 -1
- package/dist/integrations/adapters/email.js +112 -0
- package/dist/integrations/adapters/email.js.map +1 -1
- package/dist/integrations/types.d.ts +11 -0
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/scripts/db/exec.d.ts.map +1 -1
- package/dist/scripts/db/exec.js +2 -1
- package/dist/scripts/db/exec.js.map +1 -1
- package/dist/scripts/db/index.d.ts.map +1 -1
- package/dist/scripts/db/index.js +1 -0
- package/dist/scripts/db/index.js.map +1 -1
- package/dist/scripts/db/migrate-encrypt-credentials.d.ts +28 -0
- package/dist/scripts/db/migrate-encrypt-credentials.d.ts.map +1 -0
- package/dist/scripts/db/migrate-encrypt-credentials.js +190 -0
- package/dist/scripts/db/migrate-encrypt-credentials.js.map +1 -0
- package/dist/scripts/db/query.d.ts.map +1 -1
- package/dist/scripts/db/query.js +2 -1
- package/dist/scripts/db/query.js.map +1 -1
- package/dist/scripts/db/safety.d.ts +1 -0
- package/dist/scripts/db/safety.d.ts.map +1 -1
- package/dist/scripts/db/safety.js +32 -0
- package/dist/scripts/db/safety.js.map +1 -1
- package/dist/scripts/db/scoping.d.ts.map +1 -1
- package/dist/scripts/db/scoping.js +11 -1
- package/dist/scripts/db/scoping.js.map +1 -1
- package/dist/secrets/crypto.d.ts +28 -0
- package/dist/secrets/crypto.d.ts.map +1 -0
- package/dist/secrets/crypto.js +81 -0
- package/dist/secrets/crypto.js.map +1 -0
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +3 -61
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +5 -2
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +24 -7
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +39 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.js +3 -3
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +5 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/csrf.js +9 -1
- package/dist/server/csrf.js.map +1 -1
- package/dist/server/design-token-utils.d.ts +8 -1
- package/dist/server/design-token-utils.d.ts.map +1 -1
- package/dist/server/design-token-utils.js +12 -4
- package/dist/server/design-token-utils.js.map +1 -1
- package/dist/templates/default/AGENTS.md +4 -4
- package/dist/templates/default/app/routes/database.tsx +13 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +9 -2
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +7 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +4 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/a2a-protocol.md +2 -2
- package/docs/content/actions.md +2 -54
- package/docs/content/agent-mentions.md +1 -1
- package/docs/content/agent-teams.md +1 -1
- package/docs/content/authentication.md +2 -2
- package/docs/content/cli-adapters.md +33 -17
- package/docs/content/client.md +11 -20
- package/docs/content/code-agents-ui.md +19 -6
- package/docs/content/context-awareness.md +36 -20
- package/docs/content/database.md +3 -3
- package/docs/content/deployment.md +8 -8
- package/docs/content/dispatch.md +1 -1
- package/docs/content/external-agents.md +5 -1
- package/docs/content/faq.md +1 -0
- package/docs/content/frames.md +110 -30
- package/docs/content/getting-started.md +15 -14
- package/docs/content/mcp-clients.md +1 -1
- package/docs/content/mcp-protocol.md +11 -88
- package/docs/content/messaging.md +1 -1
- package/docs/content/migration-workbench.md +13 -87
- package/docs/content/multi-app-workspace.md +2 -38
- package/docs/content/multi-tenancy.md +3 -26
- package/docs/content/onboarding.md +10 -3
- package/docs/content/recurring-jobs.md +2 -2
- package/docs/content/security.md +33 -1
- package/docs/content/server.md +1 -1
- package/docs/content/template-assets.md +9 -9
- package/docs/content/template-brain.md +114 -388
- package/docs/content/template-clips.md +42 -2
- package/docs/content/template-content.md +1 -1
- package/docs/content/template-design.md +27 -0
- package/docs/content/template-dispatch.md +3 -3
- package/docs/content/template-forms.md +6 -6
- package/docs/content/template-starter.md +2 -2
- package/docs/content/using-your-agent.md +56 -0
- package/docs/content/workspace-management.md +6 -6
- package/docs/content/workspace.md +19 -0
- package/package.json +10 -3
- package/src/templates/default/AGENTS.md +4 -4
- package/src/templates/default/app/routes/database.tsx +13 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +9 -2
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +7 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell-format.js","sourceRoot":"","sources":["../../../src/client/db-admin/cell-format.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DbAdminColumn } from \"../../db-admin/types.js\";\n\n/**\n * Type-aware cell formatting and parsing helpers for the DB admin grid.\n *\n * These are intentionally dialect-agnostic: they look at the column's `type`\n * string and normalize it into one of a small set of editor \"kinds\" that drive\n * which editor UI is rendered and how values are parsed back into the mutation\n * payload.\n */\n\nexport type EditorKind =\n | \"text\"\n | \"number\"\n | \"boolean\"\n | \"json\"\n | \"timestamp\"\n | \"enum\"\n | \"uuid\";\n\n/** Sentinel meaning \"store SQL NULL\". */\nexport const NULL_VALUE = null;\n\nconst NUMBER_TYPES = [\n \"int\",\n \"integer\",\n \"smallint\",\n \"bigint\",\n \"serial\",\n \"bigserial\",\n \"smallserial\",\n \"decimal\",\n \"numeric\",\n \"real\",\n \"double\",\n \"float\",\n \"money\",\n];\n\nconst BOOLEAN_TYPES = [\"bool\", \"boolean\"];\nconst JSON_TYPES = [\"json\", \"jsonb\"];\nconst TIMESTAMP_TYPES = [\n \"timestamp\",\n \"timestamptz\",\n \"datetime\",\n \"date\",\n \"time\",\n \"timetz\",\n];\nconst UUID_TYPES = [\"uuid\", \"guid\"];\n\nfunction normalizeType(col: DbAdminColumn): string {\n return (col.type || \"\").toString().trim().toLowerCase();\n}\n\n/**\n * Try to pull allowed enum values out of a column definition.\n *\n * The base contract column shape (`DbAdminColumn`) only carries a `type`\n * string, but introspection may attach extra fields per dialect. We probe a\n * few likely shapes and also parse a Postgres/MySQL-style inline list embedded\n * in the type string, e.g. `enum('a','b')`.\n */\nexport function inferEnumValues(col: DbAdminColumn): string[] | null {\n const anyCol = col as unknown as Record<string, unknown>;\n const candidates = [anyCol.enumValues, anyCol.values, anyCol.options];\n for (const candidate of candidates) {\n if (Array.isArray(candidate) && candidate.length > 0) {\n return candidate.map((v) => String(v));\n }\n }\n const type = (col.type || \"\").toString();\n const match = type.match(/^\\s*(?:enum|set)\\s*\\((.+)\\)\\s*$/i);\n if (match) {\n const items = match[1]\n .split(\",\")\n .map((s) => s.trim().replace(/^['\"]|['\"]$/g, \"\"))\n .filter((s) => s.length > 0);\n if (items.length > 0) return items;\n }\n return null;\n}\n\n/** Infer which editor UI a column should use. */\nexport function inferEditorKind(col: DbAdminColumn): EditorKind {\n const type = normalizeType(col);\n\n if (inferEnumValues(col)) return \"enum\";\n if (UUID_TYPES.some((t) => type.includes(t))) return \"uuid\";\n if (BOOLEAN_TYPES.some((t) => type === t || type.includes(t)))\n return \"boolean\";\n if (JSON_TYPES.some((t) => type === t || type.includes(t))) return \"json\";\n if (TIMESTAMP_TYPES.some((t) => type.includes(t))) return \"timestamp\";\n if (NUMBER_TYPES.some((t) => type.includes(t))) return \"number\";\n return \"text\";\n}\n\n/** Whether a value is SQL NULL (vs empty string, 0, false, etc). */\nexport function isNull(value: unknown): boolean {\n return value === null || value === undefined;\n}\n\n/**\n * Format a DB value for compact in-cell display. Returns a marker the cell uses\n * to render NULL distinctly; for plain string consumers the text is \"NULL\".\n */\nexport function formatCellValue(\n value: unknown,\n kind: EditorKind,\n): { text: string; isNull: boolean } {\n if (isNull(value)) return { text: \"NULL\", isNull: true };\n\n switch (kind) {\n case \"boolean\":\n return { text: value === true ? \"true\" : \"false\", isNull: false };\n case \"json\":\n return { text: formatJsonCompact(value), isNull: false };\n case \"timestamp\":\n return { text: formatTimestamp(value), isNull: false };\n case \"number\":\n return { text: String(value), isNull: false };\n default:\n return { text: String(value), isNull: false };\n }\n}\n\n/** Compact single-line JSON for cell display. */\nexport function formatJsonCompact(value: unknown): string {\n if (typeof value === \"string\") {\n try {\n return JSON.stringify(JSON.parse(value));\n } catch {\n return value;\n }\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/** Pretty multi-line JSON for the expanded editor. */\nexport function formatJsonPretty(value: unknown): string {\n if (isNull(value)) return \"\";\n if (typeof value === \"string\") {\n try {\n return JSON.stringify(JSON.parse(value), null, 2);\n } catch {\n return value;\n }\n }\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n}\n\n/** Human-readable timestamp; tolerant of strings, numbers, and Dates. */\nexport function formatTimestamp(value: unknown): string {\n if (isNull(value)) return \"\";\n const date =\n value instanceof Date\n ? value\n : typeof value === \"number\"\n ? new Date(value)\n : new Date(String(value));\n if (Number.isNaN(date.getTime())) return String(value);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +\n `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n );\n}\n\n/** Convert a DB value to the string an editor input should start with. */\nexport function valueToEditString(value: unknown, kind: EditorKind): string {\n if (isNull(value)) return \"\";\n if (kind === \"json\") return formatJsonPretty(value);\n if (kind === \"timestamp\") return formatTimestamp(value);\n if (kind === \"boolean\") return value === true ? \"true\" : \"false\";\n return String(value);\n}\n\nexport class ParseError extends Error {}\n\n/**\n * Parse an edited string back into the JS value sent in the mutation payload.\n *\n * `allowEmptyString` distinguishes \"\" → empty string from \"\" → NULL. For most\n * types empty means NULL; for text the editor decides.\n */\nexport function parseEditValue(\n raw: string,\n kind: EditorKind,\n opts: { allowEmptyString?: boolean } = {},\n): unknown {\n if (raw === \"\") {\n if (kind === \"text\" && opts.allowEmptyString) return \"\";\n return NULL_VALUE;\n }\n\n const trimmed = raw.trim();\n\n switch (kind) {\n case \"number\": {\n const n = Number(trimmed);\n if (trimmed === \"\" || Number.isNaN(n)) {\n throw new ParseError(`\"${raw}\" is not a valid number`);\n }\n return n;\n }\n case \"boolean\": {\n const v = trimmed.toLowerCase();\n if ([\"true\", \"t\", \"1\", \"yes\", \"y\"].includes(v)) return true;\n if ([\"false\", \"f\", \"0\", \"no\", \"n\"].includes(v)) return false;\n if (v === \"null\") return NULL_VALUE;\n throw new ParseError(`\"${raw}\" is not a valid boolean`);\n }\n case \"json\": {\n // Accept ANY valid JSON value, including scalars.\n try {\n return JSON.parse(trimmed);\n } catch (err) {\n throw new ParseError(\n `Invalid JSON: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n case \"timestamp\":\n case \"uuid\":\n case \"enum\":\n case \"text\":\n default:\n // Preserve intentional spaces for text; trim structured types.\n return kind === \"text\" ? raw : trimmed;\n }\n}\n\n/** Cycle a tri-state boolean: null → true → false → null. */\nexport function cycleTriStateBoolean(value: unknown): boolean | null {\n if (value === null || value === undefined) return true;\n if (value === true) return false;\n return null;\n}\n"]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { DbAdminTableSchema, DbAdminMutation } from "../../db-admin/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The staged-changeset model — the production-grade core of the table editor.
|
|
4
|
+
*
|
|
5
|
+
* Edits are NOT committed on blur. Instead every cell edit, new row, and
|
|
6
|
+
* deletion accumulates here until the user explicitly commits (Cmd/Ctrl+S or
|
|
7
|
+
* the Commit button), at which point {@link buildMutation} maps the staged
|
|
8
|
+
* state into a single {@link DbAdminMutation}. Until then the grid renders the
|
|
9
|
+
* staged values overlaid on the fetched rows, and the user can discard
|
|
10
|
+
* everything.
|
|
11
|
+
*
|
|
12
|
+
* Rows are keyed by a stable primary-key string (the table's PK column values
|
|
13
|
+
* joined). If a table has no primary key, edits target a full-row `where`
|
|
14
|
+
* match instead, and the consumer should warn / disable editing.
|
|
15
|
+
*/
|
|
16
|
+
/** New rows get a temporary client id so the grid can track them pre-insert. */
|
|
17
|
+
export interface NewRow {
|
|
18
|
+
/** Stable client-only id, e.g. `new:0`. Never sent to the server. */
|
|
19
|
+
_localId: string;
|
|
20
|
+
values: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export interface Changeset {
|
|
23
|
+
/** rowPkString → { column → newValue }. */
|
|
24
|
+
edits: Map<string, Record<string, unknown>>;
|
|
25
|
+
/** Staged inserts. */
|
|
26
|
+
newRows: NewRow[];
|
|
27
|
+
/** rowPkString of rows staged for deletion. */
|
|
28
|
+
deletedKeys: Set<string>;
|
|
29
|
+
}
|
|
30
|
+
export interface UseChangesetResult {
|
|
31
|
+
edits: Map<string, Record<string, unknown>>;
|
|
32
|
+
newRows: NewRow[];
|
|
33
|
+
deletedKeys: Set<string>;
|
|
34
|
+
/** Whether editing is possible (requires a primary key on the table). */
|
|
35
|
+
canEdit: boolean;
|
|
36
|
+
/** Stage a single cell edit on an existing row. */
|
|
37
|
+
setCell: (pk: string, col: string, value: unknown) => void;
|
|
38
|
+
/** Stage many cell edits on one existing row at once. */
|
|
39
|
+
setCells: (pk: string, values: Record<string, unknown>) => void;
|
|
40
|
+
/** Append a blank new row (optionally seeded) and return its local id. */
|
|
41
|
+
addRow: (seed?: Record<string, unknown>) => string;
|
|
42
|
+
/** Patch a staged new row's values. */
|
|
43
|
+
setNewRowCell: (localId: string, col: string, value: unknown) => void;
|
|
44
|
+
/** Remove a staged new row entirely. */
|
|
45
|
+
removeNewRow: (localId: string) => void;
|
|
46
|
+
/** Stage existing rows for deletion (by pk string). Toggles off if re-staged. */
|
|
47
|
+
deleteRows: (pks: string[]) => void;
|
|
48
|
+
/** Un-stage a deletion. */
|
|
49
|
+
undeleteRows: (pks: string[]) => void;
|
|
50
|
+
/** Clear a single staged cell edit (revert to original). */
|
|
51
|
+
revertCell: (pk: string, col: string) => void;
|
|
52
|
+
/** Drop everything. */
|
|
53
|
+
discardAll: () => void;
|
|
54
|
+
/** Whether a given existing-row cell is dirty. */
|
|
55
|
+
isCellDirty: (pk: string, col: string) => boolean;
|
|
56
|
+
/** The staged value for a cell, if any. */
|
|
57
|
+
getStagedCell: (pk: string, col: string) => {
|
|
58
|
+
value: unknown;
|
|
59
|
+
} | undefined;
|
|
60
|
+
/** Whether an existing row is staged for deletion. */
|
|
61
|
+
isDeleted: (pk: string) => boolean;
|
|
62
|
+
isDirty: boolean;
|
|
63
|
+
/** Total count of pending changes (edited rows + new rows + deletions). */
|
|
64
|
+
pendingCount: number;
|
|
65
|
+
/**
|
|
66
|
+
* Build the mutation payload. `originalRows` maps pk string → the original
|
|
67
|
+
* fetched row, used to construct the `where` clause for updates/deletes.
|
|
68
|
+
*/
|
|
69
|
+
buildMutation: (originalRows: Map<string, Record<string, unknown>>, dryRun?: boolean) => DbAdminMutation;
|
|
70
|
+
}
|
|
71
|
+
/** Compute the stable pk string for a row given the schema's primary key. */
|
|
72
|
+
export declare function pkStringFor(schema: DbAdminTableSchema | undefined, row: Record<string, unknown>): string;
|
|
73
|
+
export declare function useChangeset(schema: DbAdminTableSchema | undefined): UseChangesetResult;
|
|
74
|
+
//# sourceMappingURL=changeset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changeset.d.ts","sourceRoot":"","sources":["../../../src/client/db-admin/changeset.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2
|
+
/** Compute the stable pk string for a row given the schema's primary key. */
|
|
3
|
+
export function pkStringFor(schema, row) {
|
|
4
|
+
const cols = schema && schema.primaryKey.length > 0
|
|
5
|
+
? schema.primaryKey
|
|
6
|
+
: // No PK: fall back to a full-row signature so each row is distinct.
|
|
7
|
+
Object.keys(row).sort();
|
|
8
|
+
return JSON.stringify(cols.map((c) => row[c] ?? null));
|
|
9
|
+
}
|
|
10
|
+
/** Build the `where` object that uniquely identifies an existing row. */
|
|
11
|
+
function whereFor(schema, row) {
|
|
12
|
+
if (schema && schema.primaryKey.length > 0) {
|
|
13
|
+
const where = {};
|
|
14
|
+
for (const col of schema.primaryKey)
|
|
15
|
+
where[col] = row[col] ?? null;
|
|
16
|
+
return where;
|
|
17
|
+
}
|
|
18
|
+
// No PK — match the full original row.
|
|
19
|
+
return { ...row };
|
|
20
|
+
}
|
|
21
|
+
export function useChangeset(schema) {
|
|
22
|
+
const [edits, setEdits] = useState(() => new Map());
|
|
23
|
+
const [newRows, setNewRows] = useState([]);
|
|
24
|
+
const [deletedKeys, setDeletedKeys] = useState(() => new Set());
|
|
25
|
+
const canEdit = !!schema && schema.primaryKey.length > 0;
|
|
26
|
+
const setCell = useCallback((pk, col, value) => {
|
|
27
|
+
setEdits((prev) => {
|
|
28
|
+
const next = new Map(prev);
|
|
29
|
+
const row = { ...(next.get(pk) ?? {}) };
|
|
30
|
+
row[col] = value;
|
|
31
|
+
next.set(pk, row);
|
|
32
|
+
return next;
|
|
33
|
+
});
|
|
34
|
+
}, []);
|
|
35
|
+
const setCells = useCallback((pk, values) => {
|
|
36
|
+
setEdits((prev) => {
|
|
37
|
+
const next = new Map(prev);
|
|
38
|
+
const row = { ...(next.get(pk) ?? {}), ...values };
|
|
39
|
+
next.set(pk, row);
|
|
40
|
+
return next;
|
|
41
|
+
});
|
|
42
|
+
}, []);
|
|
43
|
+
const revertCell = useCallback((pk, col) => {
|
|
44
|
+
setEdits((prev) => {
|
|
45
|
+
if (!prev.has(pk))
|
|
46
|
+
return prev;
|
|
47
|
+
const next = new Map(prev);
|
|
48
|
+
const row = { ...next.get(pk) };
|
|
49
|
+
delete row[col];
|
|
50
|
+
if (Object.keys(row).length === 0)
|
|
51
|
+
next.delete(pk);
|
|
52
|
+
else
|
|
53
|
+
next.set(pk, row);
|
|
54
|
+
return next;
|
|
55
|
+
});
|
|
56
|
+
}, []);
|
|
57
|
+
const addRow = useCallback((seed) => {
|
|
58
|
+
const localId = `new:${Math.random().toString(36).slice(2)}`;
|
|
59
|
+
setNewRows((prev) => [...prev, { _localId: localId, values: seed ?? {} }]);
|
|
60
|
+
return localId;
|
|
61
|
+
}, []);
|
|
62
|
+
const setNewRowCell = useCallback((localId, col, value) => {
|
|
63
|
+
setNewRows((prev) => prev.map((r) => r._localId === localId
|
|
64
|
+
? { ...r, values: { ...r.values, [col]: value } }
|
|
65
|
+
: r));
|
|
66
|
+
}, []);
|
|
67
|
+
const removeNewRow = useCallback((localId) => {
|
|
68
|
+
setNewRows((prev) => prev.filter((r) => r._localId !== localId));
|
|
69
|
+
}, []);
|
|
70
|
+
const deleteRows = useCallback((pks) => {
|
|
71
|
+
setDeletedKeys((prev) => {
|
|
72
|
+
const next = new Set(prev);
|
|
73
|
+
for (const pk of pks) {
|
|
74
|
+
if (next.has(pk))
|
|
75
|
+
next.delete(pk);
|
|
76
|
+
else
|
|
77
|
+
next.add(pk);
|
|
78
|
+
}
|
|
79
|
+
return next;
|
|
80
|
+
});
|
|
81
|
+
}, []);
|
|
82
|
+
const undeleteRows = useCallback((pks) => {
|
|
83
|
+
setDeletedKeys((prev) => {
|
|
84
|
+
const next = new Set(prev);
|
|
85
|
+
for (const pk of pks)
|
|
86
|
+
next.delete(pk);
|
|
87
|
+
return next;
|
|
88
|
+
});
|
|
89
|
+
}, []);
|
|
90
|
+
const discardAll = useCallback(() => {
|
|
91
|
+
setEdits(new Map());
|
|
92
|
+
setNewRows([]);
|
|
93
|
+
setDeletedKeys(new Set());
|
|
94
|
+
}, []);
|
|
95
|
+
const isCellDirty = useCallback((pk, col) => edits.has(pk) && Object.prototype.hasOwnProperty.call(edits.get(pk), col), [edits]);
|
|
96
|
+
const getStagedCell = useCallback((pk, col) => {
|
|
97
|
+
const row = edits.get(pk);
|
|
98
|
+
if (row && Object.prototype.hasOwnProperty.call(row, col)) {
|
|
99
|
+
return { value: row[col] };
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}, [edits]);
|
|
103
|
+
const isDeleted = useCallback((pk) => deletedKeys.has(pk), [deletedKeys]);
|
|
104
|
+
const pendingCount = useMemo(() => {
|
|
105
|
+
// An edited row that is also deleted only counts once (as a deletion).
|
|
106
|
+
let editedNotDeleted = 0;
|
|
107
|
+
for (const pk of edits.keys()) {
|
|
108
|
+
if (!deletedKeys.has(pk))
|
|
109
|
+
editedNotDeleted += 1;
|
|
110
|
+
}
|
|
111
|
+
return editedNotDeleted + newRows.length + deletedKeys.size;
|
|
112
|
+
}, [edits, newRows, deletedKeys]);
|
|
113
|
+
const isDirty = pendingCount > 0;
|
|
114
|
+
const buildMutation = useCallback((originalRows, dryRun) => {
|
|
115
|
+
const inserts = newRows
|
|
116
|
+
.map((r) => r.values)
|
|
117
|
+
.filter((v) => Object.keys(v).length > 0);
|
|
118
|
+
const updates = [];
|
|
119
|
+
for (const [pk, set] of edits.entries()) {
|
|
120
|
+
if (deletedKeys.has(pk))
|
|
121
|
+
continue; // deletion supersedes edit
|
|
122
|
+
const original = originalRows.get(pk);
|
|
123
|
+
if (!original)
|
|
124
|
+
continue;
|
|
125
|
+
if (Object.keys(set).length === 0)
|
|
126
|
+
continue;
|
|
127
|
+
updates.push({ where: whereFor(schema, original), set });
|
|
128
|
+
}
|
|
129
|
+
const deletes = [];
|
|
130
|
+
for (const pk of deletedKeys) {
|
|
131
|
+
const original = originalRows.get(pk);
|
|
132
|
+
if (!original)
|
|
133
|
+
continue;
|
|
134
|
+
deletes.push(whereFor(schema, original));
|
|
135
|
+
}
|
|
136
|
+
const mutation = {};
|
|
137
|
+
if (inserts.length)
|
|
138
|
+
mutation.inserts = inserts;
|
|
139
|
+
if (updates.length)
|
|
140
|
+
mutation.updates = updates;
|
|
141
|
+
if (deletes.length)
|
|
142
|
+
mutation.deletes = deletes;
|
|
143
|
+
if (dryRun)
|
|
144
|
+
mutation.dryRun = true;
|
|
145
|
+
return mutation;
|
|
146
|
+
}, [edits, newRows, deletedKeys, schema]);
|
|
147
|
+
return {
|
|
148
|
+
edits,
|
|
149
|
+
newRows,
|
|
150
|
+
deletedKeys,
|
|
151
|
+
canEdit,
|
|
152
|
+
setCell,
|
|
153
|
+
setCells,
|
|
154
|
+
addRow,
|
|
155
|
+
setNewRowCell,
|
|
156
|
+
removeNewRow,
|
|
157
|
+
deleteRows,
|
|
158
|
+
undeleteRows,
|
|
159
|
+
revertCell,
|
|
160
|
+
discardAll,
|
|
161
|
+
isCellDirty,
|
|
162
|
+
getStagedCell,
|
|
163
|
+
isDeleted,
|
|
164
|
+
isDirty,
|
|
165
|
+
pendingCount,
|
|
166
|
+
buildMutation,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=changeset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changeset.js","sourceRoot":"","sources":["../../../src/client/db-admin/changeset.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo, useState } from \"react\";\nimport type {\n DbAdminTableSchema,\n DbAdminMutation,\n} from \"../../db-admin/types.js\";\n\n/**\n * The staged-changeset model — the production-grade core of the table editor.\n *\n * Edits are NOT committed on blur. Instead every cell edit, new row, and\n * deletion accumulates here until the user explicitly commits (Cmd/Ctrl+S or\n * the Commit button), at which point {@link buildMutation} maps the staged\n * state into a single {@link DbAdminMutation}. Until then the grid renders the\n * staged values overlaid on the fetched rows, and the user can discard\n * everything.\n *\n * Rows are keyed by a stable primary-key string (the table's PK column values\n * joined). If a table has no primary key, edits target a full-row `where`\n * match instead, and the consumer should warn / disable editing.\n */\n\n/** New rows get a temporary client id so the grid can track them pre-insert. */\nexport interface NewRow {\n /** Stable client-only id, e.g. `new:0`. Never sent to the server. */\n _localId: string;\n values: Record<string, unknown>;\n}\n\nexport interface Changeset {\n /** rowPkString → { column → newValue }. */\n edits: Map<string, Record<string, unknown>>;\n /** Staged inserts. */\n newRows: NewRow[];\n /** rowPkString of rows staged for deletion. */\n deletedKeys: Set<string>;\n}\n\nexport interface UseChangesetResult {\n edits: Map<string, Record<string, unknown>>;\n newRows: NewRow[];\n deletedKeys: Set<string>;\n\n /** Whether editing is possible (requires a primary key on the table). */\n canEdit: boolean;\n\n /** Stage a single cell edit on an existing row. */\n setCell: (pk: string, col: string, value: unknown) => void;\n /** Stage many cell edits on one existing row at once. */\n setCells: (pk: string, values: Record<string, unknown>) => void;\n /** Append a blank new row (optionally seeded) and return its local id. */\n addRow: (seed?: Record<string, unknown>) => string;\n /** Patch a staged new row's values. */\n setNewRowCell: (localId: string, col: string, value: unknown) => void;\n /** Remove a staged new row entirely. */\n removeNewRow: (localId: string) => void;\n /** Stage existing rows for deletion (by pk string). Toggles off if re-staged. */\n deleteRows: (pks: string[]) => void;\n /** Un-stage a deletion. */\n undeleteRows: (pks: string[]) => void;\n /** Clear a single staged cell edit (revert to original). */\n revertCell: (pk: string, col: string) => void;\n /** Drop everything. */\n discardAll: () => void;\n\n /** Whether a given existing-row cell is dirty. */\n isCellDirty: (pk: string, col: string) => boolean;\n /** The staged value for a cell, if any. */\n getStagedCell: (pk: string, col: string) => { value: unknown } | undefined;\n /** Whether an existing row is staged for deletion. */\n isDeleted: (pk: string) => boolean;\n\n isDirty: boolean;\n /** Total count of pending changes (edited rows + new rows + deletions). */\n pendingCount: number;\n\n /**\n * Build the mutation payload. `originalRows` maps pk string → the original\n * fetched row, used to construct the `where` clause for updates/deletes.\n */\n buildMutation: (\n originalRows: Map<string, Record<string, unknown>>,\n dryRun?: boolean,\n ) => DbAdminMutation;\n}\n\n/** Compute the stable pk string for a row given the schema's primary key. */\nexport function pkStringFor(\n schema: DbAdminTableSchema | undefined,\n row: Record<string, unknown>,\n): string {\n const cols =\n schema && schema.primaryKey.length > 0\n ? schema.primaryKey\n : // No PK: fall back to a full-row signature so each row is distinct.\n Object.keys(row).sort();\n return JSON.stringify(cols.map((c) => row[c] ?? null));\n}\n\n/** Build the `where` object that uniquely identifies an existing row. */\nfunction whereFor(\n schema: DbAdminTableSchema | undefined,\n row: Record<string, unknown>,\n): Record<string, unknown> {\n if (schema && schema.primaryKey.length > 0) {\n const where: Record<string, unknown> = {};\n for (const col of schema.primaryKey) where[col] = row[col] ?? null;\n return where;\n }\n // No PK — match the full original row.\n return { ...row };\n}\n\nexport function useChangeset(\n schema: DbAdminTableSchema | undefined,\n): UseChangesetResult {\n const [edits, setEdits] = useState<Map<string, Record<string, unknown>>>(\n () => new Map(),\n );\n const [newRows, setNewRows] = useState<NewRow[]>([]);\n const [deletedKeys, setDeletedKeys] = useState<Set<string>>(() => new Set());\n\n const canEdit = !!schema && schema.primaryKey.length > 0;\n\n const setCell = useCallback((pk: string, col: string, value: unknown) => {\n setEdits((prev) => {\n const next = new Map(prev);\n const row = { ...(next.get(pk) ?? {}) };\n row[col] = value;\n next.set(pk, row);\n return next;\n });\n }, []);\n\n const setCells = useCallback(\n (pk: string, values: Record<string, unknown>) => {\n setEdits((prev) => {\n const next = new Map(prev);\n const row = { ...(next.get(pk) ?? {}), ...values };\n next.set(pk, row);\n return next;\n });\n },\n [],\n );\n\n const revertCell = useCallback((pk: string, col: string) => {\n setEdits((prev) => {\n if (!prev.has(pk)) return prev;\n const next = new Map(prev);\n const row = { ...next.get(pk)! };\n delete row[col];\n if (Object.keys(row).length === 0) next.delete(pk);\n else next.set(pk, row);\n return next;\n });\n }, []);\n\n const addRow = useCallback((seed?: Record<string, unknown>) => {\n const localId = `new:${Math.random().toString(36).slice(2)}`;\n setNewRows((prev) => [...prev, { _localId: localId, values: seed ?? {} }]);\n return localId;\n }, []);\n\n const setNewRowCell = useCallback(\n (localId: string, col: string, value: unknown) => {\n setNewRows((prev) =>\n prev.map((r) =>\n r._localId === localId\n ? { ...r, values: { ...r.values, [col]: value } }\n : r,\n ),\n );\n },\n [],\n );\n\n const removeNewRow = useCallback((localId: string) => {\n setNewRows((prev) => prev.filter((r) => r._localId !== localId));\n }, []);\n\n const deleteRows = useCallback((pks: string[]) => {\n setDeletedKeys((prev) => {\n const next = new Set(prev);\n for (const pk of pks) {\n if (next.has(pk)) next.delete(pk);\n else next.add(pk);\n }\n return next;\n });\n }, []);\n\n const undeleteRows = useCallback((pks: string[]) => {\n setDeletedKeys((prev) => {\n const next = new Set(prev);\n for (const pk of pks) next.delete(pk);\n return next;\n });\n }, []);\n\n const discardAll = useCallback(() => {\n setEdits(new Map());\n setNewRows([]);\n setDeletedKeys(new Set());\n }, []);\n\n const isCellDirty = useCallback(\n (pk: string, col: string) =>\n edits.has(pk) && Object.prototype.hasOwnProperty.call(edits.get(pk), col),\n [edits],\n );\n\n const getStagedCell = useCallback(\n (pk: string, col: string) => {\n const row = edits.get(pk);\n if (row && Object.prototype.hasOwnProperty.call(row, col)) {\n return { value: row[col] };\n }\n return undefined;\n },\n [edits],\n );\n\n const isDeleted = useCallback(\n (pk: string) => deletedKeys.has(pk),\n [deletedKeys],\n );\n\n const pendingCount = useMemo(() => {\n // An edited row that is also deleted only counts once (as a deletion).\n let editedNotDeleted = 0;\n for (const pk of edits.keys()) {\n if (!deletedKeys.has(pk)) editedNotDeleted += 1;\n }\n return editedNotDeleted + newRows.length + deletedKeys.size;\n }, [edits, newRows, deletedKeys]);\n\n const isDirty = pendingCount > 0;\n\n const buildMutation = useCallback(\n (\n originalRows: Map<string, Record<string, unknown>>,\n dryRun?: boolean,\n ): DbAdminMutation => {\n const inserts: Record<string, unknown>[] = newRows\n .map((r) => r.values)\n .filter((v) => Object.keys(v).length > 0);\n\n const updates: DbAdminMutation[\"updates\"] = [];\n for (const [pk, set] of edits.entries()) {\n if (deletedKeys.has(pk)) continue; // deletion supersedes edit\n const original = originalRows.get(pk);\n if (!original) continue;\n if (Object.keys(set).length === 0) continue;\n updates.push({ where: whereFor(schema, original), set });\n }\n\n const deletes: Record<string, unknown>[] = [];\n for (const pk of deletedKeys) {\n const original = originalRows.get(pk);\n if (!original) continue;\n deletes.push(whereFor(schema, original));\n }\n\n const mutation: DbAdminMutation = {};\n if (inserts.length) mutation.inserts = inserts;\n if (updates.length) mutation.updates = updates;\n if (deletes.length) mutation.deletes = deletes;\n if (dryRun) mutation.dryRun = true;\n return mutation;\n },\n [edits, newRows, deletedKeys, schema],\n );\n\n return {\n edits,\n newRows,\n deletedKeys,\n canEdit,\n setCell,\n setCells,\n addRow,\n setNewRowCell,\n removeNewRow,\n deleteRows,\n undeleteRows,\n revertCell,\n discardAll,\n isCellDirty,\n getStagedCell,\n isDeleted,\n isDirty,\n pendingCount,\n buildMutation,\n };\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialization + download helpers for exporting SQL editor results.
|
|
3
|
+
*
|
|
4
|
+
* Kept dependency-free and SSR-safe: `downloadFile` no-ops outside the browser.
|
|
5
|
+
*/
|
|
6
|
+
/** Render rows as RFC-4180-style CSV with a header row of the given columns. */
|
|
7
|
+
export declare function toCSV(columns: string[], rows: Record<string, unknown>[]): string;
|
|
8
|
+
/** Render rows as a pretty-printed JSON array. */
|
|
9
|
+
export declare function toJSON(rows: Record<string, unknown>[]): string;
|
|
10
|
+
/**
|
|
11
|
+
* Trigger a client-side download of `content` as a file named `name` with the
|
|
12
|
+
* given MIME type. No-ops during SSR.
|
|
13
|
+
*/
|
|
14
|
+
export declare function downloadFile(name: string, mime: string, content: string): void;
|
|
15
|
+
//# sourceMappingURL=export-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-utils.d.ts","sourceRoot":"","sources":["../../../src/client/db-admin/export-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAsBH,gFAAgF;AAChF,wBAAgB,KAAK,CACnB,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAC9B,MAAM,CAMR;AAED,kDAAkD;AAClD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,CAE9D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,IAAI,CAiBN"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialization + download helpers for exporting SQL editor results.
|
|
3
|
+
*
|
|
4
|
+
* Kept dependency-free and SSR-safe: `downloadFile` no-ops outside the browser.
|
|
5
|
+
*/
|
|
6
|
+
function cellToCSV(value) {
|
|
7
|
+
if (value === null || value === undefined)
|
|
8
|
+
return "";
|
|
9
|
+
let str;
|
|
10
|
+
if (typeof value === "object") {
|
|
11
|
+
try {
|
|
12
|
+
str = JSON.stringify(value);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
str = String(value);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
str = String(value);
|
|
20
|
+
}
|
|
21
|
+
// Quote when the value contains a comma, quote, CR, or LF. Escape embedded
|
|
22
|
+
// double-quotes by doubling them (RFC 4180).
|
|
23
|
+
if (/[",\r\n]/.test(str)) {
|
|
24
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
25
|
+
}
|
|
26
|
+
return str;
|
|
27
|
+
}
|
|
28
|
+
/** Render rows as RFC-4180-style CSV with a header row of the given columns. */
|
|
29
|
+
export function toCSV(columns, rows) {
|
|
30
|
+
const header = columns.map(cellToCSV).join(",");
|
|
31
|
+
const body = rows.map((row) => columns.map((col) => cellToCSV(row[col])).join(","));
|
|
32
|
+
return [header, ...body].join("\r\n");
|
|
33
|
+
}
|
|
34
|
+
/** Render rows as a pretty-printed JSON array. */
|
|
35
|
+
export function toJSON(rows) {
|
|
36
|
+
return JSON.stringify(rows, null, 2);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Trigger a client-side download of `content` as a file named `name` with the
|
|
40
|
+
* given MIME type. No-ops during SSR.
|
|
41
|
+
*/
|
|
42
|
+
export function downloadFile(name, mime, content) {
|
|
43
|
+
if (typeof window === "undefined" || typeof document === "undefined")
|
|
44
|
+
return;
|
|
45
|
+
try {
|
|
46
|
+
const blob = new Blob([content], { type: mime });
|
|
47
|
+
const url = URL.createObjectURL(blob);
|
|
48
|
+
const anchor = document.createElement("a");
|
|
49
|
+
anchor.href = url;
|
|
50
|
+
anchor.download = name;
|
|
51
|
+
anchor.style.display = "none";
|
|
52
|
+
document.body.appendChild(anchor);
|
|
53
|
+
anchor.click();
|
|
54
|
+
document.body.removeChild(anchor);
|
|
55
|
+
// Revoke on the next tick so the click has a chance to start the download.
|
|
56
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Best-effort — nothing actionable if the browser blocks the download.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=export-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-utils.js","sourceRoot":"","sources":["../../../src/client/db-admin/export-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACrD,IAAI,GAAW,CAAC;IAChB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IACD,2EAA2E;IAC3E,6CAA6C;IAC7C,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;IACxC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,KAAK,CACnB,OAAiB,EACjB,IAA+B;IAE/B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAC5B,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CACpD,CAAC;IACF,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACxC,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,MAAM,CAAC,IAA+B;IACpD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,IAAY,EACZ,OAAe;IAEf,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IAC7E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,GAAG,GAAG,CAAC;QAClB,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC;QACvB,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAClC,2EAA2E;QAC3E,UAAU,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;IACzE,CAAC;AACH,CAAC","sourcesContent":["/**\n * Serialization + download helpers for exporting SQL editor results.\n *\n * Kept dependency-free and SSR-safe: `downloadFile` no-ops outside the browser.\n */\n\nfunction cellToCSV(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n let str: string;\n if (typeof value === \"object\") {\n try {\n str = JSON.stringify(value);\n } catch {\n str = String(value);\n }\n } else {\n str = String(value);\n }\n // Quote when the value contains a comma, quote, CR, or LF. Escape embedded\n // double-quotes by doubling them (RFC 4180).\n if (/[\",\\r\\n]/.test(str)) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\n/** Render rows as RFC-4180-style CSV with a header row of the given columns. */\nexport function toCSV(\n columns: string[],\n rows: Record<string, unknown>[],\n): string {\n const header = columns.map(cellToCSV).join(\",\");\n const body = rows.map((row) =>\n columns.map((col) => cellToCSV(row[col])).join(\",\"),\n );\n return [header, ...body].join(\"\\r\\n\");\n}\n\n/** Render rows as a pretty-printed JSON array. */\nexport function toJSON(rows: Record<string, unknown>[]): string {\n return JSON.stringify(rows, null, 2);\n}\n\n/**\n * Trigger a client-side download of `content` as a file named `name` with the\n * given MIME type. No-ops during SSR.\n */\nexport function downloadFile(\n name: string,\n mime: string,\n content: string,\n): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n try {\n const blob = new Blob([content], { type: mime });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = url;\n anchor.download = name;\n anchor.style.display = \"none\";\n document.body.appendChild(anchor);\n anchor.click();\n document.body.removeChild(anchor);\n // Revoke on the next tick so the click has a chance to start the download.\n setTimeout(() => URL.revokeObjectURL(url), 0);\n } catch {\n // Best-effort — nothing actionable if the browser blocks the download.\n }\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { DbAdminPage } from "./DbAdminPage.js";
|
|
2
|
+
export { DevDatabaseLink, type DevDatabaseLinkProps, } from "./DevDatabaseLink.js";
|
|
3
|
+
export { dbAdminBasePath, dbAdminGet, dbAdminPost, useOverview, useTableSchema, useTableRows, mutateTable, runQuery, type DbAdminOverview, type DbAdminQueryState, } from "./useDbAdmin.js";
|
|
4
|
+
export { loadGridState, saveGridState, getLS, setLS, removeLS, type GridState, } from "./storage.js";
|
|
5
|
+
export { useDbAdminAgentSync, useNavigateConsumer, type DbAdminNavigationState, } from "./useAgentSync.js";
|
|
6
|
+
export { TableBrowser, type TableBrowserProps } from "./TableBrowser.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/db-admin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,eAAe,EACf,UAAU,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,aAAa,EACb,KAAK,EACL,KAAK,EACL,QAAQ,EACR,KAAK,SAAS,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,KAAK,sBAAsB,GAC5B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { DbAdminPage } from "./DbAdminPage.js";
|
|
2
|
+
export { DevDatabaseLink, } from "./DevDatabaseLink.js";
|
|
3
|
+
// Shared data layer + helpers (re-exported for convenience).
|
|
4
|
+
export { dbAdminBasePath, dbAdminGet, dbAdminPost, useOverview, useTableSchema, useTableRows, mutateTable, runQuery, } from "./useDbAdmin.js";
|
|
5
|
+
export { loadGridState, saveGridState, getLS, setLS, removeLS, } from "./storage.js";
|
|
6
|
+
export { useDbAdminAgentSync, useNavigateConsumer, } from "./useAgentSync.js";
|
|
7
|
+
export { TableBrowser } from "./TableBrowser.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/db-admin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EACL,eAAe,GAEhB,MAAM,sBAAsB,CAAC;AAE9B,6DAA6D;AAC7D,OAAO,EACL,eAAe,EACf,UAAU,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,YAAY,EACZ,WAAW,EACX,QAAQ,GAGT,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,aAAa,EACb,KAAK,EACL,KAAK,EACL,QAAQ,GAET,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAA0B,MAAM,mBAAmB,CAAC","sourcesContent":["export { DbAdminPage } from \"./DbAdminPage.js\";\nexport {\n DevDatabaseLink,\n type DevDatabaseLinkProps,\n} from \"./DevDatabaseLink.js\";\n\n// Shared data layer + helpers (re-exported for convenience).\nexport {\n dbAdminBasePath,\n dbAdminGet,\n dbAdminPost,\n useOverview,\n useTableSchema,\n useTableRows,\n mutateTable,\n runQuery,\n type DbAdminOverview,\n type DbAdminQueryState,\n} from \"./useDbAdmin.js\";\nexport {\n loadGridState,\n saveGridState,\n getLS,\n setLS,\n removeLS,\n type GridState,\n} from \"./storage.js\";\nexport {\n useDbAdminAgentSync,\n useNavigateConsumer,\n type DbAdminNavigationState,\n} from \"./useAgentSync.js\";\nexport { TableBrowser, type TableBrowserProps } from \"./TableBrowser.js\";\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side persistence for the SQL editor surface of the dev-mode database
|
|
3
|
+
* admin. Everything lives in `localStorage` under the `agentnative.dbadmin.sql.*`
|
|
4
|
+
* namespace and is SSR-safe (no-ops when `window` is unavailable).
|
|
5
|
+
*
|
|
6
|
+
* Two stores live here:
|
|
7
|
+
* - Query HISTORY: a capped, de-duplicated list of executed SQL strings, most
|
|
8
|
+
* recent first. Useful for re-loading a previous query into the editor.
|
|
9
|
+
* - Saved SNIPPETS: named, reusable queries the user explicitly saved.
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadHistory(): string[];
|
|
12
|
+
/**
|
|
13
|
+
* Prepend an executed query to the history. Trims whitespace, skips empties,
|
|
14
|
+
* de-dupes against the most-recent entry, and caps the list length. Returns the
|
|
15
|
+
* updated list so callers can update their in-memory copy without re-reading.
|
|
16
|
+
*/
|
|
17
|
+
export declare function pushHistory(sql: string): string[];
|
|
18
|
+
export declare function clearHistory(): void;
|
|
19
|
+
export interface SqlSnippet {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
sql: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function loadSnippets(): SqlSnippet[];
|
|
25
|
+
/**
|
|
26
|
+
* Create or update a snippet. Pass an `id` to update an existing one; omit it to
|
|
27
|
+
* create a new snippet. Returns the updated list.
|
|
28
|
+
*/
|
|
29
|
+
export declare function saveSnippet(input: {
|
|
30
|
+
id?: string;
|
|
31
|
+
name: string;
|
|
32
|
+
sql: string;
|
|
33
|
+
}): SqlSnippet[];
|
|
34
|
+
export declare function deleteSnippet(id: string): SqlSnippet[];
|
|
35
|
+
//# sourceMappingURL=sql-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-storage.d.ts","sourceRoot":"","sources":["../../../src/client/db-admin/sql-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAiCH,wBAAgB,WAAW,IAAI,MAAM,EAAE,CAGtC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAajD;AAED,wBAAgB,YAAY,IAAI,IAAI,CAOnC;AAID,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAWD,wBAAgB,YAAY,IAAI,UAAU,EAAE,CAU3C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,UAAU,EAAE,CAcf;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,EAAE,CAItD"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side persistence for the SQL editor surface of the dev-mode database
|
|
3
|
+
* admin. Everything lives in `localStorage` under the `agentnative.dbadmin.sql.*`
|
|
4
|
+
* namespace and is SSR-safe (no-ops when `window` is unavailable).
|
|
5
|
+
*
|
|
6
|
+
* Two stores live here:
|
|
7
|
+
* - Query HISTORY: a capped, de-duplicated list of executed SQL strings, most
|
|
8
|
+
* recent first. Useful for re-loading a previous query into the editor.
|
|
9
|
+
* - Saved SNIPPETS: named, reusable queries the user explicitly saved.
|
|
10
|
+
*/
|
|
11
|
+
const HISTORY_KEY = "agentnative.dbadmin.sql.history";
|
|
12
|
+
const SNIPPETS_KEY = "agentnative.dbadmin.sql.snippets";
|
|
13
|
+
const HISTORY_CAP = 50;
|
|
14
|
+
function hasStorage() {
|
|
15
|
+
return typeof window !== "undefined" && !!window.localStorage;
|
|
16
|
+
}
|
|
17
|
+
function readJSON(key, fallback) {
|
|
18
|
+
if (!hasStorage())
|
|
19
|
+
return fallback;
|
|
20
|
+
try {
|
|
21
|
+
const raw = window.localStorage.getItem(key);
|
|
22
|
+
if (!raw)
|
|
23
|
+
return fallback;
|
|
24
|
+
const parsed = JSON.parse(raw);
|
|
25
|
+
return parsed ?? fallback;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return fallback;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function writeJSON(key, value) {
|
|
32
|
+
if (!hasStorage())
|
|
33
|
+
return;
|
|
34
|
+
try {
|
|
35
|
+
window.localStorage.setItem(key, JSON.stringify(value));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Quota / privacy mode — silently degrade.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// ─── History ───────────────────────────────────────────────────────────────
|
|
42
|
+
export function loadHistory() {
|
|
43
|
+
const list = readJSON(HISTORY_KEY, []);
|
|
44
|
+
return Array.isArray(list) ? list.filter((s) => typeof s === "string") : [];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Prepend an executed query to the history. Trims whitespace, skips empties,
|
|
48
|
+
* de-dupes against the most-recent entry, and caps the list length. Returns the
|
|
49
|
+
* updated list so callers can update their in-memory copy without re-reading.
|
|
50
|
+
*/
|
|
51
|
+
export function pushHistory(sql) {
|
|
52
|
+
const trimmed = sql.trim();
|
|
53
|
+
if (!trimmed)
|
|
54
|
+
return loadHistory();
|
|
55
|
+
const existing = loadHistory();
|
|
56
|
+
if (existing[0] === trimmed)
|
|
57
|
+
return existing;
|
|
58
|
+
// Remove any earlier identical entry so the list stays unique while keeping
|
|
59
|
+
// the newly executed query at the top.
|
|
60
|
+
const deduped = existing.filter((s) => s !== trimmed);
|
|
61
|
+
const next = [trimmed, ...deduped].slice(0, HISTORY_CAP);
|
|
62
|
+
writeJSON(HISTORY_KEY, next);
|
|
63
|
+
return next;
|
|
64
|
+
}
|
|
65
|
+
export function clearHistory() {
|
|
66
|
+
if (!hasStorage())
|
|
67
|
+
return;
|
|
68
|
+
try {
|
|
69
|
+
window.localStorage.removeItem(HISTORY_KEY);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// ignore
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function newId() {
|
|
76
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
77
|
+
return crypto.randomUUID();
|
|
78
|
+
}
|
|
79
|
+
return `snip-${Date.now().toString(36)}-${Math.random()
|
|
80
|
+
.toString(36)
|
|
81
|
+
.slice(2, 8)}`;
|
|
82
|
+
}
|
|
83
|
+
export function loadSnippets() {
|
|
84
|
+
const list = readJSON(SNIPPETS_KEY, []);
|
|
85
|
+
if (!Array.isArray(list))
|
|
86
|
+
return [];
|
|
87
|
+
return list.filter((s) => !!s &&
|
|
88
|
+
typeof s.id === "string" &&
|
|
89
|
+
typeof s.name === "string" &&
|
|
90
|
+
typeof s.sql === "string");
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Create or update a snippet. Pass an `id` to update an existing one; omit it to
|
|
94
|
+
* create a new snippet. Returns the updated list.
|
|
95
|
+
*/
|
|
96
|
+
export function saveSnippet(input) {
|
|
97
|
+
const name = input.name.trim();
|
|
98
|
+
const sql = input.sql.trim();
|
|
99
|
+
if (!name || !sql)
|
|
100
|
+
return loadSnippets();
|
|
101
|
+
const existing = loadSnippets();
|
|
102
|
+
let next;
|
|
103
|
+
if (input.id && existing.some((s) => s.id === input.id)) {
|
|
104
|
+
next = existing.map((s) => (s.id === input.id ? { ...s, name, sql } : s));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
next = [{ id: input.id ?? newId(), name, sql }, ...existing];
|
|
108
|
+
}
|
|
109
|
+
writeJSON(SNIPPETS_KEY, next);
|
|
110
|
+
return next;
|
|
111
|
+
}
|
|
112
|
+
export function deleteSnippet(id) {
|
|
113
|
+
const next = loadSnippets().filter((s) => s.id !== id);
|
|
114
|
+
writeJSON(SNIPPETS_KEY, next);
|
|
115
|
+
return next;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=sql-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-storage.js","sourceRoot":"","sources":["../../../src/client/db-admin/sql-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client-side persistence for the SQL editor surface of the dev-mode database\n * admin. Everything lives in `localStorage` under the `agentnative.dbadmin.sql.*`\n * namespace and is SSR-safe (no-ops when `window` is unavailable).\n *\n * Two stores live here:\n * - Query HISTORY: a capped, de-duplicated list of executed SQL strings, most\n * recent first. Useful for re-loading a previous query into the editor.\n * - Saved SNIPPETS: named, reusable queries the user explicitly saved.\n */\n\nconst HISTORY_KEY = \"agentnative.dbadmin.sql.history\";\nconst SNIPPETS_KEY = \"agentnative.dbadmin.sql.snippets\";\nconst HISTORY_CAP = 50;\n\nfunction hasStorage(): boolean {\n return typeof window !== \"undefined\" && !!window.localStorage;\n}\n\nfunction readJSON<T>(key: string, fallback: T): T {\n if (!hasStorage()) return fallback;\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) return fallback;\n const parsed = JSON.parse(raw) as T;\n return parsed ?? fallback;\n } catch {\n return fallback;\n }\n}\n\nfunction writeJSON(key: string, value: unknown): void {\n if (!hasStorage()) return;\n try {\n window.localStorage.setItem(key, JSON.stringify(value));\n } catch {\n // Quota / privacy mode — silently degrade.\n }\n}\n\n// ─── History ───────────────────────────────────────────────────────────────\n\nexport function loadHistory(): string[] {\n const list = readJSON<string[]>(HISTORY_KEY, []);\n return Array.isArray(list) ? list.filter((s) => typeof s === \"string\") : [];\n}\n\n/**\n * Prepend an executed query to the history. Trims whitespace, skips empties,\n * de-dupes against the most-recent entry, and caps the list length. Returns the\n * updated list so callers can update their in-memory copy without re-reading.\n */\nexport function pushHistory(sql: string): string[] {\n const trimmed = sql.trim();\n if (!trimmed) return loadHistory();\n\n const existing = loadHistory();\n if (existing[0] === trimmed) return existing;\n\n // Remove any earlier identical entry so the list stays unique while keeping\n // the newly executed query at the top.\n const deduped = existing.filter((s) => s !== trimmed);\n const next = [trimmed, ...deduped].slice(0, HISTORY_CAP);\n writeJSON(HISTORY_KEY, next);\n return next;\n}\n\nexport function clearHistory(): void {\n if (!hasStorage()) return;\n try {\n window.localStorage.removeItem(HISTORY_KEY);\n } catch {\n // ignore\n }\n}\n\n// ─── Snippets ────────────────────────────────────────────────────────────────\n\nexport interface SqlSnippet {\n id: string;\n name: string;\n sql: string;\n}\n\nfunction newId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID();\n }\n return `snip-${Date.now().toString(36)}-${Math.random()\n .toString(36)\n .slice(2, 8)}`;\n}\n\nexport function loadSnippets(): SqlSnippet[] {\n const list = readJSON<SqlSnippet[]>(SNIPPETS_KEY, []);\n if (!Array.isArray(list)) return [];\n return list.filter(\n (s): s is SqlSnippet =>\n !!s &&\n typeof s.id === \"string\" &&\n typeof s.name === \"string\" &&\n typeof s.sql === \"string\",\n );\n}\n\n/**\n * Create or update a snippet. Pass an `id` to update an existing one; omit it to\n * create a new snippet. Returns the updated list.\n */\nexport function saveSnippet(input: {\n id?: string;\n name: string;\n sql: string;\n}): SqlSnippet[] {\n const name = input.name.trim();\n const sql = input.sql.trim();\n if (!name || !sql) return loadSnippets();\n\n const existing = loadSnippets();\n let next: SqlSnippet[];\n if (input.id && existing.some((s) => s.id === input.id)) {\n next = existing.map((s) => (s.id === input.id ? { ...s, name, sql } : s));\n } else {\n next = [{ id: input.id ?? newId(), name, sql }, ...existing];\n }\n writeJSON(SNIPPETS_KEY, next);\n return next;\n}\n\nexport function deleteSnippet(id: string): SqlSnippet[] {\n const next = loadSnippets().filter((s) => s.id !== id);\n writeJSON(SNIPPETS_KEY, next);\n return next;\n}\n"]}
|