@serve.zone/interfaces 32.69.0 → 32.70.0

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/changelog.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-11-05 - 32.70.0
4
+
5
+ ### Features
6
+
7
+ - Partial restore of an incomplete backup (Linus 2026-11-05 ruling): a `failed` backup record can name its failure's kind, `data.IBackupRecord.failure` (`data.TBackupFailure`): `incomplete` with `uncaptured`, each resource its service declares that the backup did not capture (`data.TBackupUncapturedResource`: a volume by mount path, always with reason `volume-capture-unavailable`, or a binding by id, always with reason `binding-not-snapshotted`; `data.TBackupUncapturedReason` names either), or `archive-unreadable`. `requests.backup.IReq_Any_Cloudly_RestoreServiceBackup` takes `confirmPartialRestore`, which an administrator sets to restore what an `incomplete` backup captured: `resourceTypes` is required and names only captured types, nothing in `uncaptured` is restored, so the service then holds data from two points in time, and the backup stays `failed`; it is refused for any other backup. `data.readPartialRestoreResourceTypes` answers the types such a restore may name: those the backup holds a snapshot of, except `volume` (a `volume` snapshot captures no service volume) and the type of any uncaptured resource; none for any other backup. A restore event (`data.IBackupRestoreEvent`) states the `resourceTypes` it was limited to and `partial` for such a restore. Additive: no validator reads these shapes. Spec: `test.backuppartialrestore`.
8
+
3
9
  ## 2026-11-05 - 32.69.0
4
10
 
5
11
  ### Features
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/interfaces',
6
- version: '32.69.0',
6
+ version: '32.70.0',
7
7
  description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsV0FBVyxFQUFFLHVGQUF1RjtDQUNyRyxDQUFBIn0=
@@ -48,10 +48,52 @@ export interface IBackupReplicationResult {
48
48
  totalSize: number;
49
49
  completedAt: number;
50
50
  }
51
+ /**
52
+ * One resource a service declares that a backup of it did not capture, with why; each kind of
53
+ * resource has its own reason.
54
+ *
55
+ * `volume-capture-unavailable`: a service volume is a node-local storage claim on its Pallet node,
56
+ * which no relay can capture yet; a `volume` snapshot is a Corestore Docker volume and captures no
57
+ * service volume. `binding-not-snapshotted`: the backup holds no snapshot of the Corestore resource
58
+ * the binding names.
59
+ */
60
+ export type TBackupUncapturedResource = {
61
+ type: 'volume';
62
+ mountPath: string;
63
+ reason: 'volume-capture-unavailable';
64
+ } | {
65
+ type: 'database' | 'objectstorage';
66
+ bindingId: string;
67
+ reason: 'binding-not-snapshotted';
68
+ };
69
+ /** Why a resource a service declares is not in one of its backups (`TBackupUncapturedResource`). */
70
+ export type TBackupUncapturedReason = TBackupUncapturedResource['reason'];
71
+ /**
72
+ * The named kind of a `failed` backup's failure; a failure without one is named only in `errorText`.
73
+ *
74
+ * `incomplete`: the backup did not capture every resource its service declares, and `uncaptured`
75
+ * names each one it missed. What it did capture is restored only by a partial restore an
76
+ * administrator confirms (`IReq_Any_Cloudly_RestoreServiceBackup` `confirmPartialRestore`), which
77
+ * restores none of `uncaptured`, and the backup stays `failed`.
78
+ * `archive-unreadable`: its stored archive is missing or damaged, so it restores nothing.
79
+ */
80
+ export type TBackupFailure = {
81
+ kind: 'incomplete';
82
+ uncaptured: TBackupUncapturedResource[];
83
+ } | {
84
+ kind: 'archive-unreadable';
85
+ };
51
86
  export interface IBackupRestoreEvent {
52
87
  restoredAt: number;
53
88
  status: 'restored' | 'failed';
54
89
  errorText?: string;
90
+ /** The resource types the restore was limited to; absent when it restored every snapshot. */
91
+ resourceTypes?: TBackupResourceType[];
92
+ /**
93
+ * A confirmed partial restore of an `incomplete` backup: it restored only `resourceTypes`, and
94
+ * none of the backup's `failure.uncaptured`.
95
+ */
96
+ partial?: true;
55
97
  }
56
98
  /**
57
99
  * Durable retention hold owned by one isolated restore. An entry is active
@@ -88,6 +130,8 @@ export interface IBackupRecord {
88
130
  completedAt?: number;
89
131
  requestedBy?: string;
90
132
  errorText?: string;
133
+ /** The named kind of a `failed` backup's failure (`TBackupFailure`). */
134
+ failure?: TBackupFailure;
91
135
  restoreHistory?: IBackupRestoreEvent[];
92
136
  /**
93
137
  * Authoritative isolated-restore holds. Cloudly atomically appends the
@@ -97,6 +141,17 @@ export interface IBackupRecord {
97
141
  restoreHolds?: IBackupRestoreHold[];
98
142
  tags?: Record<string, string>;
99
143
  }
144
+ /**
145
+ * The resource types a partial restore of a backup may name (`confirmPartialRestore`).
146
+ *
147
+ * For a `failed` backup whose failure is `incomplete`: every type it holds a snapshot of, except
148
+ * `volume` (a `volume` snapshot is a Corestore Docker volume and captures no service volume) and the
149
+ * type of any resource it did not capture, so a partial restore never restores a snapshot standing
150
+ * for a resource the backup missed. For every other backup: none. Database before object storage.
151
+ */
152
+ export declare const readPartialRestoreResourceTypes: (backupArg: Pick<IBackupRecord, 'status' | 'failure'> & {
153
+ snapshots: ReadonlyArray<Pick<TBackupSnapshot, 'type'>>;
154
+ }) => Array<Exclude<TBackupResourceType, 'volume'>>;
100
155
  /** Retention policy understood by containerarchive prune. */
101
156
  export interface IArchiveRetentionPolicy {
102
157
  keepLast?: number;
@@ -1,2 +1,16 @@
1
- export {};
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja3VwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvZGF0YS9iYWNrdXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
1
+ /**
2
+ * The resource types a partial restore of a backup may name (`confirmPartialRestore`).
3
+ *
4
+ * For a `failed` backup whose failure is `incomplete`: every type it holds a snapshot of, except
5
+ * `volume` (a `volume` snapshot is a Corestore Docker volume and captures no service volume) and the
6
+ * type of any resource it did not capture, so a partial restore never restores a snapshot standing
7
+ * for a resource the backup missed. For every other backup: none. Database before object storage.
8
+ */
9
+ export const readPartialRestoreResourceTypes = (backupArg) => {
10
+ const failure = backupArg.failure;
11
+ if (backupArg.status !== 'failed' || failure?.kind !== 'incomplete')
12
+ return [];
13
+ const uncapturedTypes = new Set(failure.uncaptured.map((resourceArg) => resourceArg.type));
14
+ return ['database', 'objectstorage'].filter((typeArg) => (!uncapturedTypes.has(typeArg) && backupArg.snapshots.some((snapshotArg) => snapshotArg.type === typeArg)));
15
+ };
16
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja3VwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvZGF0YS9iYWNrdXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBb0tBOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLENBQUMsTUFBTSwrQkFBK0IsR0FBRyxDQUM3QyxTQUFrSCxFQUNuRSxFQUFFO0lBQ2pELE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUM7SUFDbEMsSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLFFBQVEsSUFBSSxPQUFPLEVBQUUsSUFBSSxLQUFLLFlBQVk7UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUMvRSxNQUFNLGVBQWUsR0FBRyxJQUFJLEdBQUcsQ0FBc0IsT0FBTyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ2hILE9BQVEsQ0FBQyxVQUFVLEVBQUUsZUFBZSxDQUFXLENBQUMsTUFBTSxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUNsRSxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLElBQUksU0FBUyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEtBQUssT0FBTyxDQUFDLENBQ3pHLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyJ9
@@ -45,6 +45,14 @@ export interface IReq_Any_Cloudly_RestoreServiceBackup extends plugins.typedrequ
45
45
  backupId: string;
46
46
  clear?: boolean;
47
47
  resourceTypes?: Array<TBackupSnapshot['type']>;
48
+ /**
49
+ * Must be `true` to restore a `failed` backup whose failure is `incomplete`, and is refused for
50
+ * any other backup. Such a restore restores what the backup captured: `resourceTypes` is
51
+ * required and names only types it captured, none of the resources it did not capture
52
+ * (`failure.uncaptured`) is restored, so the service then holds data from two points in time,
53
+ * and the backup stays `failed`.
54
+ */
55
+ confirmPartialRestore?: boolean;
48
56
  };
49
57
  response: {
50
58
  backup: IBackupRecord;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serve.zone/interfaces",
3
- "version": "32.69.0",
3
+ "version": "32.70.0",
4
4
  "private": false,
5
5
  "description": "Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.",
6
6
  "exports": {
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/interfaces',
6
- version: '32.69.0',
6
+ version: '32.70.0',
7
7
  description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
8
8
  }
package/ts/data/backup.ts CHANGED
@@ -72,10 +72,46 @@ export interface IBackupReplicationResult {
72
72
  completedAt: number;
73
73
  }
74
74
 
75
+ /**
76
+ * One resource a service declares that a backup of it did not capture, with why; each kind of
77
+ * resource has its own reason.
78
+ *
79
+ * `volume-capture-unavailable`: a service volume is a node-local storage claim on its Pallet node,
80
+ * which no relay can capture yet; a `volume` snapshot is a Corestore Docker volume and captures no
81
+ * service volume. `binding-not-snapshotted`: the backup holds no snapshot of the Corestore resource
82
+ * the binding names.
83
+ */
84
+ export type TBackupUncapturedResource =
85
+ | { type: 'volume'; mountPath: string; reason: 'volume-capture-unavailable' }
86
+ | { type: 'database' | 'objectstorage'; bindingId: string; reason: 'binding-not-snapshotted' };
87
+
88
+ /** Why a resource a service declares is not in one of its backups (`TBackupUncapturedResource`). */
89
+ export type TBackupUncapturedReason = TBackupUncapturedResource['reason'];
90
+
91
+ /**
92
+ * The named kind of a `failed` backup's failure; a failure without one is named only in `errorText`.
93
+ *
94
+ * `incomplete`: the backup did not capture every resource its service declares, and `uncaptured`
95
+ * names each one it missed. What it did capture is restored only by a partial restore an
96
+ * administrator confirms (`IReq_Any_Cloudly_RestoreServiceBackup` `confirmPartialRestore`), which
97
+ * restores none of `uncaptured`, and the backup stays `failed`.
98
+ * `archive-unreadable`: its stored archive is missing or damaged, so it restores nothing.
99
+ */
100
+ export type TBackupFailure =
101
+ | { kind: 'incomplete'; uncaptured: TBackupUncapturedResource[] }
102
+ | { kind: 'archive-unreadable' };
103
+
75
104
  export interface IBackupRestoreEvent {
76
105
  restoredAt: number;
77
106
  status: 'restored' | 'failed';
78
107
  errorText?: string;
108
+ /** The resource types the restore was limited to; absent when it restored every snapshot. */
109
+ resourceTypes?: TBackupResourceType[];
110
+ /**
111
+ * A confirmed partial restore of an `incomplete` backup: it restored only `resourceTypes`, and
112
+ * none of the backup's `failure.uncaptured`.
113
+ */
114
+ partial?: true;
79
115
  }
80
116
 
81
117
  /**
@@ -114,6 +150,8 @@ export interface IBackupRecord {
114
150
  completedAt?: number;
115
151
  requestedBy?: string;
116
152
  errorText?: string;
153
+ /** The named kind of a `failed` backup's failure (`TBackupFailure`). */
154
+ failure?: TBackupFailure;
117
155
  restoreHistory?: IBackupRestoreEvent[];
118
156
  /**
119
157
  * Authoritative isolated-restore holds. Cloudly atomically appends the
@@ -124,6 +162,25 @@ export interface IBackupRecord {
124
162
  tags?: Record<string, string>;
125
163
  }
126
164
 
165
+ /**
166
+ * The resource types a partial restore of a backup may name (`confirmPartialRestore`).
167
+ *
168
+ * For a `failed` backup whose failure is `incomplete`: every type it holds a snapshot of, except
169
+ * `volume` (a `volume` snapshot is a Corestore Docker volume and captures no service volume) and the
170
+ * type of any resource it did not capture, so a partial restore never restores a snapshot standing
171
+ * for a resource the backup missed. For every other backup: none. Database before object storage.
172
+ */
173
+ export const readPartialRestoreResourceTypes = (
174
+ backupArg: Pick<IBackupRecord, 'status' | 'failure'> & { snapshots: ReadonlyArray<Pick<TBackupSnapshot, 'type'>> },
175
+ ): Array<Exclude<TBackupResourceType, 'volume'>> => {
176
+ const failure = backupArg.failure;
177
+ if (backupArg.status !== 'failed' || failure?.kind !== 'incomplete') return [];
178
+ const uncapturedTypes = new Set<TBackupResourceType>(failure.uncaptured.map((resourceArg) => resourceArg.type));
179
+ return (['database', 'objectstorage'] as const).filter((typeArg) => (
180
+ !uncapturedTypes.has(typeArg) && backupArg.snapshots.some((snapshotArg) => snapshotArg.type === typeArg)
181
+ ));
182
+ };
183
+
127
184
  /** Retention policy understood by containerarchive prune. */
128
185
  export interface IArchiveRetentionPolicy {
129
186
  keepLast?: number;
@@ -81,6 +81,14 @@ extends plugins.typedrequestInterfaces.implementsTR<
81
81
  backupId: string;
82
82
  clear?: boolean;
83
83
  resourceTypes?: Array<TBackupSnapshot['type']>;
84
+ /**
85
+ * Must be `true` to restore a `failed` backup whose failure is `incomplete`, and is refused for
86
+ * any other backup. Such a restore restores what the backup captured: `resourceTypes` is
87
+ * required and names only types it captured, none of the resources it did not capture
88
+ * (`failure.uncaptured`) is restored, so the service then holds data from two points in time,
89
+ * and the backup stays `failed`.
90
+ */
91
+ confirmPartialRestore?: boolean;
84
92
  };
85
93
  response: {
86
94
  backup: IBackupRecord;