zinkee 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -279,13 +279,16 @@ Notes:
279
279
  ### CRUD operations
280
280
 
281
281
  ```bash
282
- # Create
282
+ # Create a single record (--set/--set-json/--raw/--raw-file build one record)
283
283
  zinkee records create projects \
284
284
  --set name="Website Redesign" \
285
285
  --set budget=25000 \
286
286
  --set start-date="2026-01-15T00:00:00.000Z" \
287
287
  --set status=1
288
288
 
289
+ # Create many records from a JSON array file (one object per record; mutually exclusive with the single-record flags)
290
+ zinkee --json records create projects --records-file ./projects.json
291
+
289
292
  # List and query
290
293
  zinkee --json records list projects --limit 50
291
294
  zinkee --json records query projects --limit 10 --sort "_id:desc"
@@ -299,8 +302,11 @@ zinkee --json records get projects <record-uuid>
299
302
  # Update
300
303
  zinkee records update projects <record-uuid> --set status=2
301
304
 
302
- # Delete
303
- zinkee records delete projects <record-uuid>
305
+ # Delete one or more records (--record is repeatable)
306
+ zinkee records delete projects --record <record-uuid> --record <record-uuid>
307
+
308
+ # Delete many records listing one uuid per line in a file (merged with --record, de-duplicated)
309
+ zinkee records delete projects --records-file ./record-ids.txt
304
310
 
305
311
  # Clear all records from a schema (destructive — irreversible)
306
312
  zinkee records clear projects
package/dist/index.js CHANGED
@@ -2832,35 +2832,55 @@ var specificExamples = {
2832
2832
  ]),
2833
2833
  "records create": buildCommandExamples([
2834
2834
  {
2835
- description: "Minimal create using just two plain string fields.",
2835
+ description: "Create a single record with --set/--set-json (the single-record flags build one record, sent as a one-element 'records' array).",
2836
2836
  command: `zinkee --json records create contacts --set contact-name="Ada Lovelace" --set contact-email=ada@example.com`,
2837
2837
  raw: `{
2838
- "values": {
2839
- "contact-name": "Ada Lovelace",
2840
- "contact-email": "ada@example.com"
2841
- }
2838
+ "records": [
2839
+ {
2840
+ "contact-name": "Ada Lovelace",
2841
+ "contact-email": "ada@example.com"
2842
+ }
2843
+ ]
2842
2844
  }`,
2843
2845
  response: `{
2844
- "recordId": "550e8400-e29b-41d4-a716-446655440000",
2845
- "values": {
2846
- "contact-name": "Ada Lovelace",
2847
- "contact-email": "ada@example.com"
2848
- }
2846
+ "created": [
2847
+ "550e8400-e29b-41d4-a716-446655440000"
2848
+ ],
2849
+ "errors": []
2849
2850
  }`
2850
2851
  },
2851
2852
  {
2852
- description: "Create a record mixing text, option, number, date, reference and member fields. ReferenceField values must match the target's current targetDisplayField (read it from the target record before writing).",
2853
- command: `zinkee --json records create contacts --set contact-name="Grace Hopper" --set contact-email=grace@example.com --set status=2 --set amount=1999.99 --set due-date=2026-06-01T09:00:00Z --set customer-ref="CUST-12" --set owner=6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d`,
2853
+ description: "Create many records from a JSON array file (one object per record, slug -> value). Best-effort: valid records are created and invalid ones are reported per index in 'errors'. ReferenceField values must match the target's current targetDisplayField.",
2854
+ command: `zinkee --json records create contacts --records-file ./records.json`,
2854
2855
  raw: `{
2855
- "values": {
2856
- "contact-name": "Grace Hopper",
2857
- "contact-email": "grace@example.com",
2858
- "status": 2,
2859
- "amount": 1999.99,
2860
- "due-date": "2026-06-01T09:00:00Z",
2861
- "customer-ref": "CUST-12",
2862
- "owner": "6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d"
2863
- }
2856
+ "records": [
2857
+ {
2858
+ "contact-name": "Ada Lovelace",
2859
+ "contact-email": "ada@example.com"
2860
+ },
2861
+ {
2862
+ "contact-name": "Grace Hopper",
2863
+ "contact-email": "grace@example.com",
2864
+ "status": 2,
2865
+ "amount": 1999.99,
2866
+ "due-date": "2026-06-01T09:00:00Z",
2867
+ "customer-ref": "8ad602a1-f6e3-43f6-9ab2-f1c5afc99621",
2868
+ "owner": "6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d"
2869
+ }
2870
+ ]
2871
+ }`,
2872
+ response: `{
2873
+ "created": [
2874
+ "550e8400-e29b-41d4-a716-446655440000"
2875
+ ],
2876
+ "errors": [
2877
+ {
2878
+ "index": 1,
2879
+ "reason": "record_field_not_found",
2880
+ "message": "Field 'contact-emial' was not found in schema '2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e'.",
2881
+ "action": "Use a valid field slug from the schema and retry."
2882
+ }
2883
+ ]
2864
2884
  }`
2865
2885
  }
2866
2886
  ]),
@@ -4533,10 +4553,24 @@ var specificExamples = {
4533
4553
  ]),
4534
4554
  "records delete": buildCommandExamples([
4535
4555
  {
4536
- description: "Delete a record.",
4537
- command: `zinkee --json records delete contacts ${SAMPLE_UUID}`,
4556
+ description: "Delete one or more records by id (--record is repeatable).",
4557
+ command: `zinkee --json records delete contacts --record 550e8400-e29b-41d4-a716-446655440000 --record 6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d`,
4538
4558
  response: `{
4539
- "recordId": "550e8400-e29b-41d4-a716-446655440000",
4559
+ "recordIds": [
4560
+ "550e8400-e29b-41d4-a716-446655440000",
4561
+ "6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d"
4562
+ ],
4563
+ "deleted": true
4564
+ }`
4565
+ },
4566
+ {
4567
+ description: "Delete many records listing one uuid per line in a file (merged with any --record flags and de-duplicated).",
4568
+ command: `zinkee --json records delete contacts --records-file ./record-ids.txt`,
4569
+ response: `{
4570
+ "recordIds": [
4571
+ "550e8400-e29b-41d4-a716-446655440000",
4572
+ "6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d"
4573
+ ],
4540
4574
  "deleted": true
4541
4575
  }`
4542
4576
  }
@@ -10721,10 +10755,10 @@ var RecordsApi = class {
10721
10755
  `/api/v2/schemas/${selectorValue8(schema)}/records/${selectorValue8(record)}`
10722
10756
  );
10723
10757
  }
10724
- createRecord(schema, input) {
10758
+ createRecords(schema, records) {
10725
10759
  return this.client.requestJson(
10726
10760
  `/api/v2/schemas/${selectorValue8(schema)}/records`,
10727
- jsonOptions6(input, "POST")
10761
+ jsonOptions6({ records }, "POST")
10728
10762
  );
10729
10763
  }
10730
10764
  updateRecord(schema, record, input) {
@@ -10733,10 +10767,10 @@ var RecordsApi = class {
10733
10767
  jsonOptions6(input, "PATCH")
10734
10768
  );
10735
10769
  }
10736
- deleteRecord(schema, record) {
10770
+ deleteRecords(schema, recordIds) {
10737
10771
  return this.client.requestJson(
10738
- `/api/v2/schemas/${selectorValue8(schema)}/records/${selectorValue8(record)}`,
10739
- { method: "DELETE" }
10772
+ `/api/v2/schemas/${selectorValue8(schema)}/records:delete`,
10773
+ jsonOptions6({ recordIds }, "POST")
10740
10774
  );
10741
10775
  }
10742
10776
  clearRecords(schema) {
@@ -10960,6 +10994,61 @@ function parseJsonKeyValueAssignment(input) {
10960
10994
  function dedupeStrings(values) {
10961
10995
  return [...new Set(values)];
10962
10996
  }
10997
+ function toRecordUuid(value) {
10998
+ try {
10999
+ const normalized = normalizeUuidOrSlug(value);
11000
+ return normalized.kind === "uuid" ? normalized.value : null;
11001
+ } catch {
11002
+ return null;
11003
+ }
11004
+ }
11005
+ function collectRecordIds(value, previous = []) {
11006
+ const uuid = toRecordUuid(value.trim());
11007
+ if (!uuid) {
11008
+ throw new InvalidArgumentError8(`Expected a record UUID, received "${value}".`);
11009
+ }
11010
+ return [...previous, uuid];
11011
+ }
11012
+ function readRecordIdsFromFile(path) {
11013
+ let contents;
11014
+ try {
11015
+ contents = readFileSync8(path, "utf8");
11016
+ } catch (error) {
11017
+ const message = error instanceof Error ? error.message : "Unable to read file.";
11018
+ throw new CliError(
11019
+ "invalid_cli_usage",
11020
+ `Failed to read record ids from ${path}: ${message}`
11021
+ );
11022
+ }
11023
+ const ids = [];
11024
+ contents.split(/\r?\n/).forEach((line, index) => {
11025
+ const value = line.trim();
11026
+ if (!value) {
11027
+ return;
11028
+ }
11029
+ const uuid = toRecordUuid(value);
11030
+ if (!uuid) {
11031
+ throw new CliError(
11032
+ "invalid_cli_usage",
11033
+ `Invalid record UUID "${value}" at ${path}:${index + 1}.`
11034
+ );
11035
+ }
11036
+ ids.push(uuid);
11037
+ });
11038
+ return ids;
11039
+ }
11040
+ function resolveDeleteRecordIds(options) {
11041
+ const flagIds = options.record ?? [];
11042
+ const fileIds = options.recordsFile ? readRecordIdsFromFile(options.recordsFile) : [];
11043
+ const recordIds = dedupeStrings([...flagIds, ...fileIds]);
11044
+ if (recordIds.length === 0) {
11045
+ throw new CliError(
11046
+ "invalid_cli_usage",
11047
+ 'Provide at least one record id via "--record <uuid>" (repeatable) or "--records-file <path>".'
11048
+ );
11049
+ }
11050
+ return recordIds;
11051
+ }
10963
11052
  function collectStringValues(value, previous = []) {
10964
11053
  return [...previous, value];
10965
11054
  }
@@ -11071,7 +11160,60 @@ function buildRecordQueryInput(options, allowRaw) {
11071
11160
  }
11072
11161
  return input;
11073
11162
  }
11074
- function buildRecordCreateInput(options) {
11163
+ function readRecordsFromFile(path) {
11164
+ let contents;
11165
+ try {
11166
+ contents = readFileSync8(path, "utf8");
11167
+ } catch (error) {
11168
+ const message = error instanceof Error ? error.message : "Unable to read file.";
11169
+ throw new CliError(
11170
+ "invalid_cli_usage",
11171
+ `Failed to read records from ${path}: ${message}`
11172
+ );
11173
+ }
11174
+ let parsed;
11175
+ try {
11176
+ parsed = JSON.parse(contents);
11177
+ } catch (error) {
11178
+ const message = error instanceof Error ? error.message : "Invalid JSON.";
11179
+ throw new CliError(
11180
+ "invalid_cli_usage",
11181
+ `Failed to parse JSON from --records-file ${path}: ${message}`
11182
+ );
11183
+ }
11184
+ if (!Array.isArray(parsed)) {
11185
+ throw new CliError(
11186
+ "invalid_cli_usage",
11187
+ `Expected --records-file ${path} to contain a JSON array of record objects.`
11188
+ );
11189
+ }
11190
+ if (parsed.length === 0) {
11191
+ throw new CliError(
11192
+ "invalid_cli_usage",
11193
+ `--records-file ${path} must contain at least one record.`
11194
+ );
11195
+ }
11196
+ return parsed.map((record, index) => {
11197
+ if (!isJsonObject6(record)) {
11198
+ throw new CliError(
11199
+ "invalid_cli_usage",
11200
+ `Record at index ${index} in --records-file ${path} must be a JSON object.`
11201
+ );
11202
+ }
11203
+ return record;
11204
+ });
11205
+ }
11206
+ function buildCreateRecords(options) {
11207
+ const hasSingleRecordFlags = (options.set?.length ?? 0) > 0 || (options.setJson?.length ?? 0) > 0 || options.raw !== void 0 || options.rawFile !== void 0;
11208
+ if (options.recordsFile !== void 0) {
11209
+ if (hasSingleRecordFlags) {
11210
+ throw new CliError(
11211
+ "invalid_cli_usage",
11212
+ 'Provide either "--records-file" or the single-record flags (--set/--set-json/--raw/--raw-file), not both.'
11213
+ );
11214
+ }
11215
+ return readRecordsFromFile(options.recordsFile);
11216
+ }
11075
11217
  const values = {
11076
11218
  ...readRawObject3(options.raw, options.rawFile),
11077
11219
  ...buildAssignmentObject(options.set, options.setJson)
@@ -11079,10 +11221,10 @@ function buildRecordCreateInput(options) {
11079
11221
  if (isEmptyObject5(values)) {
11080
11222
  throw new CliError(
11081
11223
  "invalid_cli_usage",
11082
- 'Provide record fields via "--set", "--set-json", "--raw", or "--raw-file".'
11224
+ 'Provide record fields via "--set", "--set-json", "--raw", "--raw-file", or load multiple records with "--records-file".'
11083
11225
  );
11084
11226
  }
11085
- return { values };
11227
+ return [values];
11086
11228
  }
11087
11229
  function buildRecordUpdateInput(options) {
11088
11230
  const raw = readRawObject3(options.raw, options.rawFile);
@@ -11205,19 +11347,26 @@ function registerRecordsCommands(program, options = {}) {
11205
11347
  }
11206
11348
  emitTable4(stdout, data);
11207
11349
  });
11208
- records.command("create").description("Create a record").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).option("--set <field=value>", "set a string field", collectKeyValueAssignments, []).option("--set-json <field=json>", "set a structured field", collectJsonAssignments, []).option("--raw <json>", "raw JSON object payload").option("--raw-file <path>", "path to a raw JSON object payload").action(async function action(schema, commandOptions) {
11350
+ records.command("create").description("Create one or more records").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).option("--set <field=value>", "set a string field", collectKeyValueAssignments, []).option("--set-json <field=json>", "set a structured field", collectJsonAssignments, []).option("--raw <json>", "raw JSON object payload").option("--raw-file <path>", "path to a raw JSON object payload").option(
11351
+ "--records-file <path>",
11352
+ "path to a JSON array of record objects (mutually exclusive with --set/--set-json/--raw/--raw-file)"
11353
+ ).action(async function action(schema, commandOptions) {
11209
11354
  const { api, schemasApi, json, readOnly } = createRecordsApi(this, filePath);
11210
11355
  ensureWritable7("records create", readOnly, "write");
11356
+ const records2 = buildCreateRecords(commandOptions);
11211
11357
  const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
11212
- const data = await api.createRecord(
11213
- resolvedSchema,
11214
- buildRecordCreateInput(commandOptions)
11215
- );
11358
+ const data = await api.createRecords(resolvedSchema, records2);
11216
11359
  if (json) {
11217
11360
  emitJson10(stdout, "records create", data, filePath);
11218
11361
  return;
11219
11362
  }
11220
- writeLine10(stdout, `Created record "${getRecordIdentifier(data)}".`);
11363
+ writeLine10(stdout, `Created ${data.created.length} record(s) in schema "${schema.value}".`);
11364
+ if (data.errors.length > 0) {
11365
+ writeLine10(stdout, `${data.errors.length} record(s) rejected:`);
11366
+ for (const error of data.errors) {
11367
+ writeLine10(stdout, ` - index ${error.index}: ${error.message}`);
11368
+ }
11369
+ }
11221
11370
  });
11222
11371
  records.command("update").description("Update a record").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).argument("<record>", "record uuid or slug", parseUuidOrSlugSelector8).option("--set <field=value>", "set a string field", collectKeyValueAssignments, []).option("--set-json <field=json>", "set a structured field", collectJsonAssignments, []).option("--unset <field>", "unset a field", collectStringValues, []).option("--raw <json>", "raw JSON object of fields to set").option("--raw-file <path>", "path to a JSON object of fields to set").action(async function action(schema, record, commandOptions) {
11223
11372
  const { api, schemasApi, json, readOnly } = createRecordsApi(this, filePath);
@@ -11234,21 +11383,23 @@ function registerRecordsCommands(program, options = {}) {
11234
11383
  }
11235
11384
  writeLine10(stdout, `Updated record "${getRecordIdentifier(data)}".`);
11236
11385
  });
11237
- records.command("delete").description("Delete a record").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).argument("<record>", "record uuid or slug", parseUuidOrSlugSelector8).action(async function action(schema, record) {
11386
+ records.command("delete").description("Delete one or more records").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).option("--record <uuid>", "record uuid to delete (repeatable)", collectRecordIds, []).option(
11387
+ "--records-file <path>",
11388
+ "path to a file with one record uuid per line (merged with --record)"
11389
+ ).action(async function action(schema, commandOptions) {
11238
11390
  const { api, schemasApi, json, readOnly } = createRecordsApi(this, filePath);
11239
11391
  ensureWritable7("records delete", readOnly, "destructive");
11392
+ const recordIds = resolveDeleteRecordIds(commandOptions);
11240
11393
  const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
11241
- await api.deleteRecord(resolvedSchema, record);
11242
- const data = {
11243
- schema: resolvedSchema.value,
11244
- record: record.value,
11245
- deleted: true
11246
- };
11394
+ const data = await api.deleteRecords(resolvedSchema, recordIds);
11247
11395
  if (json) {
11248
11396
  emitJson10(stdout, "records delete", data, filePath);
11249
11397
  return;
11250
11398
  }
11251
- writeLine10(stdout, `Deleted record "${record.value}" from schema "${schema.value}".`);
11399
+ writeLine10(
11400
+ stdout,
11401
+ `Deleted ${recordIds.length} record(s) from schema "${schema.value}".`
11402
+ );
11252
11403
  });
11253
11404
  records.command("clear").description("Delete all records from a schema's table").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector8).action(async function action(schema) {
11254
11405
  const { api, schemasApi, json, readOnly } = createRecordsApi(this, filePath);