@homespunapps/core 1.6.38 → 1.6.40

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/client.d.ts CHANGED
@@ -613,6 +613,26 @@ export declare class HomespunClient {
613
613
  * is a 404 (HomespunApiError `row_not_found`), matching a normal delete.
614
614
  */
615
615
  purgeAppRow(appId: string, collection: string, key: string): Promise<void>;
616
+ /**
617
+ * POST /v1/apps/:id/collections/:name/:key/restore: undo a soft delete.
618
+ *
619
+ * Owner/agent only, independent of the collection's permission lists. A row
620
+ * that was PURGED is not restorable (410 `restore_expired`), and a restore
621
+ * can legitimately fail on quota or on a unique value another live row took
622
+ * while this one was deleted (409 `restore_conflict`).
623
+ */
624
+ restoreAppRow(appId: string, collection: string, key: string): Promise<{
625
+ row: AppRow;
626
+ }>;
627
+ /**
628
+ * GET /v1/apps/:id/collections/:name/deleted: the recovery bin, newest
629
+ * deletion first. Owner/agent only. Page with `before`, passing back the
630
+ * previous page's `next_before`.
631
+ */
632
+ listDeletedAppRows(appId: string, collection: string, opts?: {
633
+ limit?: number;
634
+ before?: string;
635
+ }): Promise<DeletedAppRowsPage>;
616
636
  /** PATCH /v1/apps/:id/collections/:name/:key — optimistic-locked update. */
617
637
  updateAppRow(appId: string, collection: string, key: string, body: {
618
638
  data: unknown;
@@ -666,6 +686,15 @@ export declare class HomespunClient {
666
686
  decision: "approve" | "reject";
667
687
  note?: string;
668
688
  }): Promise<CommunitySubmissionDetail>;
689
+ /**
690
+ * POST /v1/community/submissions/:id/unpublish (publisher): take your own
691
+ * live listing down. It leaves the public gallery, search, and the direct
692
+ * snapshot install link; existing installs are untouched, because an install
693
+ * is a fresh private copy rather than a live reference. Idempotent, and 404
694
+ * when the snapshot does not exist OR is not yours (indistinguishable on
695
+ * purpose). Republish a new version to undo it.
696
+ */
697
+ unpublishCommunityTemplate(snapshotId: string): Promise<CommunitySubmissionDetail>;
669
698
  /**
670
699
  * GET the install-time config contract for an installable template (its
671
700
  * settings collection + the ordered keyed config/upload steps). Use it to
@@ -1100,6 +1129,32 @@ export interface AppRow {
1100
1129
  updated_at: string;
1101
1130
  deleted_at: string | null;
1102
1131
  }
1132
+ /** One tombstoned row as the recovery bin reports it. */
1133
+ export interface DeletedAppRow {
1134
+ key: string;
1135
+ data: unknown;
1136
+ version: number;
1137
+ /** Who removed it. */
1138
+ author: {
1139
+ kind: string;
1140
+ id: string;
1141
+ };
1142
+ /** Who created it; null on rows predating the creator columns. */
1143
+ creator: {
1144
+ kind: string;
1145
+ id: string;
1146
+ } | null;
1147
+ created_at: string;
1148
+ deleted_at: string;
1149
+ /** When this row stops being recoverable. */
1150
+ recoverable_until: string;
1151
+ /** True once purged: contents already scrubbed, and restore refuses it. */
1152
+ purged: boolean;
1153
+ }
1154
+ export interface DeletedAppRowsPage {
1155
+ rows: DeletedAppRow[];
1156
+ next_before: string | null;
1157
+ }
1103
1158
  export interface AppRowsPage {
1104
1159
  rows: AppRow[];
1105
1160
  next_cursor: string | null;
package/dist/client.js CHANGED
@@ -1045,6 +1045,37 @@ export class HomespunClient {
1045
1045
  if (!r.ok)
1046
1046
  this.fail(r);
1047
1047
  }
1048
+ /**
1049
+ * POST /v1/apps/:id/collections/:name/:key/restore: undo a soft delete.
1050
+ *
1051
+ * Owner/agent only, independent of the collection's permission lists. A row
1052
+ * that was PURGED is not restorable (410 `restore_expired`), and a restore
1053
+ * can legitimately fail on quota or on a unique value another live row took
1054
+ * while this one was deleted (409 `restore_conflict`).
1055
+ */
1056
+ async restoreAppRow(appId, collection, key) {
1057
+ const r = await this.call("POST", `/v1/apps/${encodeURIComponent(appId)}/collections/${encodeURIComponent(collection)}/${encodeURIComponent(key)}/restore`);
1058
+ if (!r.ok)
1059
+ this.fail(r);
1060
+ return this.asObject(r);
1061
+ }
1062
+ /**
1063
+ * GET /v1/apps/:id/collections/:name/deleted: the recovery bin, newest
1064
+ * deletion first. Owner/agent only. Page with `before`, passing back the
1065
+ * previous page's `next_before`.
1066
+ */
1067
+ async listDeletedAppRows(appId, collection, opts = {}) {
1068
+ const q = new URLSearchParams();
1069
+ if (opts.limit !== undefined)
1070
+ q.set("limit", String(opts.limit));
1071
+ if (opts.before !== undefined)
1072
+ q.set("before", opts.before);
1073
+ const qs = q.toString();
1074
+ const r = await this.call("GET", `/v1/apps/${encodeURIComponent(appId)}/collections/${encodeURIComponent(collection)}/deleted${qs ? `?${qs}` : ""}`);
1075
+ if (!r.ok)
1076
+ this.fail(r);
1077
+ return this.asObject(r);
1078
+ }
1048
1079
  /** PATCH /v1/apps/:id/collections/:name/:key — optimistic-locked update. */
1049
1080
  async updateAppRow(appId, collection, key, body) {
1050
1081
  const r = await this.call("PATCH", `/v1/apps/${encodeURIComponent(appId)}/collections/${encodeURIComponent(collection)}/${encodeURIComponent(key)}`, body);
@@ -1164,6 +1195,21 @@ export class HomespunClient {
1164
1195
  this.fail(r);
1165
1196
  return this.asObject(r);
1166
1197
  }
1198
+ /**
1199
+ * POST /v1/community/submissions/:id/unpublish (publisher): take your own
1200
+ * live listing down. It leaves the public gallery, search, and the direct
1201
+ * snapshot install link; existing installs are untouched, because an install
1202
+ * is a fresh private copy rather than a live reference. Idempotent, and 404
1203
+ * when the snapshot does not exist OR is not yours (indistinguishable on
1204
+ * purpose). Republish a new version to undo it.
1205
+ */
1206
+ async unpublishCommunityTemplate(snapshotId) {
1207
+ const path = `/v1/community/submissions/${encodeURIComponent(snapshotId)}/unpublish`;
1208
+ const r = await this.call("POST", path);
1209
+ if (!r.ok)
1210
+ this.fail(r);
1211
+ return this.asObject(r);
1212
+ }
1167
1213
  // -------------------------------------------------------------------------
1168
1214
  // Agent-key community install (install-config programme, PR 4). Both act AS
1169
1215
  // the calling agent's owning human, who becomes the installed app's owner. A
package/dist/icons.js CHANGED
@@ -65,8 +65,8 @@ export function validateIconEmoji(raw) {
65
65
  error: "icon_emoji must not contain control characters",
66
66
  };
67
67
  }
68
- // Count grapheme clusters. Intl.Segmenter is available in Node 20+ and all
69
- // evergreen browsers (our targets); guard defensively anyway.
68
+ // Count grapheme clusters. Intl.Segmenter is available in every supported
69
+ // Node and all evergreen browsers (our targets); guard defensively anyway.
70
70
  let graphemeCount;
71
71
  if (typeof Intl !== "undefined" && typeof Intl.Segmenter === "function") {
72
72
  const seg = new Intl.Segmenter("en", { granularity: "grapheme" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homespunapps/core",
3
- "version": "1.6.38",
3
+ "version": "1.6.40",
4
4
  "description": "Homespun relay client: typed HTTP + WebSocket operations against a Homespun relay. Framework-free.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,10 +45,10 @@
45
45
  "zod": "^4.4.3"
46
46
  },
47
47
  "devDependencies": {
48
- "@types/node": "^26.1.1",
48
+ "@types/node": "^26.1.2",
49
49
  "@types/ws": "^8.18.1",
50
50
  "typescript": "^7.0.2",
51
- "vitest": "^4.1.8"
51
+ "vitest": "^4.1.10"
52
52
  },
53
53
  "repository": {
54
54
  "type": "git",