@supacloud/cli 0.108.0 → 0.108.2
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 +154 -102
- package/dist/shared/tools/app-starter-output.test.d.ts +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -21603,7 +21603,7 @@ var starter_metadata_default = {
|
|
|
21603
21603
|
version: "0.11.0"
|
|
21604
21604
|
},
|
|
21605
21605
|
contracts: {
|
|
21606
|
-
version: "0.
|
|
21606
|
+
version: "0.15.0"
|
|
21607
21607
|
},
|
|
21608
21608
|
db: {
|
|
21609
21609
|
version: "0.20.0",
|
|
@@ -21949,7 +21949,7 @@ function baseFiles(name, sdkDependencies) {
|
|
|
21949
21949
|
check: "bun run compile && bun run check:generated && bun run typecheck && bun run test",
|
|
21950
21950
|
test: "bun run compile && bun --no-env-file scripts/environment.ts test bun test",
|
|
21951
21951
|
dev: "bun --no-env-file scripts/environment.ts development bun scripts/dev.ts",
|
|
21952
|
-
build: "bun run compile && bun run typecheck && bun build src/application.ts --target bun --minify --outdir
|
|
21952
|
+
build: "bun run compile && bun run typecheck && bun build src/application.ts --target bun --minify --outdir output/supacloud",
|
|
21953
21953
|
"env:development": "bun --no-env-file scripts/environment.ts development",
|
|
21954
21954
|
"env:test": "bun --no-env-file scripts/environment.ts test",
|
|
21955
21955
|
"env:staging": "bun --no-env-file scripts/environment.ts staging",
|
|
@@ -21990,7 +21990,7 @@ function baseFiles(name, sdkDependencies) {
|
|
|
21990
21990
|
}),
|
|
21991
21991
|
"bunfig.toml": `env = false
|
|
21992
21992
|
`,
|
|
21993
|
-
".gitignore": ["node_modules/", "dist/", ".env", ".env.*", "!.env.*.example", "!.env.test", ""].join(`
|
|
21993
|
+
".gitignore": ["node_modules/", "dist/", "output/supacloud/", ".env", ".env.*", "!.env.*.example", "!.env.test", ""].join(`
|
|
21994
21994
|
`),
|
|
21995
21995
|
".env.development.example": `APP_ENV=development
|
|
21996
21996
|
PORT=3000
|
|
@@ -22144,10 +22144,15 @@ do not widen them to \`unknown\` just to accept unvalidated data.
|
|
|
22144
22144
|
## Production Boundary
|
|
22145
22145
|
|
|
22146
22146
|
\`bun run build\` emits an application factory, not a server. Import \`createApp\`
|
|
22147
|
-
from \`
|
|
22147
|
+
from \`output/supacloud/application.js\` in your trusted host and supply real dependency,
|
|
22148
22148
|
request-context and command-governance adapters. The memory sandbox is
|
|
22149
22149
|
development/test only.
|
|
22150
22150
|
|
|
22151
|
+
Keep backend bundles outside the frontend output directory. Vite normally cleans
|
|
22152
|
+
\`dist/\` before building; server artifacts placed there can be deleted or included
|
|
22153
|
+
in a public frontend archive. Use disjoint output directories, not a disabled
|
|
22154
|
+
frontend clean step. Compiler source artifacts remain in \`generated/\`.
|
|
22155
|
+
|
|
22151
22156
|
## Inspection And Repair
|
|
22152
22157
|
|
|
22153
22158
|
\`\`\`sh
|
|
@@ -22234,6 +22239,10 @@ sandbox. Bind persistence, authorization and audit to durable adapters before
|
|
|
22234
22239
|
invoking it in production. Do not schedule destructive work without an
|
|
22235
22240
|
idempotency key.
|
|
22236
22241
|
|
|
22242
|
+
\`bun run build\` emits \`output/supacloud/application.js\`, an application factory,
|
|
22243
|
+
not a deployed Worker. Keep it outside any frontend output directory such as
|
|
22244
|
+
\`dist/\`; frontend clean steps and public archives must not own server bundles.
|
|
22245
|
+
|
|
22237
22246
|
## Inspection And Repair
|
|
22238
22247
|
|
|
22239
22248
|
\`\`\`sh
|
|
@@ -22338,6 +22347,10 @@ explicit options; do not install every recipe into this application.
|
|
|
22338
22347
|
## Integration and delivery
|
|
22339
22348
|
|
|
22340
22349
|
createApp requires application-owned trusted identity and durable adapters.
|
|
22350
|
+
\`bun run build\` emits \`output/supacloud/application.js\`, not a deployed server.
|
|
22351
|
+
Keep this backend directory separate from frontend output such as \`dist/\`,
|
|
22352
|
+
which frontend tools may clean and publish. Do not disable frontend cleanup to
|
|
22353
|
+
preserve backend artifacts; use disjoint output directories.
|
|
22341
22354
|
Database migrations and seed data require an explicitly selected development
|
|
22342
22355
|
database and a reviewed application script; the CLI never infers a production target.
|
|
22343
22356
|
Commit generated/ after compilation. At release, check generated drift before
|
|
@@ -25096,7 +25109,7 @@ function appStarterFiles(name) {
|
|
|
25096
25109
|
test: "bun run compile && bun --no-env-file scripts/environment.ts test bun test",
|
|
25097
25110
|
dev: "bun --no-env-file scripts/environment.ts development bun scripts/dev.ts",
|
|
25098
25111
|
"dev:integration": "bun --no-env-file scripts/environment.ts development bun scripts/integration.ts",
|
|
25099
|
-
build: "bun run db:check && bun run compile && bun run typecheck && bun build src/application.ts --target bun --minify --outdir
|
|
25112
|
+
build: "bun run db:check && bun run compile && bun run typecheck && bun build src/application.ts --target bun --minify --outdir output/supacloud",
|
|
25100
25113
|
"env:development": "bun --no-env-file scripts/environment.ts development",
|
|
25101
25114
|
"env:test": "bun --no-env-file scripts/environment.ts test",
|
|
25102
25115
|
"env:staging": "bun --no-env-file scripts/environment.ts staging",
|
|
@@ -25147,6 +25160,7 @@ function appStarterFiles(name) {
|
|
|
25147
25160
|
".gitignore": [
|
|
25148
25161
|
"node_modules/",
|
|
25149
25162
|
"dist/",
|
|
25163
|
+
"output/supacloud/",
|
|
25150
25164
|
"output/database-audit/",
|
|
25151
25165
|
"db/migration-candidates/",
|
|
25152
25166
|
".env",
|
|
@@ -25737,16 +25751,21 @@ must also pin and validate the expected project and API origin.
|
|
|
25737
25751
|
|
|
25738
25752
|
## Production Boundary
|
|
25739
25753
|
|
|
25740
|
-
\`bun run build\` creates
|
|
25754
|
+
\`bun run build\` creates output/supacloud/application.js, an application factory, not a
|
|
25741
25755
|
production server. The compiler is a devDependency and is absent from that
|
|
25742
25756
|
runtime entry. The memory demo is kept in scripts/ and refuses staging/production.
|
|
25743
25757
|
|
|
25744
|
-
Import createApp from
|
|
25758
|
+
Import createApp from output/supacloud/application.js in your trusted host and supply:
|
|
25745
25759
|
|
|
25746
25760
|
- deps.dbClient implementing ReviewStore, with reads/writes bound to a real transaction;
|
|
25747
25761
|
- requestContext from verified identity, never a body-supplied actor;
|
|
25748
25762
|
- commandGovernance authorization, durable idempotency, transaction and audit adapters.
|
|
25749
25763
|
|
|
25764
|
+
Keep backend bundles outside the frontend output directory. Vite normally cleans
|
|
25765
|
+
dist/ before building; server artifacts placed there can be deleted or included
|
|
25766
|
+
in a public frontend archive. Use disjoint output directories, not a disabled
|
|
25767
|
+
frontend clean step. Compiler source artifacts remain in generated/.
|
|
25768
|
+
|
|
25750
25769
|
The starter also includes src/host/review-postgres.ts and src/delivery-host.ts.
|
|
25751
25770
|
The PostgreSQL adapter executes the same compiled approval handler with durable
|
|
25752
25771
|
receipts, audit, current membership/ownership checks on replay and transaction-bound
|
|
@@ -25837,7 +25856,7 @@ startup before any request is served.
|
|
|
25837
25856
|
## Unified User Center
|
|
25838
25857
|
|
|
25839
25858
|
For enterprise unified login, use SupAuth as the external user center. This
|
|
25840
|
-
starter exports createSupAuthApp(identity, adapters) from
|
|
25859
|
+
starter exports createSupAuthApp(identity, adapters) from output/supacloud/application.js.
|
|
25841
25860
|
Supply issuer, audience, clientId, projectId, an explicit HTTPS jwksUrl and resolveAccess
|
|
25842
25861
|
that reads current application-local access. The runtime verifies credential
|
|
25843
25862
|
signatures, configured issuer and audience, allowed asymmetric algorithms/keys
|
|
@@ -26997,125 +27016,152 @@ import { ${className}Fixture } from "./${name}.fixture";
|
|
|
26997
27016
|
const baseUrl = process.env["SUPACLOUD_FEATURE_E2E_URL"];
|
|
26998
27017
|
const authorization = process.env["SUPACLOUD_FEATURE_E2E_AUTHORIZATION"];
|
|
26999
27018
|
const otherAuthorization = process.env["SUPACLOUD_FEATURE_E2E_OTHER_AUTHORIZATION"];
|
|
27000
|
-
const fixtureId = process.env["SUPACLOUD_FEATURE_E2E_ID"]
|
|
27019
|
+
const fixtureId = process.env["SUPACLOUD_FEATURE_E2E_ID"];
|
|
27020
|
+
const raceId = process.env["SUPACLOUD_FEATURE_E2E_RACE_ID"];
|
|
27001
27021
|
const idempotencyKey = process.env["SUPACLOUD_FEATURE_E2E_IDEMPOTENCY_KEY"];
|
|
27002
|
-
const
|
|
27003
|
-
const
|
|
27004
|
-
|
|
27005
|
-
|
|
27006
|
-
|
|
27007
|
-
|
|
27008
|
-
|
|
27009
|
-
|
|
27010
|
-
|
|
27022
|
+
const required = process.env["SUPACLOUD_FEATURE_E2E_REQUIRED"] === "1";
|
|
27023
|
+
const enabled = required || [baseUrl, authorization, otherAuthorization, fixtureId, raceId, idempotencyKey]
|
|
27024
|
+
.some(value => value !== undefined);
|
|
27025
|
+
|
|
27026
|
+
function requireConfig() {
|
|
27027
|
+
if (![baseUrl, authorization, otherAuthorization, fixtureId, raceId, idempotencyKey]
|
|
27028
|
+
.every(value => typeof value === "string" && value.trim().length > 0)) {
|
|
27029
|
+
throw new Error("Feature acceptance requires URL, AUTHORIZATION, OTHER_AUTHORIZATION, ID, RACE_ID and IDEMPOTENCY_KEY (SUPACLOUD_FEATURE_E2E_*).");
|
|
27030
|
+
}
|
|
27031
|
+
if (fixtureId === raceId) throw new Error("Feature acceptance requires two distinct draft fixtures.");
|
|
27032
|
+
const url = new URL(baseUrl!);
|
|
27033
|
+
if (url.username || url.password || url.search || url.hash
|
|
27034
|
+
|| (url.protocol !== "https:" && !(url.protocol === "http:"
|
|
27035
|
+
&& ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname)))) {
|
|
27036
|
+
throw new Error("Feature acceptance requires HTTPS or a loopback HTTP endpoint without embedded credentials.");
|
|
27011
27037
|
}
|
|
27012
|
-
return { baseUrl, authorization
|
|
27038
|
+
return { baseUrl: url, authorization: authorization!, otherAuthorization: otherAuthorization!,
|
|
27039
|
+
fixtureId: fixtureId!, raceId: raceId!, idempotencyKey: idempotencyKey! };
|
|
27013
27040
|
}
|
|
27014
27041
|
|
|
27015
|
-
|
|
27016
|
-
|
|
27017
|
-
|
|
27018
|
-
|
|
27019
|
-
return JSON.parse(text) as unknown;
|
|
27020
|
-
} catch {
|
|
27021
|
-
return text;
|
|
27022
|
-
}
|
|
27042
|
+
function request(config: ReturnType<typeof requireConfig>, path: string, init: RequestInit = {}) {
|
|
27043
|
+
return fetch(new URL(path, config.baseUrl), {
|
|
27044
|
+
...init, redirect: "error", signal: AbortSignal.timeout(10_000),
|
|
27045
|
+
});
|
|
27023
27046
|
}
|
|
27024
27047
|
|
|
27025
|
-
function
|
|
27026
|
-
|
|
27048
|
+
async function object(response: Response): Promise<Record<string, unknown>> {
|
|
27049
|
+
const value: unknown = await response.json();
|
|
27050
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
27051
|
+
throw new Error("Feature acceptance expected a JSON object.");
|
|
27052
|
+
}
|
|
27053
|
+
return value as Record<string, unknown>;
|
|
27027
27054
|
}
|
|
27028
27055
|
|
|
27029
|
-
test.skipIf(!enabled)("${name} authenticated
|
|
27056
|
+
test.skipIf(!enabled)("${name} authenticated approval, replay, recovery and concurrency acceptance", async () => {
|
|
27030
27057
|
const config = requireConfig();
|
|
27031
27058
|
const headers = { authorization: config.authorization, "content-type": "application/json" };
|
|
27032
|
-
const
|
|
27059
|
+
const path = (id: string) => "/${name}/" + encodeURIComponent(id);
|
|
27060
|
+
const list = await request(config, "/${name}", { headers });
|
|
27033
27061
|
expect(list.ok).toBe(true);
|
|
27034
|
-
const
|
|
27035
|
-
|
|
27036
|
-
|
|
27037
|
-
);
|
|
27062
|
+
const listed: unknown = await list.json();
|
|
27063
|
+
expect(Array.isArray(listed)).toBe(true);
|
|
27064
|
+
expect(listed).toContainEqual(expect.objectContaining({ id: config.fixtureId }));
|
|
27065
|
+
const detail = await request(config, path(config.fixtureId), { headers });
|
|
27038
27066
|
expect(detail.ok).toBe(true);
|
|
27039
|
-
const
|
|
27040
|
-
|
|
27067
|
+
const before = await object(detail);
|
|
27068
|
+
expect(before).toMatchObject({ id: config.fixtureId, state: "draft" });
|
|
27069
|
+
expect(Number.isSafeInteger(before["version"])).toBe(true);
|
|
27070
|
+
expect(Number(before["version"])).toBeGreaterThanOrEqual(1);
|
|
27071
|
+
const body = { ...${className}Fixture.approve, expectedVersion: Number(before["version"]) };
|
|
27072
|
+
const approve = (id: string, key: string, input = body) => request(
|
|
27073
|
+
config, path(id) + "/approve",
|
|
27041
27074
|
{
|
|
27042
|
-
method: "POST",
|
|
27043
|
-
|
|
27044
|
-
body: JSON.stringify(body),
|
|
27075
|
+
method: "POST", headers: { ...headers, "idempotency-key": key },
|
|
27076
|
+
body: JSON.stringify(input),
|
|
27045
27077
|
},
|
|
27046
27078
|
);
|
|
27047
|
-
const first = await approve(config.idempotencyKey);
|
|
27048
|
-
|
|
27049
|
-
|
|
27050
|
-
|
|
27051
|
-
|
|
27052
|
-
|
|
27053
|
-
|
|
27054
|
-
|
|
27055
|
-
|
|
27056
|
-
);
|
|
27079
|
+
const first = await approve(config.fixtureId, config.idempotencyKey);
|
|
27080
|
+
expect(first.status).toBe(200);
|
|
27081
|
+
const firstBody = await object(first);
|
|
27082
|
+
expect(firstBody).toMatchObject({ id: config.fixtureId, state: "approved" });
|
|
27083
|
+
expect(Number.isSafeInteger(firstBody["version"])).toBe(true);
|
|
27084
|
+
expect(Number(firstBody["version"])).toBeGreaterThan(Number(before["version"]));
|
|
27085
|
+
|
|
27086
|
+
// Read-only reconciliation is exercised here; this is not a network-loss or worker-crash test.
|
|
27087
|
+
const operationPath = path(config.fixtureId) + "/operations/" + encodeURIComponent(config.idempotencyKey);
|
|
27088
|
+
const recovered = await request(config, operationPath, { headers });
|
|
27057
27089
|
expect(recovered.ok).toBe(true);
|
|
27058
|
-
|
|
27059
|
-
|
|
27060
|
-
|
|
27061
|
-
|
|
27062
|
-
|
|
27063
|
-
);
|
|
27090
|
+
expect(await object(recovered)).toMatchObject({
|
|
27091
|
+
id: config.fixtureId, operationId: config.idempotencyKey,
|
|
27092
|
+
status: "confirmed", state: firstBody["state"], version: firstBody["version"],
|
|
27093
|
+
});
|
|
27094
|
+
const jobStatus = await request(config, operationPath + "/status", { headers });
|
|
27064
27095
|
expect(jobStatus.ok).toBe(true);
|
|
27065
|
-
|
|
27096
|
+
const job = await object(jobStatus);
|
|
27097
|
+
expect(job).toMatchObject({ id: config.fixtureId, operationId: config.idempotencyKey });
|
|
27098
|
+
expect(typeof job["status"]).toBe("string");
|
|
27099
|
+
expect(["queued", "running", "completed"].some(status => status === job["status"])).toBe(true);
|
|
27066
27100
|
|
|
27067
|
-
|
|
27068
|
-
// domain conflict, but it must never create a second successful transition.
|
|
27069
|
-
const replay = await approve(config.idempotencyKey);
|
|
27070
|
-
const replayBody = await json(replay);
|
|
27101
|
+
const replay = await approve(config.fixtureId, config.idempotencyKey);
|
|
27071
27102
|
expect(replay.status).toBe(first.status);
|
|
27072
|
-
expect(
|
|
27103
|
+
expect(await object(replay)).toEqual(firstBody);
|
|
27073
27104
|
|
|
27074
|
-
//
|
|
27075
|
-
// boundary instead of being treated as a new approval.
|
|
27105
|
+
// Conflicting replay and stale version must fail without changing the authoritative row.
|
|
27076
27106
|
const conflicting = await approve(
|
|
27077
|
-
config.idempotencyKey,
|
|
27078
|
-
{ ...${className}Fixture.approve, reason: "conflicting replay" },
|
|
27107
|
+
config.fixtureId, config.idempotencyKey, { ...body, reason: "conflicting replay" },
|
|
27079
27108
|
);
|
|
27080
27109
|
expect([400, 409, 422]).toContain(conflicting.status);
|
|
27081
|
-
|
|
27082
|
-
|
|
27083
|
-
|
|
27110
|
+
const stale = await approve(config.fixtureId, config.idempotencyKey + "-stale");
|
|
27111
|
+
expect([409, 422]).toContain(stale.status);
|
|
27112
|
+
const after = await request(config, path(config.fixtureId), { headers });
|
|
27113
|
+
expect(after.ok).toBe(true);
|
|
27114
|
+
expect(await object(after)).toMatchObject(firstBody);
|
|
27115
|
+
|
|
27116
|
+
// Race on a separate fresh draft; racing the already-approved row proves nothing.
|
|
27117
|
+
const raceDetail = await request(config, path(config.raceId), { headers });
|
|
27118
|
+
expect(raceDetail.ok).toBe(true);
|
|
27119
|
+
const raceBefore = await object(raceDetail);
|
|
27120
|
+
expect(raceBefore).toMatchObject({ id: config.raceId, state: "draft" });
|
|
27121
|
+
expect(Number.isSafeInteger(raceBefore["version"])).toBe(true);
|
|
27122
|
+
expect(Number(raceBefore["version"])).toBeGreaterThanOrEqual(1);
|
|
27123
|
+
const raceBody = { ...body, expectedVersion: Number(raceBefore["version"]) };
|
|
27084
27124
|
const [raceA, raceB] = await Promise.all([
|
|
27085
|
-
approve(config.idempotencyKey + "-race-a"),
|
|
27086
|
-
approve(config.idempotencyKey + "-race-b"),
|
|
27125
|
+
approve(config.raceId, config.idempotencyKey + "-race-a", raceBody),
|
|
27126
|
+
approve(config.raceId, config.idempotencyKey + "-race-b", raceBody),
|
|
27087
27127
|
]);
|
|
27088
|
-
|
|
27089
|
-
|
|
27090
|
-
|
|
27091
|
-
|
|
27092
|
-
|
|
27128
|
+
const statuses = [raceA.status, raceB.status];
|
|
27129
|
+
expect(statuses.filter(status => status === 200)).toHaveLength(1);
|
|
27130
|
+
expect(statuses.filter(status => status === 409 || status === 422)).toHaveLength(1);
|
|
27131
|
+
const winner = await object(raceA.status === 200 ? raceA : raceB);
|
|
27132
|
+
expect(winner).toMatchObject({ id: config.raceId, state: "approved" });
|
|
27133
|
+
expect(Number.isSafeInteger(winner["version"])).toBe(true);
|
|
27134
|
+
expect(Number(winner["version"])).toBeGreaterThan(Number(raceBefore["version"]));
|
|
27135
|
+
const raceAfter = await request(config, path(config.raceId), { headers });
|
|
27136
|
+
expect(raceAfter.ok).toBe(true);
|
|
27137
|
+
expect(await object(raceAfter)).toMatchObject(winner);
|
|
27138
|
+
}, { timeout: 120_000 });
|
|
27139
|
+
|
|
27140
|
+
test.skipIf(!enabled)("${name} rejects cross-tenant reads, writes and receipt disclosure", async () => {
|
|
27093
27141
|
const config = requireConfig();
|
|
27094
|
-
if (!otherAuthorization) throw new Error("Missing SUPACLOUD_FEATURE_E2E_OTHER_AUTHORIZATION");
|
|
27095
27142
|
const headers = {
|
|
27096
|
-
authorization: otherAuthorization,
|
|
27143
|
+
authorization: config.otherAuthorization,
|
|
27097
27144
|
"content-type": "application/json",
|
|
27098
27145
|
};
|
|
27099
|
-
const list = await
|
|
27146
|
+
const list = await request(config, "/${name}", { headers });
|
|
27100
27147
|
expect(list.ok).toBe(true);
|
|
27101
|
-
|
|
27102
|
-
|
|
27103
|
-
|
|
27104
|
-
|
|
27105
|
-
|
|
27106
|
-
);
|
|
27107
|
-
|
|
27108
|
-
|
|
27109
|
-
|
|
27110
|
-
|
|
27111
|
-
|
|
27112
|
-
|
|
27113
|
-
|
|
27114
|
-
|
|
27115
|
-
|
|
27116
|
-
);
|
|
27148
|
+
const listed: unknown = await list.json();
|
|
27149
|
+
expect(Array.isArray(listed)).toBe(true);
|
|
27150
|
+
expect(listed).not.toContainEqual(expect.objectContaining({ id: config.fixtureId }));
|
|
27151
|
+
expect(listed).not.toContainEqual(expect.objectContaining({ id: config.raceId }));
|
|
27152
|
+
const path = "/${name}/" + encodeURIComponent(config.fixtureId);
|
|
27153
|
+
const operationPath = path + "/operations/" + encodeURIComponent(config.idempotencyKey);
|
|
27154
|
+
for (const target of [path, operationPath, operationPath + "/status"]) {
|
|
27155
|
+
const response = await request(config, target, { headers });
|
|
27156
|
+
expect([403, 404]).toContain(response.status);
|
|
27157
|
+
}
|
|
27158
|
+
// Reuse the successful key: authorization must run before a cached success is disclosed.
|
|
27159
|
+
const approval = await request(config, path + "/approve", {
|
|
27160
|
+
method: "POST", headers: { ...headers, "idempotency-key": config.idempotencyKey },
|
|
27161
|
+
body: JSON.stringify(${className}Fixture.approve),
|
|
27162
|
+
});
|
|
27117
27163
|
expect([403, 404]).toContain(approval.status);
|
|
27118
|
-
});
|
|
27164
|
+
}, { timeout: 120_000 });
|
|
27119
27165
|
`,
|
|
27120
27166
|
[`${name}.deployment.md`]: `# ${className} feature delivery checklist
|
|
27121
27167
|
|
|
@@ -27143,10 +27189,16 @@ Add request fields in the contract, not a second handwritten Writer interface.
|
|
|
27143
27189
|
Typecheck application adapters after changing a field type; the derived Port
|
|
27144
27190
|
must expose incompatible assumptions in business code.
|
|
27145
27191
|
|
|
27146
|
-
For
|
|
27147
|
-
|
|
27148
|
-
|
|
27149
|
-
|
|
27192
|
+
For release acceptance, set \`SUPACLOUD_FEATURE_E2E_REQUIRED=1\` and supply URL,
|
|
27193
|
+
AUTHORIZATION, OTHER_AUTHORIZATION, ID, RACE_ID and IDEMPOTENCY_KEY under the
|
|
27194
|
+
\`SUPACLOUD_FEATURE_E2E_\` prefix. Use two distinct fresh draft fixtures and a fresh
|
|
27195
|
+
key on every run. Missing or partial configuration fails; only an entirely
|
|
27196
|
+
unconfigured developer run skips. Use HTTPS, or loopback HTTP for local tests.
|
|
27197
|
+
The first approval must succeed, its receipt must match, replay must preserve the
|
|
27198
|
+
result, and exactly one of two concurrent writes on the second draft must succeed.
|
|
27199
|
+
Cross-tenant detail, receipt, job status and replay must be denied. This HTTP test
|
|
27200
|
+
does not prove audit/outbox counts, worker completion, crashes, browser interaction,
|
|
27201
|
+
revoked membership or deployment; verify those separately against durable sources.
|
|
27150
27202
|
|
|
27151
27203
|
The named RPC adapter must invoke the continuation in the same database transaction
|
|
27152
27204
|
used by the request-scoped Writer, or own the complete command instead; never execute
|
|
@@ -33086,7 +33138,7 @@ async function runProjectDeployment(http, args) {
|
|
|
33086
33138
|
// package.json
|
|
33087
33139
|
var package_default = {
|
|
33088
33140
|
name: "@supacloud/cli",
|
|
33089
|
-
version: "0.108.
|
|
33141
|
+
version: "0.108.2",
|
|
33090
33142
|
description: "Project-scoped CLI for SupaCloud users",
|
|
33091
33143
|
type: "module",
|
|
33092
33144
|
main: "./dist/index.js",
|
|
@@ -33144,7 +33196,7 @@ var package_default = {
|
|
|
33144
33196
|
typebox: "1.3.37"
|
|
33145
33197
|
},
|
|
33146
33198
|
overrides: {
|
|
33147
|
-
"@supacloud/contracts": "0.
|
|
33199
|
+
"@supacloud/contracts": "0.15.0",
|
|
33148
33200
|
"@supacloud/delivery": "0.21.0",
|
|
33149
33201
|
"@graphql-tools/utils": "^12.0.4"
|
|
33150
33202
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/cli",
|
|
3
|
-
"version": "0.108.
|
|
3
|
+
"version": "0.108.2",
|
|
4
4
|
"description": "Project-scoped CLI for SupaCloud users",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"typebox": "1.3.37"
|
|
59
59
|
},
|
|
60
60
|
"overrides": {
|
|
61
|
-
"@supacloud/contracts": "0.
|
|
61
|
+
"@supacloud/contracts": "0.15.0",
|
|
62
62
|
"@supacloud/delivery": "0.21.0",
|
|
63
63
|
"@graphql-tools/utils": "^12.0.4"
|
|
64
64
|
},
|