greenseed 0.1.3 → 0.1.5

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 ADDED
@@ -0,0 +1,61 @@
1
+ # Greenseed
2
+
3
+ Greenseed is a small CLI tool to seed a PostgreSQL database from JSON files.
4
+
5
+ It reads data from JSON files and inserts it into database tables safely using primary keys.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install greenseed
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ greenseed push
17
+ ```
18
+
19
+ Options:
20
+
21
+ ```bash
22
+ -c, --config <file> Config file (default: seed.config.json)
23
+ -d, --dbvar <name> Override database env var name
24
+ ```
25
+
26
+ ## Config file
27
+
28
+ Create a `seed.config.json` file.
29
+
30
+ Example:
31
+
32
+ ```json
33
+ {
34
+ "seedFileExtensions": [".json"],
35
+ "databaseUrlEnvVar": "DATABASE_URL",
36
+ "onMissingFile": "error",
37
+ "tables": [
38
+ {
39
+ "table": "users",
40
+ "schema": "public",
41
+ "primaryKeys": ["id"],
42
+ "source": "./data/users.json"
43
+ }
44
+ ]
45
+ }
46
+ ```
47
+
48
+ ## JSON data
49
+
50
+ Each source file must be a JSON array.
51
+
52
+ Example:
53
+
54
+ ```json
55
+ [
56
+ { "id": 1, "name": "Alice" },
57
+ { "id": 2, "name": "Bob" }
58
+ ]
59
+ ```
60
+
61
+ Extra fields not in the table are ignored.
package/dist/cli.cjs CHANGED
@@ -47,7 +47,7 @@ async function check(sql) {
47
47
  }
48
48
  async function tableExists(sql, schema, table) {
49
49
  const res = await sql`
50
- select to_regclass(${schema + "." + table}) is not null as exists
50
+ select to_regclass(${'"' + schema + '"."' + table + '"'}) is not null as exists
51
51
  `;
52
52
  if (!res[0]) {
53
53
  throw new Error("Failed checking table: " + schema + "." + table);
@@ -204,6 +204,30 @@ async function run(opts) {
204
204
  await sql.end();
205
205
  console.log(C.green + "Seed completed" + C.reset);
206
206
  }
207
+ var DEFAULT_CONFIG = {
208
+ seedFileExtensions: [".json"],
209
+ databaseUrlEnvVar: "DATABASE_URL",
210
+ onMissingFile: "error",
211
+ tables: [
212
+ {
213
+ table: "example_table",
214
+ schema: "public",
215
+ primaryKeys: ["id"],
216
+ source: "./data/example_table.json"
217
+ }
218
+ ]
219
+ };
220
+ function init(configPath) {
221
+ const resolved = path2__default.default.resolve(process.cwd(), configPath);
222
+ if (fs__default.default.existsSync(resolved)) {
223
+ throw new Error("Config already exists: " + resolved);
224
+ }
225
+ fs__default.default.writeFileSync(
226
+ resolved,
227
+ JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n",
228
+ "utf8"
229
+ );
230
+ }
207
231
 
208
232
  // src/cli.ts
209
233
  var prog = sade__default.default("greenseed");
@@ -215,6 +239,15 @@ prog.command("push").option("-c, --config <file>", "Config file", "seed.config.j
215
239
  process.exit(1);
216
240
  }
217
241
  });
242
+ prog.command("init").option("-c, --config <file>", "Config file", "seed.config.json").action((opts) => {
243
+ try {
244
+ init(opts.config);
245
+ console.log("\x1B[32mConfig created: " + opts.config + "\x1B[0m");
246
+ } catch (err) {
247
+ console.error("\x1B[31m" + String(err) + "\x1B[0m");
248
+ process.exit(1);
249
+ }
250
+ });
218
251
  prog.parse(process.argv);
219
252
  //# sourceMappingURL=cli.cjs.map
220
253
  //# sourceMappingURL=cli.cjs.map
package/dist/cli.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/cli.ts"],"names":["postgres","fs","path","v","dotenv","sade"],"mappings":"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","file":"cli.cjs","sourcesContent":["import postgres from \"postgres\"\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tthrow new Error(\"Database connection failed\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${schema + \".\" + table}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tthrow new Error(\"Failed checking table: \" + schema + \".\" + table)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tthrow new Error(\"Missing table: \" + t.schema + \".\" + t.table)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tthrow new Error(\"No primary keys for \" + schema + \".\" + table)\n\t}\n\n\tconst q = await sql`\n\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t${sql(rows, columns)}\n\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t`\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tthrow new Error(\"Failed to read file: \" + p)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tthrow new Error(\"Source file not found: \" + p)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tthrow new Error(\"Invalid JSON: \" + p)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error(\"Data is not an array: \" + p)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tconsole.log(C.gray + \"Starting seed process\" + C.reset)\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tconsole.log(C.yellow + \"Skipped \" + t.table + C.reset)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t)\n\t\t}\n\n\t\tconsole.log(\n\t\t\tC.green +\n\t\t\t\tt.schema +\n\t\t\t\t\".\" +\n\t\t\t\tt.table +\n\t\t\t\t\": \" +\n\t\t\t\tinserted +\n\t\t\t\t\"/\" +\n\t\t\t\trows.length +\n\t\t\t\tC.reset,\n\t\t)\n\t}\n\n\tawait sql.end()\n\tconsole.log(C.green + \"Seed completed\" + C.reset)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
1
+ {"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/init.ts","../src/cli.ts"],"names":["postgres","fs","path","v","dotenv","sade"],"mappings":"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","file":"cli.cjs","sourcesContent":["import postgres from \"postgres\"\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tthrow new Error(\"Database connection failed\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${\"\\\"\" + schema + \"\\\".\\\"\" + table + \"\\\"\"}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tthrow new Error(\"Failed checking table: \" + schema + \".\" + table)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tthrow new Error(\"Missing table: \" + t.schema + \".\" + t.table)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tthrow new Error(\"No primary keys for \" + schema + \".\" + table)\n\t}\n\n\tconst q = await sql`\n\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t${sql(rows, columns)}\n\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t`\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tthrow new Error(\"Failed to read file: \" + p)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tthrow new Error(\"Source file not found: \" + p)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tthrow new Error(\"Invalid JSON: \" + p)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error(\"Data is not an array: \" + p)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tconsole.log(C.gray + \"Starting seed process\" + C.reset)\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tconsole.log(C.yellow + \"Skipped \" + t.table + C.reset)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t)\n\t\t}\n\n\t\tconsole.log(\n\t\t\tC.green +\n\t\t\t\tt.schema +\n\t\t\t\t\".\" +\n\t\t\t\tt.table +\n\t\t\t\t\": \" +\n\t\t\t\tinserted +\n\t\t\t\t\"/\" +\n\t\t\t\trows.length +\n\t\t\t\tC.reset,\n\t\t)\n\t}\n\n\tawait sql.end()\n\tconsole.log(C.green + \"Seed completed\" + C.reset)\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst DEFAULT_CONFIG = {\n\tseedFileExtensions: [\".json\"],\n\tdatabaseUrlEnvVar: \"DATABASE_URL\",\n\tonMissingFile: \"error\",\n\ttables: [\n\t\t{\n\t\t\ttable: \"example_table\",\n\t\t\tschema: \"public\",\n\t\t\tprimaryKeys: [\"id\"],\n\t\t\tsource: \"./data/example_table.json\",\n\t\t},\n\t],\n}\n\nexport function init(configPath: string): void {\n\tconst resolved = path.resolve(process.cwd(), configPath)\n\n\tif (fs.existsSync(resolved)) {\n\t\tthrow new Error(\"Config already exists: \" + resolved)\n\t}\n\n\tfs.writeFileSync(\n\t\tresolved,\n\t\tJSON.stringify(DEFAULT_CONFIG, null, 2) + \"\\n\",\n\t\t\"utf8\",\n\t)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\nimport { init } from \"./init\"\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog\n\t.command(\"init\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.action((opts) => {\n\t\ttry {\n\t\t\tinit(opts.config)\n\t\t\tconsole.log(\"\\x1b[32mConfig created: \" + opts.config + \"\\x1b[0m\")\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
package/dist/cli.js CHANGED
@@ -18,7 +18,7 @@ async function check(sql) {
18
18
  }
19
19
  async function tableExists(sql, schema, table) {
20
20
  const res = await sql`
21
- select to_regclass(${schema + "." + table}) is not null as exists
21
+ select to_regclass(${'"' + schema + '"."' + table + '"'}) is not null as exists
22
22
  `;
23
23
  if (!res[0]) {
24
24
  throw new Error("Failed checking table: " + schema + "." + table);
@@ -175,6 +175,30 @@ async function run(opts) {
175
175
  await sql.end();
176
176
  console.log(C.green + "Seed completed" + C.reset);
177
177
  }
178
+ var DEFAULT_CONFIG = {
179
+ seedFileExtensions: [".json"],
180
+ databaseUrlEnvVar: "DATABASE_URL",
181
+ onMissingFile: "error",
182
+ tables: [
183
+ {
184
+ table: "example_table",
185
+ schema: "public",
186
+ primaryKeys: ["id"],
187
+ source: "./data/example_table.json"
188
+ }
189
+ ]
190
+ };
191
+ function init(configPath) {
192
+ const resolved = path2.resolve(process.cwd(), configPath);
193
+ if (fs.existsSync(resolved)) {
194
+ throw new Error("Config already exists: " + resolved);
195
+ }
196
+ fs.writeFileSync(
197
+ resolved,
198
+ JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n",
199
+ "utf8"
200
+ );
201
+ }
178
202
 
179
203
  // src/cli.ts
180
204
  var prog = sade("greenseed");
@@ -186,6 +210,15 @@ prog.command("push").option("-c, --config <file>", "Config file", "seed.config.j
186
210
  process.exit(1);
187
211
  }
188
212
  });
213
+ prog.command("init").option("-c, --config <file>", "Config file", "seed.config.json").action((opts) => {
214
+ try {
215
+ init(opts.config);
216
+ console.log("\x1B[32mConfig created: " + opts.config + "\x1B[0m");
217
+ } catch (err) {
218
+ console.error("\x1B[31m" + String(err) + "\x1B[0m");
219
+ process.exit(1);
220
+ }
221
+ });
189
222
  prog.parse(process.argv);
190
223
  //# sourceMappingURL=cli.js.map
191
224
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/cli.ts"],"names":["path"],"mappings":"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","file":"cli.js","sourcesContent":["import postgres from \"postgres\"\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tthrow new Error(\"Database connection failed\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${schema + \".\" + table}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tthrow new Error(\"Failed checking table: \" + schema + \".\" + table)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tthrow new Error(\"Missing table: \" + t.schema + \".\" + t.table)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tthrow new Error(\"No primary keys for \" + schema + \".\" + table)\n\t}\n\n\tconst q = await sql`\n\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t${sql(rows, columns)}\n\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t`\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tthrow new Error(\"Failed to read file: \" + p)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tthrow new Error(\"Source file not found: \" + p)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tthrow new Error(\"Invalid JSON: \" + p)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error(\"Data is not an array: \" + p)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tconsole.log(C.gray + \"Starting seed process\" + C.reset)\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tconsole.log(C.yellow + \"Skipped \" + t.table + C.reset)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t)\n\t\t}\n\n\t\tconsole.log(\n\t\t\tC.green +\n\t\t\t\tt.schema +\n\t\t\t\t\".\" +\n\t\t\t\tt.table +\n\t\t\t\t\": \" +\n\t\t\t\tinserted +\n\t\t\t\t\"/\" +\n\t\t\t\trows.length +\n\t\t\t\tC.reset,\n\t\t)\n\t}\n\n\tawait sql.end()\n\tconsole.log(C.green + \"Seed completed\" + C.reset)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
1
+ {"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/init.ts","../src/cli.ts"],"names":["path","fs"],"mappings":"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","file":"cli.js","sourcesContent":["import postgres from \"postgres\"\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tthrow new Error(\"Database connection failed\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${\"\\\"\" + schema + \"\\\".\\\"\" + table + \"\\\"\"}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tthrow new Error(\"Failed checking table: \" + schema + \".\" + table)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tthrow new Error(\"Missing table: \" + t.schema + \".\" + t.table)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tthrow new Error(\"No primary keys for \" + schema + \".\" + table)\n\t}\n\n\tconst q = await sql`\n\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t${sql(rows, columns)}\n\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t`\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tthrow new Error(\"Failed to read file: \" + p)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tthrow new Error(\"Source file not found: \" + p)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tthrow new Error(\"Invalid JSON: \" + p)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error(\"Data is not an array: \" + p)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tconsole.log(C.gray + \"Starting seed process\" + C.reset)\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tconsole.log(C.yellow + \"Skipped \" + t.table + C.reset)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t)\n\t\t}\n\n\t\tconsole.log(\n\t\t\tC.green +\n\t\t\t\tt.schema +\n\t\t\t\t\".\" +\n\t\t\t\tt.table +\n\t\t\t\t\": \" +\n\t\t\t\tinserted +\n\t\t\t\t\"/\" +\n\t\t\t\trows.length +\n\t\t\t\tC.reset,\n\t\t)\n\t}\n\n\tawait sql.end()\n\tconsole.log(C.green + \"Seed completed\" + C.reset)\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst DEFAULT_CONFIG = {\n\tseedFileExtensions: [\".json\"],\n\tdatabaseUrlEnvVar: \"DATABASE_URL\",\n\tonMissingFile: \"error\",\n\ttables: [\n\t\t{\n\t\t\ttable: \"example_table\",\n\t\t\tschema: \"public\",\n\t\t\tprimaryKeys: [\"id\"],\n\t\t\tsource: \"./data/example_table.json\",\n\t\t},\n\t],\n}\n\nexport function init(configPath: string): void {\n\tconst resolved = path.resolve(process.cwd(), configPath)\n\n\tif (fs.existsSync(resolved)) {\n\t\tthrow new Error(\"Config already exists: \" + resolved)\n\t}\n\n\tfs.writeFileSync(\n\t\tresolved,\n\t\tJSON.stringify(DEFAULT_CONFIG, null, 2) + \"\\n\",\n\t\t\"utf8\",\n\t)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\nimport { init } from \"./init\"\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog\n\t.command(\"init\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.action((opts) => {\n\t\ttry {\n\t\t\tinit(opts.config)\n\t\t\tconsole.log(\"\\x1b[32mConfig created: \" + opts.config + \"\\x1b[0m\")\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "greenseed",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "author": "Mohit Khatri",
5
5
  "main": "./dist/cli.js",
6
6
  "module": "./dist/index.js",
@@ -35,7 +35,8 @@
35
35
  "dev": "tsup --watch",
36
36
  "typecheck": "tsc --noEmit",
37
37
  "clean": "rm -rf dist",
38
- "format": "biome format . --write"
38
+ "format": "biome format . --write",
39
+ "prepublish": "npm run build"
39
40
  },
40
41
  "type": "module",
41
42
  "types": "./dist/index.d.ts",