greenseed 0.1.4 → 0.1.6

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
@@ -35,6 +35,12 @@ var path2__default = /*#__PURE__*/_interopDefault(path2);
35
35
  var postgres__default = /*#__PURE__*/_interopDefault(postgres);
36
36
  var fs__default = /*#__PURE__*/_interopDefault(fs);
37
37
 
38
+ var C = {
39
+ reset: "\x1B[0m",
40
+ dim: "\x1B[2m",
41
+ red: "\x1B[31m"
42
+ };
43
+ var logError = (msg) => console.error(`${C.red}\u2717${C.reset} ${C.dim}${msg}${C.reset}`);
38
44
  function connect(url) {
39
45
  return postgres__default.default(url, { max: 10, idle_timeout: 20 });
40
46
  }
@@ -42,7 +48,8 @@ async function check(sql) {
42
48
  try {
43
49
  await sql`select 1`;
44
50
  } catch (err) {
45
- throw new Error("Database connection failed");
51
+ logError("Database connection failed");
52
+ throw new Error("Failed to connect to database");
46
53
  }
47
54
  }
48
55
  async function tableExists(sql, schema, table) {
@@ -50,7 +57,8 @@ async function tableExists(sql, schema, table) {
50
57
  select to_regclass(${'"' + schema + '"."' + table + '"'}) is not null as exists
51
58
  `;
52
59
  if (!res[0]) {
53
- throw new Error("Failed checking table: " + schema + "." + table);
60
+ logError(`Failed to check table: ${schema}.${table}`);
61
+ throw new Error(`Table check failed: ${schema}.${table}`);
54
62
  }
55
63
  return res[0].exists;
56
64
  }
@@ -58,7 +66,8 @@ async function validateTables(sql, tables) {
58
66
  for (const t of tables) {
59
67
  const ok = await tableExists(sql, t.schema, t.table);
60
68
  if (!ok) {
61
- throw new Error("Missing table: " + t.schema + "." + t.table);
69
+ logError(`Table does not exist: ${t.schema}.${t.table}`);
70
+ throw new Error(`Missing table: ${t.schema}.${t.table}`);
62
71
  }
63
72
  }
64
73
  }
@@ -72,18 +81,42 @@ async function metadata(sql, schema, table) {
72
81
  `;
73
82
  return { columns: cols.map((c) => c.column_name) };
74
83
  }
75
- async function insert(sql, schema, table, rows, columns, primaryKeys) {
84
+ async function insert(sql, schema, table, rows, columns, primaryKeys, updateOnConflict) {
76
85
  if (primaryKeys.length === 0) {
77
- throw new Error("No primary keys for " + schema + "." + table);
86
+ logError(`No primary keys defined for ${schema}.${table}`);
87
+ throw new Error(`No primary keys for ${schema}.${table}`);
88
+ }
89
+ const updateCols = updateOnConflict.filter(
90
+ (col) => !primaryKeys.includes(col)
91
+ );
92
+ let q;
93
+ if (updateCols.length > 0) {
94
+ const updates = Object.fromEntries(
95
+ updateCols.map((col) => [col, sql`excluded.${sql(col)}`])
96
+ );
97
+ q = await sql`
98
+ insert into ${sql(schema + "." + table)}
99
+ ${sql(rows, columns)}
100
+ on conflict (${sql(primaryKeys)}) do update set
101
+ ${sql(updates)}
102
+ returning ${sql(primaryKeys[0] ?? [])}
103
+ `;
104
+ } else {
105
+ q = await sql`
106
+ insert into ${sql(schema + "." + table)}
107
+ ${sql(rows, columns)}
108
+ on conflict (${sql(primaryKeys)}) do nothing
109
+ returning ${sql(primaryKeys[0] ?? [])}
110
+ `;
78
111
  }
79
- const q = await sql`
80
- insert into ${sql(schema + "." + table)}
81
- ${sql(rows, columns)}
82
- on conflict (${sql(primaryKeys)}) do nothing
83
- returning ${sql(primaryKeys[0] ?? [])}
84
- `;
85
112
  return q.length;
86
113
  }
114
+ var C2 = {
115
+ reset: "\x1B[0m",
116
+ dim: "\x1B[2m",
117
+ red: "\x1B[31m"
118
+ };
119
+ var logError2 = (msg) => console.error(`${C2.red}\u2717${C2.reset} ${C2.dim}${msg}${C2.reset}`);
87
120
  function exists(p) {
88
121
  return fs__default.default.existsSync(p);
89
122
  }
@@ -91,7 +124,8 @@ function readFile(p) {
91
124
  try {
92
125
  return fs__default.default.readFileSync(p, "utf8");
93
126
  } catch (err) {
94
- throw new Error("Failed to read file: " + p);
127
+ logError2(`Failed to read file: ${p}`);
128
+ throw new Error(`File read error: ${p}`);
95
129
  }
96
130
  }
97
131
  function resolvePath(p) {
@@ -102,17 +136,20 @@ function loadJsonArray(p, onMissing) {
102
136
  if (onMissing === "skip") {
103
137
  return [];
104
138
  }
105
- throw new Error("Source file not found: " + p);
139
+ logError2(`Source file not found: ${p}`);
140
+ throw new Error(`Missing file: ${p}`);
106
141
  }
107
142
  const raw = readFile(p);
108
143
  let parsed;
109
144
  try {
110
145
  parsed = JSON.parse(raw);
111
146
  } catch {
112
- throw new Error("Invalid JSON: " + p);
147
+ logError2(`Invalid JSON in file: ${p}`);
148
+ throw new Error(`JSON parse error: ${p}`);
113
149
  }
114
150
  if (!Array.isArray(parsed)) {
115
- throw new Error("Data is not an array: " + p);
151
+ logError2(`Expected array but got ${typeof parsed}: ${p}`);
152
+ throw new Error(`Invalid data format: ${p}`);
116
153
  }
117
154
  return parsed;
118
155
  }
@@ -125,11 +162,33 @@ function chunk(data, size) {
125
162
  }
126
163
 
127
164
  // src/app.ts
128
- var C = {
165
+ var C3 = {
129
166
  reset: "\x1B[0m",
167
+ bold: "\x1B[1m",
168
+ dim: "\x1B[2m",
130
169
  green: "\x1B[32m",
131
170
  yellow: "\x1B[33m",
132
- gray: "\x1B[90m"
171
+ blue: "\x1B[34m",
172
+ cyan: "\x1B[36m",
173
+ red: "\x1B[31m"};
174
+ var log = {
175
+ info: (msg) => console.log(`${C3.blue}\u2139${C3.reset} ${msg}`),
176
+ success: (msg) => console.log(`${C3.green}\u2713${C3.reset} ${msg}`),
177
+ warn: (msg) => console.log(`${C3.yellow}\u26A0${C3.reset} ${msg}`),
178
+ error: (msg) => console.log(`${C3.red}\u2717${C3.reset} ${msg}`),
179
+ dim: (msg) => console.log(`${C3.dim}${msg}${C3.reset}`),
180
+ header: (msg) => console.log(`
181
+ ${C3.bold}${C3.cyan}${msg}${C3.reset}
182
+ `),
183
+ table: (label, value) => console.log(` ${C3.dim}${label}:${C3.reset} ${C3.bold}${value}${C3.reset}`),
184
+ progress: (current, total, label) => {
185
+ const percentage = Math.round(current / total * 100);
186
+ const bar = "\u2588".repeat(Math.floor(percentage / 5));
187
+ const empty = "\u2591".repeat(20 - Math.floor(percentage / 5));
188
+ console.log(
189
+ ` ${C3.cyan}${bar}${empty}${C3.reset} ${C3.bold}${percentage}%${C3.reset} ${C3.dim}(${current}/${total})${C3.reset} ${label}`
190
+ );
191
+ }
133
192
  };
134
193
  var ConfigSchema = v__namespace.object({
135
194
  seedFileExtensions: v__namespace.array(v__namespace.picklist([".json"])),
@@ -140,13 +199,15 @@ var ConfigSchema = v__namespace.object({
140
199
  table: v__namespace.string(),
141
200
  schema: v__namespace.optional(v__namespace.string(), "public"),
142
201
  primaryKeys: v__namespace.array(v__namespace.string()),
143
- source: v__namespace.string()
202
+ source: v__namespace.string(),
203
+ updateOnConflict: v__namespace.optional(v__namespace.nullable(v__namespace.array(v__namespace.string())), null)
144
204
  })
145
205
  )
146
206
  });
147
207
  async function run(opts) {
148
208
  dotenv__default.default.config();
149
209
  const configPath = resolvePath(opts.config || "seed.config.json");
210
+ log.header("\u{1F331} GreenSeed");
150
211
  let raw;
151
212
  try {
152
213
  raw = JSON.parse(readFile(configPath));
@@ -165,6 +226,8 @@ async function run(opts) {
165
226
  if (!dbUrl) {
166
227
  throw new Error("Missing DB env var: " + dbVar);
167
228
  }
229
+ log.info(`Using config: ${C3.dim}${configPath}${C3.reset}`);
230
+ log.info(`Database: ${C3.dim}${dbVar}${C3.reset}`);
168
231
  const sql = connect(dbUrl);
169
232
  await check(sql);
170
233
  await validateTables(
@@ -174,18 +237,20 @@ async function run(opts) {
174
237
  schema: t.schema
175
238
  }))
176
239
  );
177
- console.log(C.gray + "Starting seed process" + C.reset);
240
+ log.success("Database connection established");
241
+ log.header("Seeding Tables");
178
242
  for (const t of config.tables) {
179
243
  const source = path2__default.default.resolve(path2__default.default.dirname(configPath), t.source);
180
244
  const rows = loadJsonArray(source, config.onMissingFile || "error");
181
245
  if (rows.length === 0) {
182
- console.log(C.yellow + "Skipped " + t.table + C.reset);
246
+ log.warn(`Skipped ${C3.bold}${t.schema}.${t.table}${C3.reset} (no data)`);
183
247
  continue;
184
248
  }
185
249
  const meta = await metadata(sql, t.schema, t.table);
186
250
  const validCols = Object.keys(rows[0]).filter(
187
251
  (c) => meta.columns.includes(c)
188
252
  );
253
+ log.info(`Seeding ${C3.bold}${t.schema}.${t.table}${C3.reset}`);
189
254
  let inserted = 0;
190
255
  for (const part of chunk(rows, 1e3)) {
191
256
  inserted += await insert(
@@ -194,24 +259,87 @@ async function run(opts) {
194
259
  t.table,
195
260
  part,
196
261
  validCols,
197
- t.primaryKeys
262
+ t.primaryKeys,
263
+ t.updateOnConflict || []
264
+ );
265
+ }
266
+ const hasConflict = t.updateOnConflict && t.updateOnConflict.length > 0;
267
+ const action = hasConflict ? "upserted" : "inserted";
268
+ const skipped = rows.length - inserted;
269
+ if (inserted === rows.length) {
270
+ log.success(
271
+ `${C3.bold}${t.schema}.${t.table}${C3.reset} ${C3.green}${action} ${inserted} rows${C3.reset}`
272
+ );
273
+ } else {
274
+ log.success(
275
+ `${C3.bold}${t.schema}.${t.table}${C3.reset} ${C3.green}${action} ${inserted}${C3.reset}${C3.dim}, skipped ${skipped}${C3.reset}`
198
276
  );
199
277
  }
200
- console.log(
201
- C.green + t.schema + "." + t.table + ": " + inserted + "/" + rows.length + C.reset
202
- );
203
278
  }
204
279
  await sql.end();
205
- console.log(C.green + "Seed completed" + C.reset);
280
+ log.header("Summary");
281
+ log.success(
282
+ `Seeded ${C3.bold}${config.tables.length}${C3.reset} table${config.tables.length === 1 ? "" : "s"}`
283
+ );
284
+ }
285
+ var C4 = {
286
+ reset: "\x1B[0m",
287
+ dim: "\x1B[2m",
288
+ red: "\x1B[31m"
289
+ };
290
+ var logError3 = (msg) => console.error(`${C4.red}\u2717${C4.reset} ${C4.dim}${msg}${C4.reset}`);
291
+ var DEFAULT_CONFIG = {
292
+ seedFileExtensions: [".json"],
293
+ databaseUrlEnvVar: "DATABASE_URL",
294
+ onMissingFile: "error",
295
+ tables: [
296
+ {
297
+ table: "example_table",
298
+ schema: "public",
299
+ primaryKeys: ["id"],
300
+ source: "./data/example_table.json",
301
+ updateOnConflict: []
302
+ }
303
+ ]
304
+ };
305
+ function init(configPath) {
306
+ const resolved = path2__default.default.resolve(process.cwd(), configPath);
307
+ if (fs__default.default.existsSync(resolved)) {
308
+ logError3(`Config file already exists: ${resolved}`);
309
+ throw new Error(`Config already exists: ${resolved}`);
310
+ }
311
+ fs__default.default.writeFileSync(
312
+ resolved,
313
+ JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n",
314
+ "utf8"
315
+ );
206
316
  }
207
317
 
208
318
  // src/cli.ts
319
+ var C5 = {
320
+ reset: "\x1B[0m",
321
+ bold: "\x1B[1m",
322
+ green: "\x1B[32m",
323
+ red: "\x1B[31m"
324
+ };
325
+ var logError4 = (err) => console.error(`${C5.red}${C5.bold}Error:${C5.reset} ${String(err)}`);
209
326
  var prog = sade__default.default("greenseed");
210
327
  prog.command("push").option("-c, --config <file>", "Config file", "seed.config.json").option("-d, --dbvar <name>", "DB env var override").action(async (opts) => {
211
328
  try {
212
329
  await run(opts);
213
330
  } catch (err) {
214
- console.error("\x1B[31m" + String(err) + "\x1B[0m");
331
+ logError4(err);
332
+ process.exit(1);
333
+ }
334
+ });
335
+ prog.command("init").option("-c, --config <file>", "Config file", "seed.config.json").action((opts) => {
336
+ try {
337
+ init(opts.config);
338
+ console.log(
339
+ `${C5.green}\u2713${C5.reset} Config created: ${C5.bold}${opts.config}${C5.reset}`
340
+ );
341
+ } catch (err) {
342
+ logError4(err);
215
343
  process.exit(1);
216
344
  }
217
345
  });
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","C","logError","fs","path","v","dotenv","sade"],"mappings":"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","file":"cli.cjs","sourcesContent":["import postgres from \"postgres\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\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\tlogError(\"Database connection failed\")\n\t\tthrow new Error(\"Failed to connect to database\")\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\tlogError(`Failed to check table: ${schema}.${table}`)\n\t\tthrow new Error(`Table check failed: ${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\tlogError(`Table does not exist: ${t.schema}.${t.table}`)\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\tupdateOnConflict: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tlogError(`No primary keys defined for ${schema}.${table}`)\n\t\tthrow new Error(`No primary keys for ${schema}.${table}`)\n\t}\n\n\tconst updateCols = updateOnConflict.filter(\n\t\t(col) => !primaryKeys.includes(col),\n\t)\n\n\tlet q\n\tif (updateCols.length > 0) {\n\t\tconst updates = Object.fromEntries(\n\t\t\tupdateCols.map((col) => [col, sql`excluded.${sql(col)}`]),\n\t\t)\n\n\t\tq = await sql`\n\t\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t\t${sql(rows, columns)}\n\t\t\ton conflict (${sql(primaryKeys)}) do update set\n\t\t\t${sql(updates)}\n\t\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t\t`\n\t} else {\n\t\tq = await sql`\n\t\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t\t${sql(rows, columns)}\n\t\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t\t`\n\t}\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\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\tlogError(`Failed to read file: ${p}`)\n\t\tthrow new Error(`File read error: ${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\tlogError(`Source file not found: ${p}`)\n\t\tthrow new Error(`Missing file: ${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\tlogError(`Invalid JSON in file: ${p}`)\n\t\tthrow new Error(`JSON parse error: ${p}`)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tlogError(`Expected array but got ${typeof parsed}: ${p}`)\n\t\tthrow new Error(`Invalid data format: ${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\tbold: \"\\x1b[1m\",\n\tdim: \"\\x1b[2m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tblue: \"\\x1b[34m\",\n\tcyan: \"\\x1b[36m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst log = {\n\tinfo: (msg: string) => console.log(`${C.blue}ℹ${C.reset} ${msg}`),\n\tsuccess: (msg: string) => console.log(`${C.green}✓${C.reset} ${msg}`),\n\twarn: (msg: string) => console.log(`${C.yellow}⚠${C.reset} ${msg}`),\n\terror: (msg: string) => console.log(`${C.red}✗${C.reset} ${msg}`),\n\tdim: (msg: string) => console.log(`${C.dim}${msg}${C.reset}`),\n\theader: (msg: string) =>\n\t\tconsole.log(`\\n${C.bold}${C.cyan}${msg}${C.reset}\\n`),\n\ttable: (label: string, value: string | number) =>\n\t\tconsole.log(` ${C.dim}${label}:${C.reset} ${C.bold}${value}${C.reset}`),\n\tprogress: (current: number, total: number, label: string) => {\n\t\tconst percentage = Math.round((current / total) * 100)\n\t\tconst bar = \"█\".repeat(Math.floor(percentage / 5))\n\t\tconst empty = \"░\".repeat(20 - Math.floor(percentage / 5))\n\t\tconsole.log(\n\t\t\t` ${C.cyan}${bar}${empty}${C.reset} ${C.bold}${percentage}%${C.reset} ${C.dim}(${current}/${total})${C.reset} ${label}`,\n\t\t)\n\t},\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\tupdateOnConflict: v.optional(v.nullable(v.array(v.string())), null),\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\tlog.header(\"🌱 GreenSeed\")\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\tlog.info(`Using config: ${C.dim}${configPath}${C.reset}`)\n\tlog.info(`Database: ${C.dim}${dbVar}${C.reset}`)\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\tlog.success(\"Database connection established\")\n\tlog.header(\"Seeding Tables\")\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\tlog.warn(`Skipped ${C.bold}${t.schema}.${t.table}${C.reset} (no data)`)\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\tlog.info(`Seeding ${C.bold}${t.schema}.${t.table}${C.reset}`)\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\tt.updateOnConflict || [],\n\t\t\t)\n\t\t}\n\n\t\tconst hasConflict = t.updateOnConflict && t.updateOnConflict.length > 0\n\t\tconst action = hasConflict ? \"upserted\" : \"inserted\"\n\t\tconst skipped = rows.length - inserted\n\n\t\tif (inserted === rows.length) {\n\t\t\tlog.success(\n\t\t\t\t`${C.bold}${t.schema}.${t.table}${C.reset} ${C.green}${action} ${inserted} rows${C.reset}`,\n\t\t\t)\n\t\t} else {\n\t\t\tlog.success(\n\t\t\t\t`${C.bold}${t.schema}.${t.table}${C.reset} ${C.green}${action} ${inserted}${C.reset}${C.dim}, skipped ${skipped}${C.reset}`,\n\t\t\t)\n\t\t}\n\t}\n\n\tawait sql.end()\n\n\tlog.header(\"Summary\")\n\tlog.success(\n\t\t`Seeded ${C.bold}${config.tables.length}${C.reset} table${config.tables.length === 1 ? \"\" : \"s\"}`,\n\t)\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\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\tupdateOnConflict: [],\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\tlogError(`Config file already exists: ${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 C = {\n\treset: \"\\x1b[0m\",\n\tbold: \"\\x1b[1m\",\n\tgreen: \"\\x1b[32m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (err: unknown) =>\n\tconsole.error(`${C.red}${C.bold}Error:${C.reset} ${String(err)}`)\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\tlogError(err)\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(\n\t\t\t\t`${C.green}✓${C.reset} Config created: ${C.bold}${opts.config}${C.reset}`,\n\t\t\t)\n\t\t} catch (err) {\n\t\t\tlogError(err)\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
package/dist/cli.js CHANGED
@@ -6,6 +6,12 @@ import path2 from 'path';
6
6
  import postgres from 'postgres';
7
7
  import fs from 'fs';
8
8
 
9
+ var C = {
10
+ reset: "\x1B[0m",
11
+ dim: "\x1B[2m",
12
+ red: "\x1B[31m"
13
+ };
14
+ var logError = (msg) => console.error(`${C.red}\u2717${C.reset} ${C.dim}${msg}${C.reset}`);
9
15
  function connect(url) {
10
16
  return postgres(url, { max: 10, idle_timeout: 20 });
11
17
  }
@@ -13,7 +19,8 @@ async function check(sql) {
13
19
  try {
14
20
  await sql`select 1`;
15
21
  } catch (err) {
16
- throw new Error("Database connection failed");
22
+ logError("Database connection failed");
23
+ throw new Error("Failed to connect to database");
17
24
  }
18
25
  }
19
26
  async function tableExists(sql, schema, table) {
@@ -21,7 +28,8 @@ async function tableExists(sql, schema, table) {
21
28
  select to_regclass(${'"' + schema + '"."' + table + '"'}) is not null as exists
22
29
  `;
23
30
  if (!res[0]) {
24
- throw new Error("Failed checking table: " + schema + "." + table);
31
+ logError(`Failed to check table: ${schema}.${table}`);
32
+ throw new Error(`Table check failed: ${schema}.${table}`);
25
33
  }
26
34
  return res[0].exists;
27
35
  }
@@ -29,7 +37,8 @@ async function validateTables(sql, tables) {
29
37
  for (const t of tables) {
30
38
  const ok = await tableExists(sql, t.schema, t.table);
31
39
  if (!ok) {
32
- throw new Error("Missing table: " + t.schema + "." + t.table);
40
+ logError(`Table does not exist: ${t.schema}.${t.table}`);
41
+ throw new Error(`Missing table: ${t.schema}.${t.table}`);
33
42
  }
34
43
  }
35
44
  }
@@ -43,18 +52,42 @@ async function metadata(sql, schema, table) {
43
52
  `;
44
53
  return { columns: cols.map((c) => c.column_name) };
45
54
  }
46
- async function insert(sql, schema, table, rows, columns, primaryKeys) {
55
+ async function insert(sql, schema, table, rows, columns, primaryKeys, updateOnConflict) {
47
56
  if (primaryKeys.length === 0) {
48
- throw new Error("No primary keys for " + schema + "." + table);
57
+ logError(`No primary keys defined for ${schema}.${table}`);
58
+ throw new Error(`No primary keys for ${schema}.${table}`);
59
+ }
60
+ const updateCols = updateOnConflict.filter(
61
+ (col) => !primaryKeys.includes(col)
62
+ );
63
+ let q;
64
+ if (updateCols.length > 0) {
65
+ const updates = Object.fromEntries(
66
+ updateCols.map((col) => [col, sql`excluded.${sql(col)}`])
67
+ );
68
+ q = await sql`
69
+ insert into ${sql(schema + "." + table)}
70
+ ${sql(rows, columns)}
71
+ on conflict (${sql(primaryKeys)}) do update set
72
+ ${sql(updates)}
73
+ returning ${sql(primaryKeys[0] ?? [])}
74
+ `;
75
+ } else {
76
+ q = await sql`
77
+ insert into ${sql(schema + "." + table)}
78
+ ${sql(rows, columns)}
79
+ on conflict (${sql(primaryKeys)}) do nothing
80
+ returning ${sql(primaryKeys[0] ?? [])}
81
+ `;
49
82
  }
50
- const q = await sql`
51
- insert into ${sql(schema + "." + table)}
52
- ${sql(rows, columns)}
53
- on conflict (${sql(primaryKeys)}) do nothing
54
- returning ${sql(primaryKeys[0] ?? [])}
55
- `;
56
83
  return q.length;
57
84
  }
85
+ var C2 = {
86
+ reset: "\x1B[0m",
87
+ dim: "\x1B[2m",
88
+ red: "\x1B[31m"
89
+ };
90
+ var logError2 = (msg) => console.error(`${C2.red}\u2717${C2.reset} ${C2.dim}${msg}${C2.reset}`);
58
91
  function exists(p) {
59
92
  return fs.existsSync(p);
60
93
  }
@@ -62,7 +95,8 @@ function readFile(p) {
62
95
  try {
63
96
  return fs.readFileSync(p, "utf8");
64
97
  } catch (err) {
65
- throw new Error("Failed to read file: " + p);
98
+ logError2(`Failed to read file: ${p}`);
99
+ throw new Error(`File read error: ${p}`);
66
100
  }
67
101
  }
68
102
  function resolvePath(p) {
@@ -73,17 +107,20 @@ function loadJsonArray(p, onMissing) {
73
107
  if (onMissing === "skip") {
74
108
  return [];
75
109
  }
76
- throw new Error("Source file not found: " + p);
110
+ logError2(`Source file not found: ${p}`);
111
+ throw new Error(`Missing file: ${p}`);
77
112
  }
78
113
  const raw = readFile(p);
79
114
  let parsed;
80
115
  try {
81
116
  parsed = JSON.parse(raw);
82
117
  } catch {
83
- throw new Error("Invalid JSON: " + p);
118
+ logError2(`Invalid JSON in file: ${p}`);
119
+ throw new Error(`JSON parse error: ${p}`);
84
120
  }
85
121
  if (!Array.isArray(parsed)) {
86
- throw new Error("Data is not an array: " + p);
122
+ logError2(`Expected array but got ${typeof parsed}: ${p}`);
123
+ throw new Error(`Invalid data format: ${p}`);
87
124
  }
88
125
  return parsed;
89
126
  }
@@ -96,11 +133,33 @@ function chunk(data, size) {
96
133
  }
97
134
 
98
135
  // src/app.ts
99
- var C = {
136
+ var C3 = {
100
137
  reset: "\x1B[0m",
138
+ bold: "\x1B[1m",
139
+ dim: "\x1B[2m",
101
140
  green: "\x1B[32m",
102
141
  yellow: "\x1B[33m",
103
- gray: "\x1B[90m"
142
+ blue: "\x1B[34m",
143
+ cyan: "\x1B[36m",
144
+ red: "\x1B[31m"};
145
+ var log = {
146
+ info: (msg) => console.log(`${C3.blue}\u2139${C3.reset} ${msg}`),
147
+ success: (msg) => console.log(`${C3.green}\u2713${C3.reset} ${msg}`),
148
+ warn: (msg) => console.log(`${C3.yellow}\u26A0${C3.reset} ${msg}`),
149
+ error: (msg) => console.log(`${C3.red}\u2717${C3.reset} ${msg}`),
150
+ dim: (msg) => console.log(`${C3.dim}${msg}${C3.reset}`),
151
+ header: (msg) => console.log(`
152
+ ${C3.bold}${C3.cyan}${msg}${C3.reset}
153
+ `),
154
+ table: (label, value) => console.log(` ${C3.dim}${label}:${C3.reset} ${C3.bold}${value}${C3.reset}`),
155
+ progress: (current, total, label) => {
156
+ const percentage = Math.round(current / total * 100);
157
+ const bar = "\u2588".repeat(Math.floor(percentage / 5));
158
+ const empty = "\u2591".repeat(20 - Math.floor(percentage / 5));
159
+ console.log(
160
+ ` ${C3.cyan}${bar}${empty}${C3.reset} ${C3.bold}${percentage}%${C3.reset} ${C3.dim}(${current}/${total})${C3.reset} ${label}`
161
+ );
162
+ }
104
163
  };
105
164
  var ConfigSchema = v.object({
106
165
  seedFileExtensions: v.array(v.picklist([".json"])),
@@ -111,13 +170,15 @@ var ConfigSchema = v.object({
111
170
  table: v.string(),
112
171
  schema: v.optional(v.string(), "public"),
113
172
  primaryKeys: v.array(v.string()),
114
- source: v.string()
173
+ source: v.string(),
174
+ updateOnConflict: v.optional(v.nullable(v.array(v.string())), null)
115
175
  })
116
176
  )
117
177
  });
118
178
  async function run(opts) {
119
179
  dotenv.config();
120
180
  const configPath = resolvePath(opts.config || "seed.config.json");
181
+ log.header("\u{1F331} GreenSeed");
121
182
  let raw;
122
183
  try {
123
184
  raw = JSON.parse(readFile(configPath));
@@ -136,6 +197,8 @@ async function run(opts) {
136
197
  if (!dbUrl) {
137
198
  throw new Error("Missing DB env var: " + dbVar);
138
199
  }
200
+ log.info(`Using config: ${C3.dim}${configPath}${C3.reset}`);
201
+ log.info(`Database: ${C3.dim}${dbVar}${C3.reset}`);
139
202
  const sql = connect(dbUrl);
140
203
  await check(sql);
141
204
  await validateTables(
@@ -145,18 +208,20 @@ async function run(opts) {
145
208
  schema: t.schema
146
209
  }))
147
210
  );
148
- console.log(C.gray + "Starting seed process" + C.reset);
211
+ log.success("Database connection established");
212
+ log.header("Seeding Tables");
149
213
  for (const t of config.tables) {
150
214
  const source = path2.resolve(path2.dirname(configPath), t.source);
151
215
  const rows = loadJsonArray(source, config.onMissingFile || "error");
152
216
  if (rows.length === 0) {
153
- console.log(C.yellow + "Skipped " + t.table + C.reset);
217
+ log.warn(`Skipped ${C3.bold}${t.schema}.${t.table}${C3.reset} (no data)`);
154
218
  continue;
155
219
  }
156
220
  const meta = await metadata(sql, t.schema, t.table);
157
221
  const validCols = Object.keys(rows[0]).filter(
158
222
  (c) => meta.columns.includes(c)
159
223
  );
224
+ log.info(`Seeding ${C3.bold}${t.schema}.${t.table}${C3.reset}`);
160
225
  let inserted = 0;
161
226
  for (const part of chunk(rows, 1e3)) {
162
227
  inserted += await insert(
@@ -165,24 +230,87 @@ async function run(opts) {
165
230
  t.table,
166
231
  part,
167
232
  validCols,
168
- t.primaryKeys
233
+ t.primaryKeys,
234
+ t.updateOnConflict || []
235
+ );
236
+ }
237
+ const hasConflict = t.updateOnConflict && t.updateOnConflict.length > 0;
238
+ const action = hasConflict ? "upserted" : "inserted";
239
+ const skipped = rows.length - inserted;
240
+ if (inserted === rows.length) {
241
+ log.success(
242
+ `${C3.bold}${t.schema}.${t.table}${C3.reset} ${C3.green}${action} ${inserted} rows${C3.reset}`
243
+ );
244
+ } else {
245
+ log.success(
246
+ `${C3.bold}${t.schema}.${t.table}${C3.reset} ${C3.green}${action} ${inserted}${C3.reset}${C3.dim}, skipped ${skipped}${C3.reset}`
169
247
  );
170
248
  }
171
- console.log(
172
- C.green + t.schema + "." + t.table + ": " + inserted + "/" + rows.length + C.reset
173
- );
174
249
  }
175
250
  await sql.end();
176
- console.log(C.green + "Seed completed" + C.reset);
251
+ log.header("Summary");
252
+ log.success(
253
+ `Seeded ${C3.bold}${config.tables.length}${C3.reset} table${config.tables.length === 1 ? "" : "s"}`
254
+ );
255
+ }
256
+ var C4 = {
257
+ reset: "\x1B[0m",
258
+ dim: "\x1B[2m",
259
+ red: "\x1B[31m"
260
+ };
261
+ var logError3 = (msg) => console.error(`${C4.red}\u2717${C4.reset} ${C4.dim}${msg}${C4.reset}`);
262
+ var DEFAULT_CONFIG = {
263
+ seedFileExtensions: [".json"],
264
+ databaseUrlEnvVar: "DATABASE_URL",
265
+ onMissingFile: "error",
266
+ tables: [
267
+ {
268
+ table: "example_table",
269
+ schema: "public",
270
+ primaryKeys: ["id"],
271
+ source: "./data/example_table.json",
272
+ updateOnConflict: []
273
+ }
274
+ ]
275
+ };
276
+ function init(configPath) {
277
+ const resolved = path2.resolve(process.cwd(), configPath);
278
+ if (fs.existsSync(resolved)) {
279
+ logError3(`Config file already exists: ${resolved}`);
280
+ throw new Error(`Config already exists: ${resolved}`);
281
+ }
282
+ fs.writeFileSync(
283
+ resolved,
284
+ JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n",
285
+ "utf8"
286
+ );
177
287
  }
178
288
 
179
289
  // src/cli.ts
290
+ var C5 = {
291
+ reset: "\x1B[0m",
292
+ bold: "\x1B[1m",
293
+ green: "\x1B[32m",
294
+ red: "\x1B[31m"
295
+ };
296
+ var logError4 = (err) => console.error(`${C5.red}${C5.bold}Error:${C5.reset} ${String(err)}`);
180
297
  var prog = sade("greenseed");
181
298
  prog.command("push").option("-c, --config <file>", "Config file", "seed.config.json").option("-d, --dbvar <name>", "DB env var override").action(async (opts) => {
182
299
  try {
183
300
  await run(opts);
184
301
  } catch (err) {
185
- console.error("\x1B[31m" + String(err) + "\x1B[0m");
302
+ logError4(err);
303
+ process.exit(1);
304
+ }
305
+ });
306
+ prog.command("init").option("-c, --config <file>", "Config file", "seed.config.json").action((opts) => {
307
+ try {
308
+ init(opts.config);
309
+ console.log(
310
+ `${C5.green}\u2713${C5.reset} Config created: ${C5.bold}${opts.config}${C5.reset}`
311
+ );
312
+ } catch (err) {
313
+ logError4(err);
186
314
  process.exit(1);
187
315
  }
188
316
  });
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":["C","logError","path","fs"],"mappings":"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","file":"cli.js","sourcesContent":["import postgres from \"postgres\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\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\tlogError(\"Database connection failed\")\n\t\tthrow new Error(\"Failed to connect to database\")\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\tlogError(`Failed to check table: ${schema}.${table}`)\n\t\tthrow new Error(`Table check failed: ${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\tlogError(`Table does not exist: ${t.schema}.${t.table}`)\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\tupdateOnConflict: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tlogError(`No primary keys defined for ${schema}.${table}`)\n\t\tthrow new Error(`No primary keys for ${schema}.${table}`)\n\t}\n\n\tconst updateCols = updateOnConflict.filter(\n\t\t(col) => !primaryKeys.includes(col),\n\t)\n\n\tlet q\n\tif (updateCols.length > 0) {\n\t\tconst updates = Object.fromEntries(\n\t\t\tupdateCols.map((col) => [col, sql`excluded.${sql(col)}`]),\n\t\t)\n\n\t\tq = await sql`\n\t\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t\t${sql(rows, columns)}\n\t\t\ton conflict (${sql(primaryKeys)}) do update set\n\t\t\t${sql(updates)}\n\t\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t\t`\n\t} else {\n\t\tq = await sql`\n\t\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t\t${sql(rows, columns)}\n\t\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t\t`\n\t}\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\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\tlogError(`Failed to read file: ${p}`)\n\t\tthrow new Error(`File read error: ${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\tlogError(`Source file not found: ${p}`)\n\t\tthrow new Error(`Missing file: ${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\tlogError(`Invalid JSON in file: ${p}`)\n\t\tthrow new Error(`JSON parse error: ${p}`)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tlogError(`Expected array but got ${typeof parsed}: ${p}`)\n\t\tthrow new Error(`Invalid data format: ${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\tbold: \"\\x1b[1m\",\n\tdim: \"\\x1b[2m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tblue: \"\\x1b[34m\",\n\tcyan: \"\\x1b[36m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst log = {\n\tinfo: (msg: string) => console.log(`${C.blue}ℹ${C.reset} ${msg}`),\n\tsuccess: (msg: string) => console.log(`${C.green}✓${C.reset} ${msg}`),\n\twarn: (msg: string) => console.log(`${C.yellow}⚠${C.reset} ${msg}`),\n\terror: (msg: string) => console.log(`${C.red}✗${C.reset} ${msg}`),\n\tdim: (msg: string) => console.log(`${C.dim}${msg}${C.reset}`),\n\theader: (msg: string) =>\n\t\tconsole.log(`\\n${C.bold}${C.cyan}${msg}${C.reset}\\n`),\n\ttable: (label: string, value: string | number) =>\n\t\tconsole.log(` ${C.dim}${label}:${C.reset} ${C.bold}${value}${C.reset}`),\n\tprogress: (current: number, total: number, label: string) => {\n\t\tconst percentage = Math.round((current / total) * 100)\n\t\tconst bar = \"█\".repeat(Math.floor(percentage / 5))\n\t\tconst empty = \"░\".repeat(20 - Math.floor(percentage / 5))\n\t\tconsole.log(\n\t\t\t` ${C.cyan}${bar}${empty}${C.reset} ${C.bold}${percentage}%${C.reset} ${C.dim}(${current}/${total})${C.reset} ${label}`,\n\t\t)\n\t},\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\tupdateOnConflict: v.optional(v.nullable(v.array(v.string())), null),\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\tlog.header(\"🌱 GreenSeed\")\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\tlog.info(`Using config: ${C.dim}${configPath}${C.reset}`)\n\tlog.info(`Database: ${C.dim}${dbVar}${C.reset}`)\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\tlog.success(\"Database connection established\")\n\tlog.header(\"Seeding Tables\")\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\tlog.warn(`Skipped ${C.bold}${t.schema}.${t.table}${C.reset} (no data)`)\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\tlog.info(`Seeding ${C.bold}${t.schema}.${t.table}${C.reset}`)\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\tt.updateOnConflict || [],\n\t\t\t)\n\t\t}\n\n\t\tconst hasConflict = t.updateOnConflict && t.updateOnConflict.length > 0\n\t\tconst action = hasConflict ? \"upserted\" : \"inserted\"\n\t\tconst skipped = rows.length - inserted\n\n\t\tif (inserted === rows.length) {\n\t\t\tlog.success(\n\t\t\t\t`${C.bold}${t.schema}.${t.table}${C.reset} ${C.green}${action} ${inserted} rows${C.reset}`,\n\t\t\t)\n\t\t} else {\n\t\t\tlog.success(\n\t\t\t\t`${C.bold}${t.schema}.${t.table}${C.reset} ${C.green}${action} ${inserted}${C.reset}${C.dim}, skipped ${skipped}${C.reset}`,\n\t\t\t)\n\t\t}\n\t}\n\n\tawait sql.end()\n\n\tlog.header(\"Summary\")\n\tlog.success(\n\t\t`Seeded ${C.bold}${config.tables.length}${C.reset} table${config.tables.length === 1 ? \"\" : \"s\"}`,\n\t)\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\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\tupdateOnConflict: [],\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\tlogError(`Config file already exists: ${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 C = {\n\treset: \"\\x1b[0m\",\n\tbold: \"\\x1b[1m\",\n\tgreen: \"\\x1b[32m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (err: unknown) =>\n\tconsole.error(`${C.red}${C.bold}Error:${C.reset} ${String(err)}`)\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\tlogError(err)\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(\n\t\t\t\t`${C.green}✓${C.reset} Config created: ${C.bold}${opts.config}${C.reset}`,\n\t\t\t)\n\t\t} catch (err) {\n\t\t\tlogError(err)\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.4",
3
+ "version": "0.1.6",
4
4
  "author": "Mohit Khatri",
5
5
  "main": "./dist/cli.js",
6
6
  "module": "./dist/index.js",