@homespunapps/cli 1.5.1 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/data.js +46 -2
- package/dist/help-catalog.js +32 -1
- package/package.json +2 -2
package/dist/commands/data.js
CHANGED
|
@@ -18,7 +18,7 @@ export async function runData(args) {
|
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
20
|
if (!appArg || !collection || !verb) {
|
|
21
|
-
fail("usage: homespun data <app> <collection> <list|get|upsert|update|delete|purge|import>", "invalid_args");
|
|
21
|
+
fail("usage: homespun data <app> <collection> <list|get|upsert|update|delete|purge|import|retention>", "invalid_args");
|
|
22
22
|
}
|
|
23
23
|
const sub = {
|
|
24
24
|
positionals: args.positionals.slice(3),
|
|
@@ -43,8 +43,10 @@ export async function runData(args) {
|
|
|
43
43
|
return runPurge(appArg, collection, sub);
|
|
44
44
|
case "import":
|
|
45
45
|
return runImport(appArg, collection, sub);
|
|
46
|
+
case "retention":
|
|
47
|
+
return runRetention(appArg, collection, sub);
|
|
46
48
|
default:
|
|
47
|
-
fail(`unknown verb '${verb}': homespun data <app> <collection> <list|get|upsert|update|delete|purge|import>`, "invalid_args");
|
|
49
|
+
fail(`unknown verb '${verb}': homespun data <app> <collection> <list|get|upsert|update|delete|purge|import|retention>`, "invalid_args");
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
function parseIntFlag(args, name, defaultValue, bounds = {}) {
|
|
@@ -353,3 +355,45 @@ async function runImport(appArg, collection, args) {
|
|
|
353
355
|
failures,
|
|
354
356
|
});
|
|
355
357
|
}
|
|
358
|
+
// `homespun data <app> <collection> retention` (issue #956): show or change the
|
|
359
|
+
// OWNER retention override on a collection. With no set/clear flag (or --show) it
|
|
360
|
+
// reads the current effective retention; otherwise it sets an axis
|
|
361
|
+
// (--max-rows/--max-age-days) or clears one (--clear-rows/--clear-age, reverting
|
|
362
|
+
// that axis to the author default). Effective retention is per-axis
|
|
363
|
+
// `override ?? authorDefault`. Prints the effective bounds and the would-prune
|
|
364
|
+
// count either way.
|
|
365
|
+
async function runRetention(appArg, collection, args) {
|
|
366
|
+
assertKnownFlags(args, ...specFor("data", "retention"));
|
|
367
|
+
const clearRows = args.bools.has("clear-rows");
|
|
368
|
+
const clearAge = args.bools.has("clear-age");
|
|
369
|
+
const maxRows = parseIntFlag(args, "max-rows", undefined, { min: 1 });
|
|
370
|
+
const maxAgeDays = parseIntFlag(args, "max-age-days", undefined, { min: 1 });
|
|
371
|
+
if (clearRows && maxRows !== undefined) {
|
|
372
|
+
fail("--clear-rows cannot be combined with --max-rows", "invalid_args");
|
|
373
|
+
}
|
|
374
|
+
if (clearAge && maxAgeDays !== undefined) {
|
|
375
|
+
fail("--clear-age cannot be combined with --max-age-days", "invalid_args");
|
|
376
|
+
}
|
|
377
|
+
// Build the per-axis patch: a number sets the axis, null clears it, an omitted
|
|
378
|
+
// axis is left unchanged. `--show` (or no set/clear flag) reads without writing.
|
|
379
|
+
const patch = {};
|
|
380
|
+
if (clearRows)
|
|
381
|
+
patch.maxRows = null;
|
|
382
|
+
else if (maxRows !== undefined)
|
|
383
|
+
patch.maxRows = maxRows;
|
|
384
|
+
if (clearAge)
|
|
385
|
+
patch.maxAgeDays = null;
|
|
386
|
+
else if (maxAgeDays !== undefined)
|
|
387
|
+
patch.maxAgeDays = maxAgeDays;
|
|
388
|
+
const willWrite = patch.maxRows !== undefined || patch.maxAgeDays !== undefined;
|
|
389
|
+
const client = makeClient(args);
|
|
390
|
+
const appId = await resolveAppId(client, appArg);
|
|
391
|
+
try {
|
|
392
|
+
printJson(willWrite
|
|
393
|
+
? await client.setCollectionRetention(appId, collection, patch)
|
|
394
|
+
: await client.getCollectionRetention(appId, collection));
|
|
395
|
+
}
|
|
396
|
+
catch (e) {
|
|
397
|
+
failFromError(e);
|
|
398
|
+
}
|
|
399
|
+
}
|
package/dist/help-catalog.js
CHANGED
|
@@ -128,7 +128,7 @@ const DATA = {
|
|
|
128
128
|
noun: "data",
|
|
129
129
|
tagline: "collection row CRUD for an app",
|
|
130
130
|
group: "app",
|
|
131
|
-
rootSummary: "Collection row CRUD for an app: list, get, upsert, update, delete, purge, import.",
|
|
131
|
+
rootSummary: "Collection row CRUD for an app: list, get, upsert, update, delete, purge, import, plus retention (owner override).",
|
|
132
132
|
// The data parser is verb-LAST: `homespun data <app> <collection> <verb>`
|
|
133
133
|
// (see commands/data.ts, which reads the verb from positionals[2]). Every
|
|
134
134
|
// other noun is verb-first. Each verb below carries only the positionals that
|
|
@@ -258,12 +258,43 @@ const DATA = {
|
|
|
258
258
|
},
|
|
259
259
|
],
|
|
260
260
|
},
|
|
261
|
+
{
|
|
262
|
+
verb: "retention",
|
|
263
|
+
summary: "Shows or overrides the owner retention on a collection (owner control).",
|
|
264
|
+
flags: [
|
|
265
|
+
{
|
|
266
|
+
name: "max-rows",
|
|
267
|
+
value: "<n>",
|
|
268
|
+
description: "Override the max live rows kept (per-axis, positive)",
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
name: "max-age-days",
|
|
272
|
+
value: "<n>",
|
|
273
|
+
description: "Override the max row age in days (per-axis, positive)",
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
bools: [
|
|
277
|
+
{
|
|
278
|
+
name: "clear-rows",
|
|
279
|
+
description: "Clear the rows override, reverting to the author default",
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: "clear-age",
|
|
283
|
+
description: "Clear the age override, reverting to the author default",
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: "show",
|
|
287
|
+
description: "Only read the current effective retention, change nothing",
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
},
|
|
261
291
|
],
|
|
262
292
|
notes: [
|
|
263
293
|
"<app> accepts either the app_id or its slug. upsert is the ONLY create-shaped verb: omit --key to add a new row (the server generates the key); pass --key to ensure a row exists at that key (returns the existing row with deduped:true on a collision, never errors). Pass --on <field> to upsert on a manifest-declared UNIQUE field instead of the key: the row whose <field> value matches is updated in place (idempotent re-import), else created.",
|
|
264
294
|
"list --where takes a JSON array of {field, op, value} conditions (ANDed), op one of eq, neq, in, notIn, gt, lt, gte, lte (in and notIn take an array value). --sort takes a JSON array of {field, dir} (dir asc or desc). Filtering is applied AFTER the read permission and author scoping, so a filtered list is always a subset of what you could already read. Comparisons are same-type only (no coercion); dates compare as ISO-8601 strings. A custom --sort cannot be combined with --since.",
|
|
265
295
|
"purge removes ONE row by --key even in an append-only collection. Owner and agent only (never members or anyone); it bypasses append-only and the collection delete list on purpose, and writes an audited delete feed entry.",
|
|
266
296
|
"import reads NDJSON (one JSON object per line) OR a JSON array from --file and bulk-writes it in chunks via the batch API, in ONE process. Each object is a row's data. Pass --key-field to derive the row key from a field: an existing row at that key is LEFT UNCHANGED, so this is create-or-skip-by-id, not overwrite, and re-importing changed data for a known key does not update it. Import DEFAULTS TO SILENT (it suppresses notify and webhooks, since a bulk import is a migration); pass --emit-effects to fire them. A per-row failure is listed in the summary WITHOUT aborting the import.",
|
|
297
|
+
"retention is an OWNER control: the author declares default retention in the manifest, and this tightens or loosens it per collection at runtime WITHOUT a redeploy. Effective retention is per-axis override-or-author-default: --max-rows/--max-age-days set an axis override, --clear-rows/--clear-age revert an axis to the author default, and with no flag (or --show) it just reads. The response reports the effective bounds, the author default, the override, and wouldPrune (how many live rows the effective bound would prune on the next sweep). The override survives redeploys and effective maxRows is capped at MAX_ROWS_PER_APP.",
|
|
267
298
|
],
|
|
268
299
|
outputNote: 'Output is a single JSON object on stdout, and import additionally writes per-chunk progress lines to stderr. Errors go to stderr as {"error":{"code","message"}} with a non-zero exit.',
|
|
269
300
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homespunapps/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "Command-line client for the Homespun relay: create apps, inspect state, send and watch events.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"test:unit": "vitest run"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@homespunapps/core": "^1.
|
|
39
|
+
"@homespunapps/core": "^1.6.1",
|
|
40
40
|
"qrcode-terminal": "^0.12.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|