@supacloud/cli 0.104.0 → 0.105.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/index.js +162 -19
- package/dist/shared/tools/app-tool-export.test.d.ts +1 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -9996,6 +9996,7 @@ var reference = String2({ minLength: 1, maxLength: 128, pattern: "^[A-Za-z][A-Za
|
|
|
9996
9996
|
var references = _Array_(reference, { uniqueItems: true });
|
|
9997
9997
|
var kind = Union([Literal("api"), Literal("jobs"), Literal("webhook"), Literal("function")]);
|
|
9998
9998
|
var isolation = Union([Literal("shared"), Literal("process")]);
|
|
9999
|
+
var mode = Union([Literal("required"), Literal("none")]);
|
|
9999
10000
|
var DeliveryFilePathSchema = String2({
|
|
10000
10001
|
maxLength: 512,
|
|
10001
10002
|
pattern: "^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*$"
|
|
@@ -10069,6 +10070,7 @@ var routeSchema = _Object_({
|
|
|
10069
10070
|
]),
|
|
10070
10071
|
path: String2(),
|
|
10071
10072
|
command: Optional(String2()),
|
|
10073
|
+
idempotency: Optional(mode),
|
|
10072
10074
|
reason: Union([Literal("default-http"), Literal("explicit-module")])
|
|
10073
10075
|
}, objectOptions);
|
|
10074
10076
|
var jobSchema = _Object_({
|
|
@@ -10213,7 +10215,8 @@ var DeliveryBuildManifestSchema = _Object_({
|
|
|
10213
10215
|
routes: _Array_(_Object_({
|
|
10214
10216
|
method: String2(),
|
|
10215
10217
|
path: String2(),
|
|
10216
|
-
target: String2()
|
|
10218
|
+
target: String2(),
|
|
10219
|
+
idempotency: Optional(Union([Literal("required"), Literal("none")]))
|
|
10217
10220
|
}, closed2)),
|
|
10218
10221
|
jobs: _Array_(_Object_({ name: String2(), target: String2() }, closed2))
|
|
10219
10222
|
}, closed2);
|
|
@@ -10275,7 +10278,12 @@ function parseDeliveryBuildManifest(value) {
|
|
|
10275
10278
|
throw new Error("Invalid delivery object inventory.");
|
|
10276
10279
|
}
|
|
10277
10280
|
}
|
|
10278
|
-
const routes = value.plan.targets.flatMap((target) => target.routes.map((route) => ({
|
|
10281
|
+
const routes = value.plan.targets.flatMap((target) => target.routes.map((route) => ({
|
|
10282
|
+
method: route.method,
|
|
10283
|
+
path: route.path,
|
|
10284
|
+
target: target.name,
|
|
10285
|
+
...route.idempotency === undefined ? {} : { idempotency: route.idempotency }
|
|
10286
|
+
})));
|
|
10279
10287
|
const jobs = value.plan.targets.flatMap((target) => target.jobs.map((job) => ({ name: job.name, target: target.name })));
|
|
10280
10288
|
if (canonical(value.routes) !== canonical(routes) || canonical(value.jobs) !== canonical(jobs)) {
|
|
10281
10289
|
throw new Error("Delivery mappings do not match planned ownership.");
|
|
@@ -21481,9 +21489,13 @@ function uniqueToolName(name, moduleName, used) {
|
|
|
21481
21489
|
}
|
|
21482
21490
|
function findRouteForCommand(manifest, commandClassName) {
|
|
21483
21491
|
for (const module of manifest.modules) {
|
|
21492
|
+
const command = module.commands.find((candidate) => candidate.className === commandClassName || candidate.name === commandClassName);
|
|
21493
|
+
if (!command)
|
|
21494
|
+
continue;
|
|
21484
21495
|
for (const controller of module.controllers) {
|
|
21485
21496
|
for (const route of controller.routes) {
|
|
21486
|
-
|
|
21497
|
+
const transition = module.featureSpec?.transitions.find((candidate) => candidate.route !== undefined && (candidate.command === command.className || candidate.command === command.name) && candidate.route === `${route.method} ${controller.path}${route.path}`);
|
|
21498
|
+
if (route.command !== command.className && route.command !== command.name && transition === undefined)
|
|
21487
21499
|
continue;
|
|
21488
21500
|
return {
|
|
21489
21501
|
httpMethod: route.method,
|
|
@@ -21576,25 +21588,25 @@ var starter_metadata_default = {
|
|
|
21576
21588
|
generatedBy: "supacloud/starter-metadata-v1",
|
|
21577
21589
|
packages: {
|
|
21578
21590
|
compiler: {
|
|
21579
|
-
version: "0.
|
|
21591
|
+
version: "0.49.0"
|
|
21580
21592
|
},
|
|
21581
21593
|
app: {
|
|
21582
|
-
version: "0.
|
|
21594
|
+
version: "0.30.0",
|
|
21583
21595
|
dependencies: {
|
|
21584
21596
|
rxjs: "7.8.2"
|
|
21585
21597
|
}
|
|
21586
21598
|
},
|
|
21587
21599
|
elysia: {
|
|
21588
|
-
version: "0.
|
|
21600
|
+
version: "0.35.0"
|
|
21589
21601
|
},
|
|
21590
21602
|
commands: {
|
|
21591
|
-
version: "0.
|
|
21603
|
+
version: "0.11.0"
|
|
21592
21604
|
},
|
|
21593
21605
|
contracts: {
|
|
21594
|
-
version: "0.
|
|
21606
|
+
version: "0.13.0"
|
|
21595
21607
|
},
|
|
21596
21608
|
db: {
|
|
21597
|
-
version: "0.
|
|
21609
|
+
version: "0.20.0",
|
|
21598
21610
|
peerDependencies: {
|
|
21599
21611
|
"drizzle-orm": "1.0.0-rc.5-5935859"
|
|
21600
21612
|
},
|
|
@@ -21603,7 +21615,7 @@ var starter_metadata_default = {
|
|
|
21603
21615
|
}
|
|
21604
21616
|
},
|
|
21605
21617
|
sdk: {
|
|
21606
|
-
version: "0.
|
|
21618
|
+
version: "0.45.0",
|
|
21607
21619
|
peerDependencies: {
|
|
21608
21620
|
"@supabase/supabase-js": "^2.117.3"
|
|
21609
21621
|
}
|
|
@@ -26248,6 +26260,10 @@ function featureScaffold(name, className, governance) {
|
|
|
26248
26260
|
export const ${className}Params = t.Object({
|
|
26249
26261
|
id: t.String({ minLength: 1, maxLength: 128, pattern: "^[A-Za-z0-9_-]+$" }),
|
|
26250
26262
|
});
|
|
26263
|
+
export const ${className}ReceiptParams = t.Object({
|
|
26264
|
+
id: t.String({ minLength: 1, maxLength: 128, pattern: "^[A-Za-z0-9_-]+$" }),
|
|
26265
|
+
operationId: t.String({ minLength: 1, maxLength: 512, pattern: "^[A-Za-z0-9._:-]+$" }),
|
|
26266
|
+
});
|
|
26251
26267
|
export const ${className}ListQuery = t.Object({
|
|
26252
26268
|
state: t.Optional(t.Union([t.Literal("draft"), t.Literal("approved")])),
|
|
26253
26269
|
});
|
|
@@ -26260,6 +26276,33 @@ export const ${className}Row = t.Object({
|
|
|
26260
26276
|
state: t.String(),
|
|
26261
26277
|
version: t.Integer({ minimum: 1 }),
|
|
26262
26278
|
});
|
|
26279
|
+
export const ${className}Receipt = t.Object({
|
|
26280
|
+
operationId: t.String({ minLength: 1, maxLength: 512 }),
|
|
26281
|
+
status: t.Union([
|
|
26282
|
+
t.Literal("pending"), t.Literal("confirmed"), t.Literal("failed"), t.Literal("unknown"),
|
|
26283
|
+
]),
|
|
26284
|
+
state: t.String(),
|
|
26285
|
+
version: t.Integer({ minimum: 1 }),
|
|
26286
|
+
});
|
|
26287
|
+
export const ${className}JobStatus = t.Object({
|
|
26288
|
+
operationId: t.String({ minLength: 1, maxLength: 512 }),
|
|
26289
|
+
status: t.Union([
|
|
26290
|
+
t.Literal("queued"), t.Literal("running"), t.Literal("completed"),
|
|
26291
|
+
t.Literal("failed"), t.Literal("cancelled"), t.Literal("unknown"),
|
|
26292
|
+
]),
|
|
26293
|
+
errorCode: t.Optional(t.String({ minLength: 1, maxLength: 128 })),
|
|
26294
|
+
});
|
|
26295
|
+
/** Query tags are explicit cache contracts; use them with @supacloud/query. */
|
|
26296
|
+
export const ${className}QueryTags = {
|
|
26297
|
+
list: ${JSON.stringify(`${name}.list`)},
|
|
26298
|
+
detail: ${JSON.stringify(`${name}.detail`)},
|
|
26299
|
+
receipt: ${JSON.stringify(`${name}.receipt`)},
|
|
26300
|
+
} as const;
|
|
26301
|
+
export const ${className}ApproveInvalidateTags = [
|
|
26302
|
+
${className}QueryTags.list,
|
|
26303
|
+
${className}QueryTags.detail,
|
|
26304
|
+
${className}QueryTags.receipt,
|
|
26305
|
+
] as const;
|
|
26263
26306
|
export const ${className}ListResponse = t.Array(${className}Row);
|
|
26264
26307
|
export const ${className}RefreshInput = t.Object({
|
|
26265
26308
|
id: t.String({ minLength: 1, maxLength: 128, pattern: "^[A-Za-z0-9_-]+$" }),
|
|
@@ -26272,6 +26315,9 @@ export const ${className}RefreshInput = t.Object({
|
|
|
26272
26315
|
import type {
|
|
26273
26316
|
${className}ApproveBody,
|
|
26274
26317
|
${className}ListQuery,
|
|
26318
|
+
${className}Receipt,
|
|
26319
|
+
${className}ReceiptParams,
|
|
26320
|
+
${className}JobStatus,
|
|
26275
26321
|
${className}Row,
|
|
26276
26322
|
${className}RefreshInput,
|
|
26277
26323
|
} from "./${name}.contracts";
|
|
@@ -26281,7 +26327,9 @@ export type ${className}ListInput = StaticDecode<typeof ${className}ListQuery>;
|
|
|
26281
26327
|
export type ${className}ApproveInput = StaticDecode<typeof ${className}ApproveBody>;
|
|
26282
26328
|
export type ${className}ApproveCommand = ${className}ApproveInput & { id: string };
|
|
26283
26329
|
export type ${className}Result = StaticDecode<typeof ${className}Row>;
|
|
26330
|
+
export type ${className}ReceiptResult = StaticDecode<typeof ${className}Receipt>;
|
|
26284
26331
|
export type ${className}RefreshJobInput = StaticDecode<typeof ${className}RefreshInput>;
|
|
26332
|
+
export type ${className}JobStatusResult = StaticDecode<typeof ${className}JobStatus>;
|
|
26285
26333
|
|
|
26286
26334
|
export interface ${className}ReadPort {
|
|
26287
26335
|
list(context: FeatureRequestContext, input: ${className}ListInput): Promise<${className}Result[]>;
|
|
@@ -26292,12 +26340,26 @@ export interface ${className}WritePort {
|
|
|
26292
26340
|
approve(context: FeatureRequestContext, input: ${className}ApproveCommand): Promise<${className}Result>;
|
|
26293
26341
|
}
|
|
26294
26342
|
|
|
26343
|
+
export interface ${className}ReceiptPort {
|
|
26344
|
+
lookup(context: FeatureRequestContext, input: { id: string; operationId: string }): Promise<${className}ReceiptResult>;
|
|
26345
|
+
}
|
|
26346
|
+
|
|
26347
|
+
export interface ${className}JobStatusPort {
|
|
26348
|
+
lookup(context: FeatureRequestContext, input: { id: string; operationId: string }): Promise<${className}JobStatusResult>;
|
|
26349
|
+
}
|
|
26350
|
+
|
|
26295
26351
|
export const ${className}Reader = new InjectionToken<${className}ReadPort>(
|
|
26296
26352
|
"${name}.reader", { scope: "request" },
|
|
26297
26353
|
);
|
|
26298
26354
|
export const ${className}Writer = new InjectionToken<${className}WritePort>(
|
|
26299
26355
|
"${name}.writer", { scope: "request" },
|
|
26300
26356
|
);
|
|
26357
|
+
export const ${className}Receipts = new InjectionToken<${className}ReceiptPort>(
|
|
26358
|
+
"${name}.receipts", { scope: "request" },
|
|
26359
|
+
);
|
|
26360
|
+
export const ${className}JobStatuses = new InjectionToken<${className}JobStatusPort>(
|
|
26361
|
+
"${name}.job-statuses", { scope: "request" },
|
|
26362
|
+
);
|
|
26301
26363
|
`,
|
|
26302
26364
|
[`${name}.service.ts`]: `import {
|
|
26303
26365
|
Inject, Injectable, Optional, REQUEST_CONTEXT, requireFeatureRequestContext,
|
|
@@ -26305,10 +26367,15 @@ export const ${className}Writer = new InjectionToken<${className}WritePort>(
|
|
|
26305
26367
|
} from "@supacloud/app/core";
|
|
26306
26368
|
import {
|
|
26307
26369
|
${className}Reader,
|
|
26370
|
+
${className}JobStatuses,
|
|
26371
|
+
${className}Receipts,
|
|
26308
26372
|
${className}Writer,
|
|
26309
26373
|
type ${className}ApproveInput,
|
|
26310
26374
|
type ${className}ReadPort,
|
|
26311
26375
|
type ${className}Result,
|
|
26376
|
+
type ${className}ReceiptPort,
|
|
26377
|
+
type ${className}JobStatusPort,
|
|
26378
|
+
type ${className}JobStatusResult,
|
|
26312
26379
|
type ${className}WritePort,
|
|
26313
26380
|
} from "./${name}.model";
|
|
26314
26381
|
|
|
@@ -26318,6 +26385,8 @@ export class ${className}Service {
|
|
|
26318
26385
|
@Inject(REQUEST_CONTEXT) private readonly requestContext: unknown,
|
|
26319
26386
|
@Inject(${className}Reader) @Optional() private readonly reader?: ${className}ReadPort,
|
|
26320
26387
|
@Inject(${className}Writer) @Optional() private readonly writer?: ${className}WritePort,
|
|
26388
|
+
@Inject(${className}Receipts) @Optional() private readonly receipts?: ${className}ReceiptPort,
|
|
26389
|
+
@Inject(${className}JobStatuses) @Optional() private readonly jobStatuses?: ${className}JobStatusPort,
|
|
26321
26390
|
) {}
|
|
26322
26391
|
|
|
26323
26392
|
private context(): FeatureRequestContext {
|
|
@@ -26338,6 +26407,16 @@ export class ${className}Service {
|
|
|
26338
26407
|
if (!this.writer) throw new Error("Bind ${className}Writer with durable command governance");
|
|
26339
26408
|
return this.writer.approve(this.context(), { ...input, id });
|
|
26340
26409
|
}
|
|
26410
|
+
|
|
26411
|
+
async receipt(id: string, operationId: string): Promise<Awaited<ReturnType<${className}ReceiptPort["lookup"]>>> {
|
|
26412
|
+
if (!this.receipts) throw new Error("Bind ${className}Receipts with durable receipt lookup");
|
|
26413
|
+
return this.receipts.lookup(this.context(), { id, operationId });
|
|
26414
|
+
}
|
|
26415
|
+
|
|
26416
|
+
async jobStatus(id: string, operationId: string): Promise<${className}JobStatusResult> {
|
|
26417
|
+
if (!this.jobStatuses) throw new Error("Bind ${className}JobStatuses with the existing Workflow/Queue status adapter");
|
|
26418
|
+
return this.jobStatuses.lookup(this.context(), { id, operationId });
|
|
26419
|
+
}
|
|
26341
26420
|
}
|
|
26342
26421
|
`,
|
|
26343
26422
|
[`${name}.feature.ts`]: `import { Effect } from "effect";
|
|
@@ -26349,6 +26428,9 @@ import {
|
|
|
26349
26428
|
import {
|
|
26350
26429
|
${className}ApproveBody,
|
|
26351
26430
|
${className}ListQuery,
|
|
26431
|
+
${className}Receipt,
|
|
26432
|
+
${className}ReceiptParams,
|
|
26433
|
+
${className}JobStatus,
|
|
26352
26434
|
${className}ListResponse,
|
|
26353
26435
|
${className}Params,
|
|
26354
26436
|
${className}RefreshInput,
|
|
@@ -26358,6 +26440,8 @@ import { ${className}Service } from "./${name}.service";
|
|
|
26358
26440
|
import type {
|
|
26359
26441
|
${className}ApproveInput,
|
|
26360
26442
|
${className}ListInput,
|
|
26443
|
+
${className}ReceiptResult,
|
|
26444
|
+
${className}JobStatusResult,
|
|
26361
26445
|
${className}RefreshJobInput,
|
|
26362
26446
|
${className}Result,
|
|
26363
26447
|
} from "./${name}.model";
|
|
@@ -26372,6 +26456,11 @@ export const featureSpec = defineFeatureSpec({
|
|
|
26372
26456
|
permission: ${JSON.stringify(permission)}, transaction: "required",
|
|
26373
26457
|
idempotency: "required", audit: ${JSON.stringify(`${name}.approved`)},
|
|
26374
26458
|
job: ${JSON.stringify(`${name}.refresh`)},
|
|
26459
|
+
invalidateTags: [
|
|
26460
|
+
${JSON.stringify(`${name}.list`)},
|
|
26461
|
+
${JSON.stringify(`${name}.detail`)},
|
|
26462
|
+
${JSON.stringify(`${name}.receipt`)},
|
|
26463
|
+
],
|
|
26375
26464
|
persistence: {
|
|
26376
26465
|
boundary: "database", optimisticLock: "required", versionField: "version", tenantField: "tenant_id", stateField: "state",
|
|
26377
26466
|
receipt: "required", reconciliation: "none",
|
|
@@ -26435,6 +26524,7 @@ export class ${className}Controller {
|
|
|
26435
26524
|
|
|
26436
26525
|
@Get("/", {
|
|
26437
26526
|
query: ${className}ListQuery,
|
|
26527
|
+
tags: [${JSON.stringify(`${name}.list`)}],
|
|
26438
26528
|
responses: { 200: ${className}ListResponse },
|
|
26439
26529
|
effect: { required: true, dependencies: [], errors: [], retry: "none" },
|
|
26440
26530
|
})
|
|
@@ -26444,6 +26534,7 @@ export class ${className}Controller {
|
|
|
26444
26534
|
|
|
26445
26535
|
@Get("/:id", {
|
|
26446
26536
|
params: ${className}Params,
|
|
26537
|
+
tags: [${JSON.stringify(`${name}.detail`)}],
|
|
26447
26538
|
responses: { 200: ${className}Row },
|
|
26448
26539
|
effect: { required: true, dependencies: [], errors: [], retry: "none" },
|
|
26449
26540
|
})
|
|
@@ -26456,6 +26547,7 @@ export class ${className}Controller {
|
|
|
26456
26547
|
body: ${className}ApproveBody,
|
|
26457
26548
|
responses: { 200: ${className}Row },
|
|
26458
26549
|
command: Approve${className}Command,
|
|
26550
|
+
idempotency: "required",
|
|
26459
26551
|
effect: { required: true, dependencies: [], errors: [], retry: "none" },
|
|
26460
26552
|
})
|
|
26461
26553
|
approve(
|
|
@@ -26464,6 +26556,32 @@ export class ${className}Controller {
|
|
|
26464
26556
|
): Effect.Effect<${className}Result, never, never> {
|
|
26465
26557
|
return Effect.promise(() => this.approveCommand.execute(id, body));
|
|
26466
26558
|
}
|
|
26559
|
+
|
|
26560
|
+
@Get("/:id/operations/:operationId", {
|
|
26561
|
+
params: ${className}ReceiptParams,
|
|
26562
|
+
tags: [${JSON.stringify(`${name}.receipt`)}],
|
|
26563
|
+
responses: { 200: ${className}Receipt },
|
|
26564
|
+
effect: { required: true, dependencies: [], errors: [], retry: "none" },
|
|
26565
|
+
})
|
|
26566
|
+
receipt(
|
|
26567
|
+
@Param("id") id: string,
|
|
26568
|
+
@Param("operationId") operationId: string,
|
|
26569
|
+
): Effect.Effect<${className}ReceiptResult, never, never> {
|
|
26570
|
+
return Effect.promise(() => this.service.receipt(id, operationId));
|
|
26571
|
+
}
|
|
26572
|
+
|
|
26573
|
+
@Get("/:id/operations/:operationId/status", {
|
|
26574
|
+
params: ${className}ReceiptParams,
|
|
26575
|
+
tags: [${JSON.stringify(`${name}.job-status`)}],
|
|
26576
|
+
responses: { 200: ${className}JobStatus },
|
|
26577
|
+
effect: { required: true, dependencies: [], errors: [], retry: "none" },
|
|
26578
|
+
})
|
|
26579
|
+
jobStatus(
|
|
26580
|
+
@Param("id") id: string,
|
|
26581
|
+
@Param("operationId") operationId: string,
|
|
26582
|
+
): Effect.Effect<${className}JobStatusResult, never, never> {
|
|
26583
|
+
return Effect.promise(() => this.service.jobStatus(id, operationId));
|
|
26584
|
+
}
|
|
26467
26585
|
}
|
|
26468
26586
|
|
|
26469
26587
|
export const ${className}Feature = defineFeatureSlice({
|
|
@@ -26500,6 +26618,7 @@ test("${name} fails closed until verified read and durable write ports are bound
|
|
|
26500
26618
|
await expect(service.list({})).rejects.toThrow("Bind ${className}Reader");
|
|
26501
26619
|
await expect(service.detail("example")).rejects.toThrow("Bind ${className}Reader");
|
|
26502
26620
|
await expect(service.approve("example", { expectedVersion: 1 })).rejects.toThrow("Bind ${className}Writer");
|
|
26621
|
+
await expect(service.jobStatus("example", "operation-example")).rejects.toThrow("Bind ${className}JobStatuses");
|
|
26503
26622
|
});
|
|
26504
26623
|
|
|
26505
26624
|
test("${name} preserves denied reads and rejected writes without fallback or retry", async () => {
|
|
@@ -26611,6 +26730,22 @@ test.skipIf(!enabled)("${name} authenticated list/detail/approval E2E", async ()
|
|
|
26611
26730
|
const firstBody = await json(first);
|
|
26612
26731
|
expect([200, 409, 422]).toContain(first.status);
|
|
26613
26732
|
|
|
26733
|
+
// A lost response is recovered through the durable receipt, never by
|
|
26734
|
+
// submitting the approval again.
|
|
26735
|
+
const recovered = await fetch(
|
|
26736
|
+
new URL("/${name}/" + encodeURIComponent(fixtureId) + "/operations/" + encodeURIComponent(config.idempotencyKey), config.baseUrl),
|
|
26737
|
+
{ headers },
|
|
26738
|
+
);
|
|
26739
|
+
expect(recovered.ok).toBe(true);
|
|
26740
|
+
const recoveredBody = await json(recovered);
|
|
26741
|
+
expect(recoveredBody).toMatchObject({ operationId: config.idempotencyKey });
|
|
26742
|
+
const jobStatus = await fetch(
|
|
26743
|
+
new URL("/${name}/" + encodeURIComponent(fixtureId) + "/operations/" + encodeURIComponent(config.idempotencyKey) + "/status", config.baseUrl),
|
|
26744
|
+
{ headers },
|
|
26745
|
+
);
|
|
26746
|
+
expect(jobStatus.ok).toBe(true);
|
|
26747
|
+
expect(await json(jobStatus)).toMatchObject({ operationId: config.idempotencyKey });
|
|
26748
|
+
|
|
26614
26749
|
// Replaying an identical command may return the original success or a
|
|
26615
26750
|
// domain conflict, but it must never create a second successful transition.
|
|
26616
26751
|
const replay = await approve(config.idempotencyKey);
|
|
@@ -26670,6 +26805,10 @@ test.skipIf(!tenantIsolationEnabled)("${name} rejects cross-tenant access", asyn
|
|
|
26670
26805
|
- [ ] Bind ${className}Writer to a transactional RPC or persistent Command adapter.
|
|
26671
26806
|
- [ ] Register the \`${rpc}\` compiler and runtime RPC capability.
|
|
26672
26807
|
- [ ] Verify RLS, permission denial, stale version, identical replay and conflicting replay.
|
|
26808
|
+
- [ ] Verify receipt lookup recovers a lost response without resubmitting the Command.
|
|
26809
|
+
- [ ] Pass \`${className}ApproveInvalidateTags\` to the generated mutation's \`invalidateTags\` option.
|
|
26810
|
+
- [ ] Bind \`${className}JobStatuses\` to the existing Workflow/Queue status read path.
|
|
26811
|
+
- [ ] Verify status lookup is tenant-scoped and distinguishes queued, running, completed, failed and cancelled.
|
|
26673
26812
|
- [ ] Verify the refresh job uses the existing Workflow/Queue delivery path.
|
|
26674
26813
|
- [ ] Run the generated client contract and authenticated E2E test.
|
|
26675
26814
|
- [ ] Run \`supacloud app check --format json\`, package tests and the selected deployment receipt.
|
|
@@ -26696,10 +26835,14 @@ used by the request-scoped Writer, or own the complete command instead; never ex
|
|
|
26696
26835
|
both paths. Lock/check the version and state, reauthorize replays, and commit the
|
|
26697
26836
|
business write, receipt, audit and outbox together. Do not put a network side effect
|
|
26698
26837
|
inside this transaction. The refresh Job is an explicit unimplemented integration
|
|
26699
|
-
point, not an automatically scheduled workflow.
|
|
26838
|
+
point, not an automatically scheduled workflow. The generated status route is also
|
|
26839
|
+
an explicit read adapter boundary; it does not treat a queue message ID or current
|
|
26840
|
+
business row as proof of task completion.
|
|
26700
26841
|
|
|
26701
26842
|
Compile the routes with the existing client generator and use @supacloud/query for
|
|
26702
|
-
identity-scoped caching/invalidation.
|
|
26843
|
+
identity-scoped caching/invalidation. Use the generated \`${className}ApproveInvalidateTags\`
|
|
26844
|
+
constant for the approval mutation; tags are explicit and never inferred from route names.
|
|
26845
|
+
There is deliberately no second hand-maintained
|
|
26703
26846
|
HTTP client. A committed approval followed by a failed refresh is still committed;
|
|
26704
26847
|
recover its receipt instead of resending. Use the host's standard error translation
|
|
26705
26848
|
and requestId propagation; the scaffold does not claim to implement those adapters.
|
|
@@ -32583,7 +32726,7 @@ async function runProjectDeployment(http, args) {
|
|
|
32583
32726
|
// package.json
|
|
32584
32727
|
var package_default = {
|
|
32585
32728
|
name: "@supacloud/cli",
|
|
32586
|
-
version: "0.
|
|
32729
|
+
version: "0.105.0",
|
|
32587
32730
|
description: "Project-scoped CLI for SupaCloud users",
|
|
32588
32731
|
type: "module",
|
|
32589
32732
|
main: "./dist/index.js",
|
|
@@ -32635,19 +32778,19 @@ var package_default = {
|
|
|
32635
32778
|
directory: "packages/cli"
|
|
32636
32779
|
},
|
|
32637
32780
|
dependencies: {
|
|
32638
|
-
"@supacloud/compiler": "^0.
|
|
32639
|
-
"@supacloud/delivery": "0.
|
|
32640
|
-
"@supacloud/db": "0.
|
|
32781
|
+
"@supacloud/compiler": "^0.49.0",
|
|
32782
|
+
"@supacloud/delivery": "0.21.0",
|
|
32783
|
+
"@supacloud/db": "0.20.0",
|
|
32641
32784
|
typebox: "1.3.37"
|
|
32642
32785
|
},
|
|
32643
32786
|
overrides: {
|
|
32644
|
-
"@supacloud/contracts": "0.
|
|
32645
|
-
"@supacloud/delivery": "0.
|
|
32787
|
+
"@supacloud/contracts": "0.13.0",
|
|
32788
|
+
"@supacloud/delivery": "0.21.0",
|
|
32646
32789
|
"@graphql-tools/utils": "^12.0.4"
|
|
32647
32790
|
},
|
|
32648
32791
|
devDependencies: {
|
|
32649
32792
|
"@supabase/supabase-js": "2.117.3",
|
|
32650
|
-
"@supacloud/elysia": "0.
|
|
32793
|
+
"@supacloud/elysia": "0.35.0",
|
|
32651
32794
|
"@types/bun": "^1.4.3",
|
|
32652
32795
|
effect: "4.0.5",
|
|
32653
32796
|
elysia: "2.0.0-beta.32",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.105.0",
|
|
4
4
|
"description": "Project-scoped CLI for SupaCloud users",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -52,19 +52,19 @@
|
|
|
52
52
|
"directory": "packages/cli"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@supacloud/compiler": "^0.
|
|
56
|
-
"@supacloud/delivery": "0.
|
|
57
|
-
"@supacloud/db": "0.
|
|
55
|
+
"@supacloud/compiler": "^0.49.0",
|
|
56
|
+
"@supacloud/delivery": "0.21.0",
|
|
57
|
+
"@supacloud/db": "0.20.0",
|
|
58
58
|
"typebox": "1.3.37"
|
|
59
59
|
},
|
|
60
60
|
"overrides": {
|
|
61
|
-
"@supacloud/contracts": "0.
|
|
62
|
-
"@supacloud/delivery": "0.
|
|
61
|
+
"@supacloud/contracts": "0.13.0",
|
|
62
|
+
"@supacloud/delivery": "0.21.0",
|
|
63
63
|
"@graphql-tools/utils": "^12.0.4"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@supabase/supabase-js": "2.117.3",
|
|
67
|
-
"@supacloud/elysia": "0.
|
|
67
|
+
"@supacloud/elysia": "0.35.0",
|
|
68
68
|
"@types/bun": "^1.4.3",
|
|
69
69
|
"effect": "4.0.5",
|
|
70
70
|
"elysia": "2.0.0-beta.32",
|