@noy-db/as-sql 0.6.0 → 0.7.0-pre.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.
package/README.md CHANGED
@@ -14,7 +14,7 @@ pnpm add @noy-db/hub @noy-db/as-sql
14
14
 
15
15
  ## What it is
16
16
 
17
- SQL dump export for noy-db — decrypts records and emits dialect-aware CREATE TABLE + INSERT statements for postgres / mysql / sqlite. One-way migration helper. Gated by RFC #249 canExportPlaintext.
17
+ SQL dump export for noy-db — decrypts records and emits dialect-aware CREATE TABLE + INSERT statements for postgres / mysql / sqlite. One-way migration helper. Gated by `vault.assertCanExport('plaintext', …)`.
18
18
 
19
19
  ## Status
20
20
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Vault } from '@noy-db/hub';
2
+ import { NoydbFormat } from '@noy-db/hub/as';
2
3
 
3
4
  /**
4
5
  * **@noy-db/as-sql** — SQL dump export for migration.
@@ -63,8 +64,38 @@ interface AsSQLWriteOptions extends AsSQLOptions {
63
64
  /** Required for Node file-write — Tier 3 risk gate. */
64
65
  readonly acknowledgeRisks: true;
65
66
  }
66
- declare function toString(vault: Vault, options?: AsSQLOptions): Promise<string>;
67
+ /** Options a SQL format instance carries. Read concerns live on `vault.export`. */
68
+ interface AsSQLFormatOptions {
69
+ /** Target dialect. Default `'postgres'`. */
70
+ readonly dialect?: SqlDialect;
71
+ /** Schema + data, schema only, or data only. Default `'schema+data'`. */
72
+ readonly mode?: SqlMode;
73
+ /** Map collection name → table name. Default identity. */
74
+ readonly tableNames?: (collection: string) => string;
75
+ /** Include `_noydb_version` / `_noydb_ts` metadata columns. Default `false`. */
76
+ readonly metadataColumns?: boolean;
77
+ /**
78
+ * Collection allowlist, kept here as well as on `vault.export` because the
79
+ * encoder buckets by collection. `readOpts` forwards it to the READ so hub
80
+ * does not decrypt what will not be emitted.
81
+ */
82
+ readonly include?: readonly string[];
83
+ }
84
+ /**
85
+ * The SQL format — the `as-*` port instance.
86
+ *
87
+ * Export-only: SQL has no round-trip here, so `decode` is absent and
88
+ * `vault.import(asSql(), …)` throws naming the format. That is a property of
89
+ * this implementation, not of the contract.
90
+ */
91
+ declare function asSql(options?: AsSQLFormatOptions): NoydbFormat<string>;
92
+ /** Browser download. Hub gates, reads and redacts; this wraps the bytes. */
67
93
  declare function download(vault: Vault, options?: AsSQLDownloadOptions): Promise<void>;
94
+ /**
95
+ * Node file write. Not in hub because `hub-portable` forbids Node builtins
96
+ * there — hub must run in a browser, Worker, Deno and Bun. The gate, the read
97
+ * and the redaction all moved; these lines are the runtime-specific part.
98
+ */
68
99
  declare function write(vault: Vault, path: string, options: AsSQLWriteOptions): Promise<void>;
69
100
 
70
- export { type AsSQLDownloadOptions, type AsSQLOptions, type AsSQLWriteOptions, type SqlDialect, type SqlMode, download, toString, write };
101
+ export { type AsSQLDownloadOptions, type AsSQLFormatOptions, type AsSQLOptions, type AsSQLWriteOptions, type SqlDialect, type SqlMode, asSql, download, write };
package/dist/index.js CHANGED
@@ -1,21 +1,14 @@
1
1
  // src/index.ts
2
- import { applyListProjection } from "@noy-db/hub";
3
- async function toString(vault, options = {}) {
4
- vault.assertCanExport("plaintext", "sql");
2
+ function encodeSql(chunks, options = {}) {
5
3
  const dialect = options.dialect ?? "postgres";
6
4
  const mode = options.mode ?? "schema+data";
7
5
  const tableName = options.tableNames ?? ((c) => c);
8
6
  const includeAll = !options.include || options.include.length === 0;
9
7
  const allowlist = options.include ? new Set(options.include) : null;
10
8
  const buckets = /* @__PURE__ */ new Map();
11
- for await (const chunk of vault.exportStream({ granularity: "collection" })) {
9
+ for (const chunk of chunks) {
12
10
  if (!includeAll && allowlist && !allowlist.has(chunk.collection)) continue;
13
- let records = chunk.records.map((r) => stripMeta(r));
14
- if (options.redact !== void 0 && options.redact !== false) {
15
- const desc = vault.collection(chunk.collection).describe();
16
- const projectionOpts = options.redact === true ? void 0 : { sensitivity: options.redact.sensitivity };
17
- records = records.map((r) => applyListProjection(desc, r, projectionOpts));
18
- }
11
+ const records = chunk.records.map((r) => stripMeta(r));
19
12
  const bucket = buckets.get(chunk.collection) ?? [];
20
13
  bucket.push(...records);
21
14
  buckets.set(chunk.collection, bucket);
@@ -40,26 +33,40 @@ async function toString(vault, options = {}) {
40
33
  }
41
34
  return parts.join("\n");
42
35
  }
36
+ function asSql(options = {}) {
37
+ return {
38
+ id: "sql",
39
+ extension: "sql",
40
+ mimeType: "application/sql;charset=utf-8",
41
+ tier: "plaintext",
42
+ encode: (chunks) => encodeSql(chunks, options)
43
+ };
44
+ }
45
+ function readOpts(o) {
46
+ return {
47
+ ...o.include ? { collections: o.include } : {},
48
+ ...o.redact !== void 0 && o.redact !== false ? { redact: o.redact === true ? true : { sensitivity: o.redact.sensitivity } } : {}
49
+ };
50
+ }
43
51
  async function download(vault, options = {}) {
44
- const sql = await toString(vault, options);
45
- const filename = options.filename ?? "vault-export.sql";
46
- const blob = new Blob([sql], { type: "application/sql;charset=utf-8" });
47
- const url = URL.createObjectURL(blob);
52
+ const fmt = asSql(options);
53
+ const sql = await vault.export(fmt, readOpts(options));
54
+ const url = URL.createObjectURL(new Blob([sql], { type: fmt.mimeType }));
48
55
  const a = document.createElement("a");
49
56
  a.href = url;
50
- a.download = filename;
57
+ a.download = options.filename ?? `vault-export.${fmt.extension}`;
51
58
  a.click();
52
59
  URL.revokeObjectURL(url);
53
60
  }
54
61
  async function write(vault, path, options) {
55
62
  if (options.acknowledgeRisks !== true) {
56
63
  throw new Error(
57
- 'as-sql.write: acknowledgeRisks: true is required for on-disk plaintext output. See docs/patterns/as-exports.md \xA7"The three tiers of \\"plaintext out\\""'
64
+ "as-sql.write: acknowledgeRisks: true is required for on-disk plaintext output."
58
65
  );
59
66
  }
60
- const sql = await toString(vault, options);
67
+ const sql = await vault.export(asSql(options), readOpts(options));
61
68
  const { writeFile } = await import("fs/promises");
62
- await writeFile(path, sql, "utf-8");
69
+ await writeFile(path, sql, "utf8");
63
70
  }
64
71
  function inferSchema(records, includeMeta) {
65
72
  const observed = /* @__PURE__ */ new Map();
@@ -166,8 +173,8 @@ function stripMeta(record) {
166
173
  return out;
167
174
  }
168
175
  export {
176
+ asSql,
169
177
  download,
170
- toString,
171
178
  write
172
179
  };
173
180
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * **@noy-db/as-sql** — SQL dump export for migration.\n *\n * One-way, at-export-time string formatter that emits dialect-aware\n * `CREATE TABLE` + `INSERT INTO` statements. This is NOT a runtime\n * SQL query frontend — it's the migration bridge for consumers moving\n * noy-db data into Postgres, MySQL, or SQLite.\n *\n * Column types are inferred from value types in the first 100 records\n * per collection. Every record's row uses parameterless inline-literal\n * INSERT (no prepared statements) so the dump loads with a single\n * `psql -f`, `mysql <`, or `sqlite3 < dump.sql` invocation.\n *\n * **Zero dependencies** — hand-rolled string formatter, ~300 LoC.\n *\n * See `docs/patterns/as-exports.md` for the three-tier egress model.\n *\n * @packageDocumentation\n */\n\nimport { applyListProjection, type Vault, type CollectionDescription } from '@noy-db/hub'\n\nexport type SqlDialect = 'postgres' | 'mysql' | 'sqlite'\nexport type SqlMode = 'schema-only' | 'data-only' | 'schema+data'\n\nexport interface AsSQLOptions {\n /** Target dialect. Default `'postgres'`. */\n readonly dialect?: SqlDialect\n /** Collection allowlist. Omit for all. */\n readonly include?: readonly string[]\n /** Schema + data, schema only, or data only. Default `'schema+data'`. */\n readonly mode?: SqlMode\n /** Map collection name → table name. Default identity. */\n readonly tableNames?: (collection: string) => string\n /** Include `_noydb_version` / `_noydb_ts` metadata columns. Default `false`. */\n readonly metadataColumns?: boolean\n\n /**\n * Apply the hub's `applyListProjection` read-projection before\n * serialising rows. `true` redacts only `classifiedFields` (mask /\n * omit / rider, per the field's preset). The object form additionally\n * redacts fields carrying a plain `fieldMeta` `sensitivity: 'pii' |\n * 'secret'` tag, per `sensitivity: 'omit' | 'mask'`.\n *\n * Caveat: `describe()` reflects the declarations of *this session's*\n * collection instance — redaction only takes effect when the\n * collection was opened (this call or earlier in the session) with\n * its `classifiedFields` / `fieldMeta` options. This is presentation-\n * layer redaction; it never affects what's on disk. Sealed handles\n * are unaffected either way — they always serialize as `'[sealed]'`,\n * so ciphertext never leaks regardless of this option.\n *\n * Rider companion fields (e.g. `pan_last4`) remain visible as their\n * own columns — they are safe write-time projections.\n */\n readonly redact?: boolean | { readonly sensitivity: 'omit' | 'mask' }\n}\n\nexport interface AsSQLDownloadOptions extends AsSQLOptions {\n /** Filename offered to the browser. Default `'vault-export.sql'`. */\n readonly filename?: string\n}\n\nexport interface AsSQLWriteOptions extends AsSQLOptions {\n /** Required for Node file-write — Tier 3 risk gate. */\n readonly acknowledgeRisks: true\n}\n\nexport async function toString(vault: Vault, options: AsSQLOptions = {}): Promise<string> {\n vault.assertCanExport('plaintext', 'sql')\n\n const dialect = options.dialect ?? 'postgres'\n const mode = options.mode ?? 'schema+data'\n const tableName = options.tableNames ?? ((c: string) => c)\n const includeAll = !options.include || options.include.length === 0\n const allowlist = options.include ? new Set(options.include) : null\n\n // Bucket records by collection so we can emit schema+data atomically.\n const buckets = new Map<string, Record<string, unknown>[]>()\n for await (const chunk of vault.exportStream({ granularity: 'collection' })) {\n if (!includeAll && allowlist && !allowlist.has(chunk.collection)) continue\n let records = chunk.records.map(r => stripMeta(r as Record<string, unknown>))\n\n // Apply redaction projection if specified.\n if (options.redact !== undefined && options.redact !== false) {\n const desc: CollectionDescription = vault.collection(chunk.collection).describe()\n const projectionOpts = options.redact === true ? undefined\n : { sensitivity: options.redact.sensitivity }\n records = records.map((r) => applyListProjection(desc, r, projectionOpts))\n }\n\n const bucket = buckets.get(chunk.collection) ?? []\n bucket.push(...records)\n buckets.set(chunk.collection, bucket)\n }\n\n const parts: string[] = []\n parts.push(`-- Generated by @noy-db/as-sql · dialect: ${dialect} · mode: ${mode}`)\n parts.push(`-- Bundle snapshot — NOT a live connection. Load with: ${loadCommand(dialect)}`)\n parts.push('')\n\n for (const [collection, records] of buckets) {\n const table = tableName(collection)\n const schema = inferSchema(records, options.metadataColumns === true)\n if (mode !== 'data-only') {\n parts.push(createTable(dialect, table, schema))\n parts.push('')\n }\n if (mode !== 'schema-only') {\n for (const rec of records) {\n parts.push(insertRow(dialect, table, schema, rec))\n }\n parts.push('')\n }\n }\n\n return parts.join('\\n')\n}\n\nexport async function download(vault: Vault, options: AsSQLDownloadOptions = {}): Promise<void> {\n const sql = await toString(vault, options)\n const filename = options.filename ?? 'vault-export.sql'\n const blob = new Blob([sql], { type: 'application/sql;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n a.click()\n URL.revokeObjectURL(url)\n}\n\nexport async function write(vault: Vault, path: string, options: AsSQLWriteOptions): Promise<void> {\n if (options.acknowledgeRisks !== true) {\n throw new Error(\n 'as-sql.write: acknowledgeRisks: true is required for on-disk plaintext output. ' +\n 'See docs/patterns/as-exports.md §\"The three tiers of \\\\\"plaintext out\\\\\"\"',\n )\n }\n const sql = await toString(vault, options)\n const { writeFile } = await import('node:fs/promises')\n await writeFile(path, sql, 'utf-8')\n}\n\n// ─── SQL formatting internals ───────────────────────────────────────────\n\ntype SqlType = 'text' | 'integer' | 'real' | 'boolean' | 'timestamp' | 'jsonb'\n\ninterface ColumnSchema {\n readonly name: string\n readonly type: SqlType\n readonly nullable: boolean\n}\n\nfunction inferSchema(records: readonly Record<string, unknown>[], includeMeta: boolean): ColumnSchema[] {\n const observed = new Map<string, { types: Set<SqlType>; nullable: boolean }>()\n const sample = records.slice(0, 100) // sample up to 100 rows for type inference\n for (const rec of sample) {\n for (const key of Object.keys(rec)) {\n if (!observed.has(key)) observed.set(key, { types: new Set(), nullable: false })\n const slot = observed.get(key)!\n const value = rec[key]\n if (value === null || value === undefined) {\n slot.nullable = true\n continue\n }\n slot.types.add(inferType(value))\n }\n // Fields missing from this record → nullable\n for (const key of observed.keys()) {\n if (!(key in rec)) observed.get(key)!.nullable = true\n }\n }\n\n const columns: ColumnSchema[] = []\n for (const [name, info] of observed) {\n if (!includeMeta && (name === '_v' || name === '_ts' || name === '_by')) continue\n const type: SqlType = info.types.size === 1\n ? [...info.types][0]!\n : 'text' // mixed types → fall back to text\n columns.push({ name, type, nullable: info.nullable })\n }\n if (includeMeta) {\n columns.push({ name: '_noydb_version', type: 'integer', nullable: true })\n columns.push({ name: '_noydb_ts', type: 'timestamp', nullable: true })\n }\n return columns\n}\n\nfunction inferType(value: unknown): SqlType {\n if (typeof value === 'boolean') return 'boolean'\n if (typeof value === 'number') return Number.isInteger(value) ? 'integer' : 'real'\n if (value instanceof Date) return 'timestamp'\n if (typeof value === 'string') {\n if (/^\\d{4}-\\d{2}-\\d{2}T/.test(value)) return 'timestamp'\n return 'text'\n }\n return 'jsonb'\n}\n\nfunction createTable(dialect: SqlDialect, table: string, schema: readonly ColumnSchema[]): string {\n const cols = schema.map(c => ` ${quoteIdent(dialect, c.name)} ${mapType(dialect, c.type)}${c.nullable ? '' : ' NOT NULL'}`)\n return `CREATE TABLE ${quoteIdent(dialect, table)} (\\n${cols.join(',\\n')}\\n);`\n}\n\nfunction mapType(dialect: SqlDialect, type: SqlType): string {\n const map: Record<SqlDialect, Record<SqlType, string>> = {\n postgres: { text: 'TEXT', integer: 'INTEGER', real: 'REAL', boolean: 'BOOLEAN', timestamp: 'TIMESTAMPTZ', jsonb: 'JSONB' },\n mysql: { text: 'TEXT', integer: 'BIGINT', real: 'DOUBLE', boolean: 'TINYINT(1)', timestamp: 'DATETIME', jsonb: 'JSON' },\n sqlite: { text: 'TEXT', integer: 'INTEGER', real: 'REAL', boolean: 'INTEGER', timestamp: 'TEXT', jsonb: 'TEXT' },\n }\n return map[dialect][type]\n}\n\nfunction insertRow(\n dialect: SqlDialect,\n table: string,\n schema: readonly ColumnSchema[],\n record: Record<string, unknown>,\n): string {\n const cols = schema.map(c => quoteIdent(dialect, c.name)).join(', ')\n const values = schema.map(c => formatLiteral(dialect, c.type, record[c.name])).join(', ')\n return `INSERT INTO ${quoteIdent(dialect, table)} (${cols}) VALUES (${values});`\n}\n\nfunction formatLiteral(dialect: SqlDialect, type: SqlType, value: unknown): string {\n if (value === null || value === undefined) return 'NULL'\n if (type === 'boolean') {\n if (dialect === 'mysql' || dialect === 'sqlite') return value ? '1' : '0'\n return value ? 'TRUE' : 'FALSE'\n }\n if (type === 'integer' || type === 'real') return stringifyScalar(value)\n if (type === 'timestamp') {\n const s = value instanceof Date ? value.toISOString() : stringifyScalar(value)\n return quoteString(dialect, s)\n }\n if (type === 'jsonb') {\n return quoteString(dialect, JSON.stringify(value))\n }\n return quoteString(dialect, stringifyScalar(value))\n}\n\nfunction stringifyScalar(value: unknown): string {\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value)\n }\n if (value === null || value === undefined) return ''\n return JSON.stringify(value)\n}\n\nfunction quoteString(dialect: SqlDialect, s: string): string {\n // Both standard SQL and common dialects use '' to escape single quotes.\n const escaped = s.replace(/'/g, \"''\")\n return `'${escaped}'`\n}\n\nfunction quoteIdent(dialect: SqlDialect, name: string): string {\n if (dialect === 'mysql') return `\\`${name.replace(/`/g, '``')}\\``\n return `\"${name.replace(/\"/g, '\"\"')}\"`\n}\n\nfunction loadCommand(dialect: SqlDialect): string {\n if (dialect === 'postgres') return 'psql -f dump.sql'\n if (dialect === 'mysql') return 'mysql -u <user> -p < dump.sql'\n return 'sqlite3 <database>.db < dump.sql'\n}\n\nfunction stripMeta(record: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(record)) {\n if (key === '_iv' || key === '_data' || key === '_noydb') continue\n out[key] = value\n }\n return out\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * **@noy-db/as-sql** — SQL dump export for migration.\n *\n * One-way, at-export-time string formatter that emits dialect-aware\n * `CREATE TABLE` + `INSERT INTO` statements. This is NOT a runtime\n * SQL query frontend — it's the migration bridge for consumers moving\n * noy-db data into Postgres, MySQL, or SQLite.\n *\n * Column types are inferred from value types in the first 100 records\n * per collection. Every record's row uses parameterless inline-literal\n * INSERT (no prepared statements) so the dump loads with a single\n * `psql -f`, `mysql <`, or `sqlite3 < dump.sql` invocation.\n *\n * **Zero dependencies** — hand-rolled string formatter, ~300 LoC.\n *\n * See `docs/patterns/as-exports.md` for the three-tier egress model.\n *\n * @packageDocumentation\n */\n\nimport type { Vault } from '@noy-db/hub'\nimport type { ExportChunk, NoydbFormat, FormatExportOptions } from '@noy-db/hub/as'\n\nexport type SqlDialect = 'postgres' | 'mysql' | 'sqlite'\nexport type SqlMode = 'schema-only' | 'data-only' | 'schema+data'\n\nexport interface AsSQLOptions {\n /** Target dialect. Default `'postgres'`. */\n readonly dialect?: SqlDialect\n /** Collection allowlist. Omit for all. */\n readonly include?: readonly string[]\n /** Schema + data, schema only, or data only. Default `'schema+data'`. */\n readonly mode?: SqlMode\n /** Map collection name → table name. Default identity. */\n readonly tableNames?: (collection: string) => string\n /** Include `_noydb_version` / `_noydb_ts` metadata columns. Default `false`. */\n readonly metadataColumns?: boolean\n\n /**\n * Apply the hub's `applyListProjection` read-projection before\n * serialising rows. `true` redacts only `classifiedFields` (mask /\n * omit / rider, per the field's preset). The object form additionally\n * redacts fields carrying a plain `fieldMeta` `sensitivity: 'pii' |\n * 'secret'` tag, per `sensitivity: 'omit' | 'mask'`.\n *\n * Caveat: `describe()` reflects the declarations of *this session's*\n * collection instance — redaction only takes effect when the\n * collection was opened (this call or earlier in the session) with\n * its `classifiedFields` / `fieldMeta` options. This is presentation-\n * layer redaction; it never affects what's on disk. Sealed handles\n * are unaffected either way — they always serialize as `'[sealed]'`,\n * so ciphertext never leaks regardless of this option.\n *\n * Rider companion fields (e.g. `pan_last4`) remain visible as their\n * own columns — they are safe write-time projections.\n */\n readonly redact?: boolean | { readonly sensitivity: 'omit' | 'mask' }\n}\n\nexport interface AsSQLDownloadOptions extends AsSQLOptions {\n /** Filename offered to the browser. Default `'vault-export.sql'`. */\n readonly filename?: string\n}\n\nexport interface AsSQLWriteOptions extends AsSQLOptions {\n /** Required for Node file-write — Tier 3 risk gate. */\n readonly acknowledgeRisks: true\n}\n\n/**\n * The pure encoder — records in, SQL out. Already gated and already redacted\n * by hub; it has no vault (ADR 0004).\n */\nfunction encodeSql(chunks: readonly ExportChunk[], options: AsSQLFormatOptions = {}): string {\n const dialect = options.dialect ?? 'postgres'\n const mode = options.mode ?? 'schema+data'\n const tableName = options.tableNames ?? ((c: string) => c)\n const includeAll = !options.include || options.include.length === 0\n const allowlist = options.include ? new Set(options.include) : null\n\n // Bucket records by collection so we can emit schema+data atomically.\n const buckets = new Map<string, Record<string, unknown>[]>()\n for (const chunk of chunks) {\n if (!includeAll && allowlist && !allowlist.has(chunk.collection)) continue\n const records = chunk.records.map((r: unknown) => stripMeta(r as Record<string, unknown>))\n\n\n const bucket = buckets.get(chunk.collection) ?? []\n bucket.push(...records)\n buckets.set(chunk.collection, bucket)\n }\n\n const parts: string[] = []\n parts.push(`-- Generated by @noy-db/as-sql · dialect: ${dialect} · mode: ${mode}`)\n parts.push(`-- Bundle snapshot — NOT a live connection. Load with: ${loadCommand(dialect)}`)\n parts.push('')\n\n for (const [collection, records] of buckets) {\n const table = tableName(collection)\n const schema = inferSchema(records, options.metadataColumns === true)\n if (mode !== 'data-only') {\n parts.push(createTable(dialect, table, schema))\n parts.push('')\n }\n if (mode !== 'schema-only') {\n for (const rec of records) {\n parts.push(insertRow(dialect, table, schema, rec))\n }\n parts.push('')\n }\n }\n\n return parts.join('\\n')\n}\n\n/** Options a SQL format instance carries. Read concerns live on `vault.export`. */\nexport interface AsSQLFormatOptions {\n /** Target dialect. Default `'postgres'`. */\n readonly dialect?: SqlDialect\n /** Schema + data, schema only, or data only. Default `'schema+data'`. */\n readonly mode?: SqlMode\n /** Map collection name → table name. Default identity. */\n readonly tableNames?: (collection: string) => string\n /** Include `_noydb_version` / `_noydb_ts` metadata columns. Default `false`. */\n readonly metadataColumns?: boolean\n /**\n * Collection allowlist, kept here as well as on `vault.export` because the\n * encoder buckets by collection. `readOpts` forwards it to the READ so hub\n * does not decrypt what will not be emitted.\n */\n readonly include?: readonly string[]\n}\n\n/**\n * The SQL format — the `as-*` port instance.\n *\n * Export-only: SQL has no round-trip here, so `decode` is absent and\n * `vault.import(asSql(), …)` throws naming the format. That is a property of\n * this implementation, not of the contract.\n */\nexport function asSql(options: AsSQLFormatOptions = {}): NoydbFormat<string> {\n return {\n id: 'sql',\n extension: 'sql',\n mimeType: 'application/sql;charset=utf-8',\n tier: 'plaintext',\n encode: (chunks) => encodeSql(chunks, options),\n }\n}\n\n/** Only the keys actually set — `exactOptionalPropertyTypes` is on. */\nfunction readOpts(o: AsSQLOptions): FormatExportOptions {\n return {\n ...(o.include ? { collections: o.include } : {}),\n ...(o.redact !== undefined && o.redact !== false\n ? { redact: o.redact === true ? true : { sensitivity: o.redact.sensitivity } }\n : {}),\n }\n}\n\n/** Browser download. Hub gates, reads and redacts; this wraps the bytes. */\nexport async function download(vault: Vault, options: AsSQLDownloadOptions = {}): Promise<void> {\n const fmt = asSql(options)\n const sql = await vault.export(fmt, readOpts(options))\n const url = URL.createObjectURL(new Blob([sql], { type: fmt.mimeType }))\n const a = document.createElement('a')\n a.href = url\n a.download = options.filename ?? `vault-export.${fmt.extension}`\n a.click()\n URL.revokeObjectURL(url)\n}\n\n/**\n * Node file write. Not in hub because `hub-portable` forbids Node builtins\n * there — hub must run in a browser, Worker, Deno and Bun. The gate, the read\n * and the redaction all moved; these lines are the runtime-specific part.\n */\nexport async function write(vault: Vault, path: string, options: AsSQLWriteOptions): Promise<void> {\n if (options.acknowledgeRisks !== true) {\n throw new Error(\n 'as-sql.write: acknowledgeRisks: true is required for on-disk plaintext output.',\n )\n }\n const sql = await vault.export(asSql(options), readOpts(options))\n const { writeFile } = await import('node:fs/promises')\n await writeFile(path, sql, 'utf8')\n}\n\n\n// ─── SQL formatting internals ───────────────────────────────────────────\n\ntype SqlType = 'text' | 'integer' | 'real' | 'boolean' | 'timestamp' | 'jsonb'\n\ninterface ColumnSchema {\n readonly name: string\n readonly type: SqlType\n readonly nullable: boolean\n}\n\nfunction inferSchema(records: readonly Record<string, unknown>[], includeMeta: boolean): ColumnSchema[] {\n const observed = new Map<string, { types: Set<SqlType>; nullable: boolean }>()\n const sample = records.slice(0, 100) // sample up to 100 rows for type inference\n for (const rec of sample) {\n for (const key of Object.keys(rec)) {\n if (!observed.has(key)) observed.set(key, { types: new Set(), nullable: false })\n const slot = observed.get(key)!\n const value = rec[key]\n if (value === null || value === undefined) {\n slot.nullable = true\n continue\n }\n slot.types.add(inferType(value))\n }\n // Fields missing from this record → nullable\n for (const key of observed.keys()) {\n if (!(key in rec)) observed.get(key)!.nullable = true\n }\n }\n\n const columns: ColumnSchema[] = []\n for (const [name, info] of observed) {\n if (!includeMeta && (name === '_v' || name === '_ts' || name === '_by')) continue\n const type: SqlType = info.types.size === 1\n ? [...info.types][0]!\n : 'text' // mixed types → fall back to text\n columns.push({ name, type, nullable: info.nullable })\n }\n if (includeMeta) {\n columns.push({ name: '_noydb_version', type: 'integer', nullable: true })\n columns.push({ name: '_noydb_ts', type: 'timestamp', nullable: true })\n }\n return columns\n}\n\nfunction inferType(value: unknown): SqlType {\n if (typeof value === 'boolean') return 'boolean'\n if (typeof value === 'number') return Number.isInteger(value) ? 'integer' : 'real'\n if (value instanceof Date) return 'timestamp'\n if (typeof value === 'string') {\n if (/^\\d{4}-\\d{2}-\\d{2}T/.test(value)) return 'timestamp'\n return 'text'\n }\n return 'jsonb'\n}\n\nfunction createTable(dialect: SqlDialect, table: string, schema: readonly ColumnSchema[]): string {\n const cols = schema.map(c => ` ${quoteIdent(dialect, c.name)} ${mapType(dialect, c.type)}${c.nullable ? '' : ' NOT NULL'}`)\n return `CREATE TABLE ${quoteIdent(dialect, table)} (\\n${cols.join(',\\n')}\\n);`\n}\n\nfunction mapType(dialect: SqlDialect, type: SqlType): string {\n const map: Record<SqlDialect, Record<SqlType, string>> = {\n postgres: { text: 'TEXT', integer: 'INTEGER', real: 'REAL', boolean: 'BOOLEAN', timestamp: 'TIMESTAMPTZ', jsonb: 'JSONB' },\n mysql: { text: 'TEXT', integer: 'BIGINT', real: 'DOUBLE', boolean: 'TINYINT(1)', timestamp: 'DATETIME', jsonb: 'JSON' },\n sqlite: { text: 'TEXT', integer: 'INTEGER', real: 'REAL', boolean: 'INTEGER', timestamp: 'TEXT', jsonb: 'TEXT' },\n }\n return map[dialect][type]\n}\n\nfunction insertRow(\n dialect: SqlDialect,\n table: string,\n schema: readonly ColumnSchema[],\n record: Record<string, unknown>,\n): string {\n const cols = schema.map(c => quoteIdent(dialect, c.name)).join(', ')\n const values = schema.map(c => formatLiteral(dialect, c.type, record[c.name])).join(', ')\n return `INSERT INTO ${quoteIdent(dialect, table)} (${cols}) VALUES (${values});`\n}\n\nfunction formatLiteral(dialect: SqlDialect, type: SqlType, value: unknown): string {\n if (value === null || value === undefined) return 'NULL'\n if (type === 'boolean') {\n if (dialect === 'mysql' || dialect === 'sqlite') return value ? '1' : '0'\n return value ? 'TRUE' : 'FALSE'\n }\n if (type === 'integer' || type === 'real') return stringifyScalar(value)\n if (type === 'timestamp') {\n const s = value instanceof Date ? value.toISOString() : stringifyScalar(value)\n return quoteString(dialect, s)\n }\n if (type === 'jsonb') {\n return quoteString(dialect, JSON.stringify(value))\n }\n return quoteString(dialect, stringifyScalar(value))\n}\n\nfunction stringifyScalar(value: unknown): string {\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value)\n }\n if (value === null || value === undefined) return ''\n return JSON.stringify(value)\n}\n\nfunction quoteString(dialect: SqlDialect, s: string): string {\n // Both standard SQL and common dialects use '' to escape single quotes.\n const escaped = s.replace(/'/g, \"''\")\n return `'${escaped}'`\n}\n\nfunction quoteIdent(dialect: SqlDialect, name: string): string {\n if (dialect === 'mysql') return `\\`${name.replace(/`/g, '``')}\\``\n return `\"${name.replace(/\"/g, '\"\"')}\"`\n}\n\nfunction loadCommand(dialect: SqlDialect): string {\n if (dialect === 'postgres') return 'psql -f dump.sql'\n if (dialect === 'mysql') return 'mysql -u <user> -p < dump.sql'\n return 'sqlite3 <database>.db < dump.sql'\n}\n\nfunction stripMeta(record: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(record)) {\n if (key === '_iv' || key === '_data' || key === '_noydb') continue\n out[key] = value\n }\n return out\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@noy-db/as-sql",
3
- "version": "0.6.0",
4
- "description": "SQL dump export for noy-db — decrypts records and emits dialect-aware CREATE TABLE + INSERT statements for postgres / mysql / sqlite. One-way migration helper. Gated by RFC #249 canExportPlaintext.",
3
+ "version": "0.7.0-pre.1",
4
+ "description": "SQL dump export for noy-db — decrypts records and emits dialect-aware CREATE TABLE + INSERT statements for postgres / mysql / sqlite. One-way migration helper. Gated by `vault.assertCanExport('plaintext', …)`.",
5
5
  "license": "MIT",
6
6
  "author": "vLannaAi <vicio@lanna.ai>",
7
7
  "homepage": "https://github.com/vLannaAi/noy-db/tree/main/packages/as-sql#readme",
@@ -32,11 +32,11 @@
32
32
  "node": ">=22.0.0"
33
33
  },
34
34
  "peerDependencies": {
35
- "@noy-db/hub": "0.6.0"
35
+ "@noy-db/hub": "0.7.0-pre.1"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^22.0.0",
39
- "@noy-db/hub": "0.6.0"
39
+ "@noy-db/hub": "0.7.0-pre.1"
40
40
  },
41
41
  "keywords": [
42
42
  "noy-db",