@noodleseed/one 0.168.0 → 0.169.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/dist/commands/deployment-delete-ops.d.ts +12 -0
- package/dist/commands/deployment-delete-ops.d.ts.map +1 -0
- package/dist/commands/deployment-delete-ops.js +230 -0
- package/dist/commands/deployment-delete-ops.js.map +1 -0
- package/dist/commands/deployment-delete-recovery.d.ts +35 -0
- package/dist/commands/deployment-delete-recovery.d.ts.map +1 -0
- package/dist/commands/deployment-delete-recovery.js +99 -0
- package/dist/commands/deployment-delete-recovery.js.map +1 -0
- package/dist/commands/deployments-ops.d.ts +2 -1
- package/dist/commands/deployments-ops.d.ts.map +1 -1
- package/dist/commands/deployments-ops.js +5 -1
- package/dist/commands/deployments-ops.js.map +1 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-tenant-resources.js +27 -1
- package/node_modules/@noodle-borg/control-plane/dist/delete-deployment.d.ts +44 -0
- package/node_modules/@noodle-borg/control-plane/dist/delete-deployment.js +105 -0
- package/node_modules/@noodle-borg/control-plane/dist/deployment-deletion-contracts.d.ts +20 -0
- package/node_modules/@noodle-borg/control-plane/dist/portable.d.ts +2 -0
- package/node_modules/@noodle-borg/control-plane/dist/portable.js +1 -0
- package/node_modules/@noodle-borg/service/dist/deployment-deletion.js +40 -0
- package/node_modules/@noodle-borg/service/dist/registry-compile.js +6 -0
- package/node_modules/@noodle-borg/service/dist/registry-deletion.js +75 -0
- package/node_modules/@noodle-borg/service/dist/registry-state.js +4 -2
- package/node_modules/@noodle-borg/service/dist/registry-targets.js +4 -0
- package/node_modules/@noodle-borg/service/dist/registry.d.ts +2 -0
- package/node_modules/@noodle-borg/service/dist/registry.js +7 -23
- package/node_modules/@noodle-borg/service/dist/routes/deploy-dispatch.js +15 -1
- package/node_modules/@noodle-borg/service/dist/routes/deployment-delete.js +53 -0
- package/node_modules/@noodle-borg/service/dist/store/in-memory.d.ts +2 -1
- package/node_modules/@noodle-borg/service/dist/store/in-memory.js +8 -0
- package/node_modules/@noodle-borg/service/dist/store/json-file-deletion.js +64 -0
- package/node_modules/@noodle-borg/service/dist/store/json-file.d.ts +2 -1
- package/node_modules/@noodle-borg/service/dist/store/json-file.js +23 -5
- package/node_modules/@noodle-borg/service/dist/store.d.ts +5 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/deployment-deletion.d.ts +29 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/deployment-deletion.js +33 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -5,9 +5,11 @@ import { normalizeServerVersion } from '@noodle-borg/module';
|
|
|
5
5
|
import { assertSameAppPackageSnapshot, sanitizeDeployRecordAppPackageSnapshot, } from '../app-package-snapshot.js';
|
|
6
6
|
import { assertCustomerAuthRestorePrecondition, assertUniqueActiveCustomerAuthAudienceBindings, findActiveCustomerAuthAudienceConflict, requiresCustomerAuthProjection, } from '../customer-auth-audience-binding.js';
|
|
7
7
|
import { matchesDeploymentActivation } from '../deployment-activation-precondition.js';
|
|
8
|
+
import { planDeploymentDeletion } from '../deployment-deletion.js';
|
|
8
9
|
import { assertDeploymentActivationUnlocked, assertDeploymentAppendUnlocked, } from '../deployment-lock.js';
|
|
9
10
|
import { assertDeploymentAppendPolicy, assertDeploymentAppendVersion, } from '../deployment-record-version.js';
|
|
10
11
|
import { defaultActiveRecord, sameDeploymentScope, sameTenantRecord, } from '../deployment-versioning.js';
|
|
12
|
+
import { commitDeletedDeploymentIds, readDeletedDeploymentIds, serializeFileLifecycle, } from './json-file-deletion.js';
|
|
11
13
|
import { appArchivedAt, deploymentSummary, matchesDeploymentFilter, paginateAppSummaries, planAppArchive, planAppRestore, planArchivedAppSweep, preserveDeploymentOwnerState, resolveActiveAccessUpdate, resolveProductionEnvironment, summarizeApps, summarizeEnvs, validateDeploymentListFilter, withoutArchiveStamp, } from './records.js';
|
|
12
14
|
import { DEPLOYMENT_ID_PATTERN, validateSlug, validateTenantRef } from './validate.js';
|
|
13
15
|
/**
|
|
@@ -19,11 +21,23 @@ import { DEPLOYMENT_ID_PATTERN, validateSlug, validateTenantRef } from './valida
|
|
|
19
21
|
export class JsonFileArtifactStore {
|
|
20
22
|
#dir;
|
|
21
23
|
#environmentMetadataDir;
|
|
22
|
-
#
|
|
24
|
+
#deletionMetadataDir;
|
|
23
25
|
constructor(dataDir) {
|
|
24
26
|
this.#dir = join(dataDir, 'deployments');
|
|
27
|
+
this.#deletionMetadataDir = join(dataDir, 'deployment-metadata');
|
|
25
28
|
this.#environmentMetadataDir = join(dataDir, 'environment-metadata');
|
|
26
29
|
}
|
|
30
|
+
deleteDeployments(ref, selection) {
|
|
31
|
+
return this.#serializeLifecycle(async () => {
|
|
32
|
+
const result = planDeploymentDeletion(await this.loadAll(), ref, selection);
|
|
33
|
+
if (!result.ok)
|
|
34
|
+
return result;
|
|
35
|
+
await commitDeletedDeploymentIds(this.#deletionMetadataDir, result.deleted.map((record) => record.deploymentId));
|
|
36
|
+
// Logical deletion has committed. Failed physical cleanup remains hidden by the journal on restart.
|
|
37
|
+
await Promise.all(result.deleted.map((record) => rm(join(this.#dir, `${record.deploymentId}.json`), { force: true }).catch(() => undefined)));
|
|
38
|
+
return result;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
27
41
|
append(record, precondition) {
|
|
28
42
|
return this.#serializeLifecycle(() => this.#appendUnlocked(record, precondition));
|
|
29
43
|
}
|
|
@@ -35,6 +49,9 @@ export class JsonFileArtifactStore {
|
|
|
35
49
|
if (!DEPLOYMENT_ID_PATTERN.test(record.deploymentId)) {
|
|
36
50
|
throw new Error(`invalid deploymentId for persistence: "${record.deploymentId}"`);
|
|
37
51
|
}
|
|
52
|
+
if ((await readDeletedDeploymentIds(this.#deletionMetadataDir)).has(record.deploymentId)) {
|
|
53
|
+
throw new Error('Deployment ID has been deleted');
|
|
54
|
+
}
|
|
38
55
|
const records = await this.loadAll();
|
|
39
56
|
assertDeploymentAppendVersion(records, record);
|
|
40
57
|
assertDeploymentAppendPolicy(records, record, precondition);
|
|
@@ -63,6 +80,8 @@ export class JsonFileArtifactStore {
|
|
|
63
80
|
// An invalid id can never be a file we wrote; treat as absent (also closes off any traversal).
|
|
64
81
|
if (!DEPLOYMENT_ID_PATTERN.test(deploymentId))
|
|
65
82
|
return undefined;
|
|
83
|
+
if ((await readDeletedDeploymentIds(this.#deletionMetadataDir)).has(deploymentId))
|
|
84
|
+
return undefined;
|
|
66
85
|
try {
|
|
67
86
|
const text = await readFile(join(this.#dir, `${deploymentId}.json`), 'utf8');
|
|
68
87
|
return sanitizeDeployRecordAppPackageSnapshot(JSON.parse(text));
|
|
@@ -172,9 +191,7 @@ export class JsonFileArtifactStore {
|
|
|
172
191
|
});
|
|
173
192
|
}
|
|
174
193
|
#serializeLifecycle(operation) {
|
|
175
|
-
|
|
176
|
-
this.#lifecycleTail = result.then(() => undefined, () => undefined);
|
|
177
|
-
return result;
|
|
194
|
+
return serializeFileLifecycle(this.#dir, operation);
|
|
178
195
|
}
|
|
179
196
|
async loadAll() {
|
|
180
197
|
let names;
|
|
@@ -199,7 +216,8 @@ export class JsonFileArtifactStore {
|
|
|
199
216
|
// other servers recover. (Visibility of skipped files lands with structured logging, Slice 27.)
|
|
200
217
|
}
|
|
201
218
|
}
|
|
202
|
-
|
|
219
|
+
const deleted = await readDeletedDeploymentIds(this.#deletionMetadataDir);
|
|
220
|
+
return records.filter((record) => !deleted.has(record.deploymentId));
|
|
203
221
|
}
|
|
204
222
|
async listDeployments(filter) {
|
|
205
223
|
const safe = validateDeploymentListFilter(filter);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ArtifactCustomerAuthRouting, HostedPackagedAsset } from '@noodle-borg/compiler';
|
|
2
|
-
import type { OrganizationStore } from '@noodle-borg/control-plane/portable';
|
|
2
|
+
import type { DeploymentDeleteSelection, OrganizationStore, DeploymentDeleteResult as PortableDeploymentDeleteResult } from '@noodle-borg/control-plane/portable';
|
|
3
|
+
export type { DeploymentDeleteSelection } from '@noodle-borg/control-plane/portable';
|
|
3
4
|
import type { OrgMembershipSource } from '@noodle-borg/module';
|
|
4
5
|
import type { SealedSecret } from '@noodle-borg/runtime';
|
|
5
6
|
import type { AccessMode } from '@noodle-borg/transport-http';
|
|
@@ -76,6 +77,7 @@ export interface DeployRecord {
|
|
|
76
77
|
/** Immutable rendered package bytes bound to this exact deployment, when the manifest has a guide. */
|
|
77
78
|
readonly appPackageSnapshot?: AppPackageSnapshotV1;
|
|
78
79
|
}
|
|
80
|
+
export type DeploymentDeleteResult = PortableDeploymentDeleteResult<DeployRecord>;
|
|
79
81
|
export interface DeploymentLockMetadata {
|
|
80
82
|
/** ISO-8601 time the version pointer was locked. */
|
|
81
83
|
readonly lockedAt: string;
|
|
@@ -302,6 +304,8 @@ export interface ControlPlaneStore extends OrganizationStore {
|
|
|
302
304
|
* an earlier deployment without changing the tenant-facing URL.
|
|
303
305
|
*/
|
|
304
306
|
export interface ArtifactStore {
|
|
307
|
+
/** Atomically remove exact deployment history or an inventory-guarded whole version. */
|
|
308
|
+
deleteDeployments(ref: TenantRef, selection: DeploymentDeleteSelection): Promise<DeploymentDeleteResult>;
|
|
305
309
|
append(record: DeployRecord, precondition?: DeploymentPolicyPrecondition): Promise<void>;
|
|
306
310
|
loadAll(): Promise<readonly DeployRecord[]>;
|
|
307
311
|
/**
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/** Bind destructive version deletion to the complete inventory the operator confirmed. */
|
|
3
|
+
export declare const deploymentVersionDeleteRequestSchema: z.ZodObject<{
|
|
4
|
+
expectedDeploymentIds: z.ZodArray<z.ZodString>;
|
|
5
|
+
}, z.core.$strict>;
|
|
6
|
+
export type DeploymentVersionDeleteRequest = z.infer<typeof deploymentVersionDeleteRequestSchema>;
|
|
7
|
+
export declare const deploymentDeleteResponseSchema: z.ZodObject<{
|
|
8
|
+
target: z.ZodObject<{
|
|
9
|
+
org: z.ZodString;
|
|
10
|
+
app: z.ZodString;
|
|
11
|
+
env: z.ZodString;
|
|
12
|
+
}, z.core.$strict>;
|
|
13
|
+
ok: z.ZodLiteral<true>;
|
|
14
|
+
deletedDeploymentIds: z.ZodArray<z.ZodString>;
|
|
15
|
+
auditRecorded: z.ZodBoolean;
|
|
16
|
+
}, z.core.$strict>;
|
|
17
|
+
/** Additive response reader for clients that can outlive a service revision. */
|
|
18
|
+
export declare const deploymentDeleteClientResponseSchema: z.ZodObject<{
|
|
19
|
+
target: z.ZodObject<{
|
|
20
|
+
org: z.ZodString;
|
|
21
|
+
app: z.ZodString;
|
|
22
|
+
env: z.ZodString;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
ok: z.ZodLiteral<true>;
|
|
25
|
+
deletedDeploymentIds: z.ZodArray<z.ZodString>;
|
|
26
|
+
auditRecorded: z.ZodBoolean;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
export type DeploymentDeleteResponse = z.infer<typeof deploymentDeleteResponseSchema>;
|
|
29
|
+
//# sourceMappingURL=deployment-deletion.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const deploymentId = z
|
|
3
|
+
.string()
|
|
4
|
+
.min(1)
|
|
5
|
+
.max(256)
|
|
6
|
+
.regex(/^[a-z0-9-]+$/);
|
|
7
|
+
const ids = z
|
|
8
|
+
.array(deploymentId)
|
|
9
|
+
.min(1)
|
|
10
|
+
.max(10000)
|
|
11
|
+
.refine((values) => new Set(values).size === values.length, 'deployment IDs must be unique');
|
|
12
|
+
const target = {
|
|
13
|
+
org: z.string().min(1),
|
|
14
|
+
app: z.string().min(1),
|
|
15
|
+
env: z.string().min(1),
|
|
16
|
+
};
|
|
17
|
+
/** Bind destructive version deletion to the complete inventory the operator confirmed. */
|
|
18
|
+
export const deploymentVersionDeleteRequestSchema = z.strictObject({ expectedDeploymentIds: ids });
|
|
19
|
+
const result = {
|
|
20
|
+
ok: z.literal(true),
|
|
21
|
+
deletedDeploymentIds: ids,
|
|
22
|
+
auditRecorded: z.boolean(),
|
|
23
|
+
};
|
|
24
|
+
export const deploymentDeleteResponseSchema = z.strictObject({
|
|
25
|
+
...result,
|
|
26
|
+
target: z.strictObject(target),
|
|
27
|
+
});
|
|
28
|
+
/** Additive response reader for clients that can outlive a service revision. */
|
|
29
|
+
export const deploymentDeleteClientResponseSchema = z.object({
|
|
30
|
+
...result,
|
|
31
|
+
target: z.object(target),
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=deployment-deletion.js.map
|
|
@@ -389,6 +389,7 @@ export * from './application-activity.js';
|
|
|
389
389
|
export * from './application-connections.js';
|
|
390
390
|
export * from './application-onboarding.js';
|
|
391
391
|
export * from './billing-catalog.js';
|
|
392
|
+
export * from './deployment-deletion.js';
|
|
392
393
|
export * from './operation-coordination.js';
|
|
393
394
|
export { MIXED_CUSTOMER_AUTH_FEATURE_VERSION, type ServiceInfoClientResponse, type ServiceInfoResponse, serviceInfoClientResponseSchema, serviceInfoResponseSchema, } from './service-info.js';
|
|
394
395
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -228,6 +228,7 @@ export * from './application-activity.js';
|
|
|
228
228
|
export * from './application-connections.js';
|
|
229
229
|
export * from './application-onboarding.js';
|
|
230
230
|
export * from './billing-catalog.js';
|
|
231
|
+
export * from './deployment-deletion.js';
|
|
231
232
|
export * from './operation-coordination.js';
|
|
232
233
|
export { MIXED_CUSTOMER_AUTH_FEATURE_VERSION, serviceInfoClientResponseSchema, serviceInfoResponseSchema, } from './service-info.js';
|
|
233
234
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noodleseed/one",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.169.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Noodle CLI by Noodle Seed — author, run, and deploy declarative MCP servers. Embedding the assistant in your own web app is @noodleseed/assistant.",
|
|
6
6
|
"license": "Apache-2.0",
|