@fungi.computer/puffball 0.0.0-stage → 0.0.1
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/LICENSE +21 -0
- package/README.md +443 -2
- package/dist/app-api.d.ts +7 -0
- package/dist/app-api.js +61 -0
- package/dist/app-backend-binding-vocabulary.d.ts +299 -0
- package/dist/app-backend-binding-vocabulary.js +257 -0
- package/dist/app-backend-bindings.d.ts +21 -0
- package/dist/app-backend-bindings.js +47 -0
- package/dist/app-declaration.d.ts +46 -0
- package/dist/app-declaration.js +115 -0
- package/dist/app-event-facts.d.ts +40 -0
- package/dist/app-event-facts.js +28 -0
- package/dist/app-method-context.d.ts +109 -0
- package/dist/app-method-context.js +9 -0
- package/dist/app-method-runtime.d.ts +86 -0
- package/dist/app-method-runtime.js +122 -0
- package/dist/app-origin-paths.d.ts +6 -0
- package/dist/app-origin-paths.js +6 -0
- package/dist/app-vocabulary.d.ts +189 -0
- package/dist/app-vocabulary.js +103 -0
- package/dist/app.d.ts +379 -0
- package/dist/app.js +71 -0
- package/dist/artifact-dispositions.d.ts +115 -0
- package/dist/artifact-dispositions.js +234 -0
- package/dist/assets.d.ts +54 -0
- package/dist/assets.js +89 -0
- package/dist/backend.d.ts +41 -0
- package/dist/backend.js +29 -0
- package/dist/background-replay.d.ts +115 -0
- package/dist/background-replay.js +175 -0
- package/dist/buyer-references.d.ts +69 -0
- package/dist/buyer-references.js +154 -0
- package/dist/cloudflare-assets.d.ts +48 -0
- package/dist/cloudflare-assets.js +105 -0
- package/dist/cloudflare.d.ts +254 -0
- package/dist/cloudflare.js +1619 -0
- package/dist/effect.d.ts +73 -0
- package/dist/effect.js +325 -0
- package/dist/git-representation.d.ts +13 -0
- package/dist/git-representation.js +16 -0
- package/dist/inbox.d.ts +132 -0
- package/dist/inbox.js +81 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +85 -0
- package/dist/internal/app-description.d.ts +14 -0
- package/dist/internal/app-description.js +98 -0
- package/dist/internal/app-release-artifact-reader.d.ts +26 -0
- package/dist/internal/app-release-artifact-reader.js +1 -0
- package/dist/internal/app-release-contract.d.ts +540 -0
- package/dist/internal/app-release-contract.js +169 -0
- package/dist/internal/app-release-schema.d.ts +1676 -0
- package/dist/internal/app-release-schema.js +386 -0
- package/dist/internal/app-release-store.d.ts +47 -0
- package/dist/internal/app-release-store.js +1 -0
- package/dist/internal/background-sync.d.ts +7 -0
- package/dist/internal/background-sync.js +10 -0
- package/dist/internal/deterministic-memory-app-release-store.d.ts +14 -0
- package/dist/internal/deterministic-memory-app-release-store.js +319 -0
- package/dist/internal/failure.d.ts +2 -0
- package/dist/internal/failure.js +9 -0
- package/dist/internal/immutable-release-material.d.ts +3 -0
- package/dist/internal/immutable-release-material.js +13 -0
- package/dist/internal/promise-app-release-store-adapter.d.ts +9 -0
- package/dist/internal/promise-app-release-store-adapter.js +65 -0
- package/dist/internal/promise-artifact-reader-adapter.d.ts +9 -0
- package/dist/internal/promise-artifact-reader-adapter.js +18 -0
- package/dist/internal/public-route-sql.d.ts +10 -0
- package/dist/internal/public-route-sql.js +239 -0
- package/dist/internal/public-website-authority.d.ts +338 -0
- package/dist/internal/public-website-authority.js +50 -0
- package/dist/internal/public-website-delivery.d.ts +59 -0
- package/dist/internal/public-website-delivery.js +232 -0
- package/dist/internal/public-website-fetch.d.ts +8 -0
- package/dist/internal/public-website-fetch.js +54 -0
- package/dist/internal/release-transition-material.d.ts +3 -0
- package/dist/internal/release-transition-material.js +11 -0
- package/dist/internal/verified-artifact-snapshot.d.ts +41 -0
- package/dist/internal/verified-artifact-snapshot.js +91 -0
- package/dist/public-route-moderation.d.ts +83 -0
- package/dist/public-route-moderation.js +236 -0
- package/dist/public-route-policy.d.ts +51 -0
- package/dist/public-route-policy.js +88 -0
- package/dist/public-routes.d.ts +34 -0
- package/dist/public-routes.js +136 -0
- package/dist/publisher.d.ts +61 -0
- package/dist/publisher.js +16 -0
- package/dist/safety-takedown.d.ts +56 -0
- package/dist/safety-takedown.js +146 -0
- package/dist/seller-closure-notice.d.ts +14 -0
- package/dist/seller-closure-notice.js +14 -0
- package/dist/seller-closure.d.ts +15 -0
- package/dist/seller-closure.js +31 -0
- package/dist/testing.d.ts +8 -0
- package/dist/testing.js +47 -0
- package/docs/api/README.md +26 -0
- package/docs/api/app-backend-binding-vocabulary.md +276 -0
- package/docs/api/app-backend-bindings.md +141 -0
- package/docs/api/app-event-facts.md +110 -0
- package/docs/api/app-method-context.md +166 -0
- package/docs/api/app-method-runtime.md +330 -0
- package/docs/api/backend.md +108 -0
- package/docs/api/background-replay.md +516 -0
- package/docs/api/cloudflare.md +1044 -0
- package/docs/api/effect.md +2429 -0
- package/docs/api/git-representation.md +56 -0
- package/docs/api/inbox.md +289 -0
- package/docs/api/index.md +2868 -0
- package/docs/api/publisher.md +147 -0
- package/docs/api/testing.md +31 -0
- package/package.json +158 -3
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
import { isReservedPublicRoute, publicRouteKey, publicRoutePolicy, publicRouteConfigurationSchema, } from "../public-route-policy.js";
|
|
3
|
+
import { registerPromisePublicWebsiteAuthorityReader } from "./public-website-authority.js";
|
|
4
|
+
import { sameReleaseTransitionMaterial } from "./release-transition-material.js";
|
|
5
|
+
function copyApp(app) {
|
|
6
|
+
return { appId: app.appId };
|
|
7
|
+
}
|
|
8
|
+
function copyRelease(release) {
|
|
9
|
+
return {
|
|
10
|
+
releaseId: release.releaseId,
|
|
11
|
+
appId: release.appId,
|
|
12
|
+
artifact: {
|
|
13
|
+
artifactRef: release.artifact.artifactRef,
|
|
14
|
+
artifactDigest: release.artifact.artifactDigest,
|
|
15
|
+
provenance: {
|
|
16
|
+
sourceRevision: release.artifact.provenance.sourceRevision,
|
|
17
|
+
sourceTreeDigest: release.artifact.provenance.sourceTreeDigest,
|
|
18
|
+
buildRecipeDigest: release.artifact.provenance.buildRecipeDigest,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function copyPublicSurface(surface) {
|
|
24
|
+
return {
|
|
25
|
+
publicSurfaceId: surface.publicSurfaceId,
|
|
26
|
+
appId: surface.appId,
|
|
27
|
+
stableRoute: surface.stableRoute,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function copyTransition(transition) {
|
|
31
|
+
return { ...transition };
|
|
32
|
+
}
|
|
33
|
+
function copyReceipt(receipt) {
|
|
34
|
+
return {
|
|
35
|
+
transition: copyTransition(receipt.transition),
|
|
36
|
+
transitionId: receipt.transitionId,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function copyVerifiedAppDescription(description) {
|
|
40
|
+
return structuredClone(description);
|
|
41
|
+
}
|
|
42
|
+
function transitionKey(transition) {
|
|
43
|
+
return `${transition.kind}:${transition.idempotencyKey}`;
|
|
44
|
+
}
|
|
45
|
+
function transitionLookup(stored, request) {
|
|
46
|
+
if (stored === undefined)
|
|
47
|
+
return { kind: "absent" };
|
|
48
|
+
if (!sameReleaseTransitionMaterial(request, stored.request))
|
|
49
|
+
return { kind: "conflict" };
|
|
50
|
+
return { kind: "found", receipt: copyReceipt(stored.receipt) };
|
|
51
|
+
}
|
|
52
|
+
function successful(value) {
|
|
53
|
+
return { kind: "success", value };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Compose an in-process deterministic store for the narrow App/Release port.
|
|
57
|
+
*
|
|
58
|
+
* Store methods mutate only the private collections below. Decisions remain in
|
|
59
|
+
* pure helpers, and every returned record is copied, so callers cannot mutate
|
|
60
|
+
* retained state after an atomic create-or-observe operation.
|
|
61
|
+
*/
|
|
62
|
+
export function createDeterministicMemoryAppReleaseStore(options = { routes: { platformRoutes: [], structuralRoutes: [] } }) {
|
|
63
|
+
const routes = Schema.decodeUnknownSync(publicRouteConfigurationSchema, {
|
|
64
|
+
onExcessProperty: "error",
|
|
65
|
+
})(options.routes);
|
|
66
|
+
const apps = new Map();
|
|
67
|
+
const releases = new Map();
|
|
68
|
+
const publicSurfaces = new Map();
|
|
69
|
+
const claims = new Map();
|
|
70
|
+
const eligibleSurface = (surface) => {
|
|
71
|
+
const key = publicRouteKey(surface.stableRoute);
|
|
72
|
+
const held = claims.get(key);
|
|
73
|
+
if (held && held.publicSurfaceId !== surface.publicSurfaceId)
|
|
74
|
+
return false;
|
|
75
|
+
if (surface.publisher.kind === "platform")
|
|
76
|
+
return routes.platformRoutes.some((route) => route.appId === surface.appId &&
|
|
77
|
+
route.stableRoute === surface.stableRoute);
|
|
78
|
+
const publisher = options.publishers?.find((value) => value.appId === surface.appId);
|
|
79
|
+
if (publisher?.teamId !== surface.publisher.teamId ||
|
|
80
|
+
publisher.source.provider !== "artifacts" ||
|
|
81
|
+
publisher.source.repository !== surface.appId ||
|
|
82
|
+
!publisher.templateSeed ||
|
|
83
|
+
publisher.templateSeed.appId === surface.appId)
|
|
84
|
+
return false;
|
|
85
|
+
if (held)
|
|
86
|
+
return true;
|
|
87
|
+
if ([...claims.values()].filter((value) => value.publisher.kind === "customer" &&
|
|
88
|
+
value.publisher.teamId === surface.publisher.teamId).length >= publicRoutePolicy.maxPublicRoutesPerTeam)
|
|
89
|
+
return false;
|
|
90
|
+
return (!isReservedPublicRoute(surface.stableRoute, routes.structuralRoutes) &&
|
|
91
|
+
!routes.platformRoutes.some((value) => publicRouteKey(value.stableRoute) === key));
|
|
92
|
+
};
|
|
93
|
+
const verifications = new Map();
|
|
94
|
+
const revokedReleases = new Set();
|
|
95
|
+
const selectedReleases = new Map();
|
|
96
|
+
const surfaceReleaseHistory = new Map();
|
|
97
|
+
const transitions = new Map();
|
|
98
|
+
const transitionIds = new Set();
|
|
99
|
+
const readMemoryReleaseLifecycle = (releaseId) => {
|
|
100
|
+
const release = releases.get(releaseId);
|
|
101
|
+
const verification = verifications.get(releaseId);
|
|
102
|
+
return release === undefined
|
|
103
|
+
? { kind: "absent" }
|
|
104
|
+
: {
|
|
105
|
+
kind: "found",
|
|
106
|
+
release: copyRelease(release),
|
|
107
|
+
verification: verification === undefined
|
|
108
|
+
? { kind: "unverified" }
|
|
109
|
+
: {
|
|
110
|
+
kind: "verified",
|
|
111
|
+
digest: verification.digest,
|
|
112
|
+
mediaType: verification.mediaType,
|
|
113
|
+
description: copyVerifiedAppDescription(verification.description),
|
|
114
|
+
},
|
|
115
|
+
revocation: revokedReleases.has(releaseId)
|
|
116
|
+
? { kind: "revoked" }
|
|
117
|
+
: { kind: "active" },
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
const readPublicWebsiteAuthority = (request) => {
|
|
121
|
+
const matches = [...claims.values()]
|
|
122
|
+
.filter((surface) => surface.stableRoute === request.stableRoute)
|
|
123
|
+
.slice(0, 2);
|
|
124
|
+
if (matches.length === 0)
|
|
125
|
+
return Promise.resolve(successful({ kind: "absent" }));
|
|
126
|
+
if (matches.length === 2)
|
|
127
|
+
return Promise.resolve(successful({ kind: "ambiguous" }));
|
|
128
|
+
const surface = matches[0];
|
|
129
|
+
if (surface === undefined) {
|
|
130
|
+
return Promise.resolve({
|
|
131
|
+
kind: "failure",
|
|
132
|
+
failure: { kind: "unavailable" },
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const app = apps.get(surface.appId);
|
|
136
|
+
if (app === undefined)
|
|
137
|
+
return Promise.resolve({
|
|
138
|
+
kind: "failure",
|
|
139
|
+
failure: { kind: "unavailable" },
|
|
140
|
+
});
|
|
141
|
+
const currentReleaseId = selectedReleases.get(surface.publicSurfaceId);
|
|
142
|
+
const releaseId = request.expectedReleaseId ?? currentReleaseId;
|
|
143
|
+
const sequence = releaseId === undefined
|
|
144
|
+
? undefined
|
|
145
|
+
: surfaceReleaseHistory.get(surface.publicSurfaceId)?.get(releaseId);
|
|
146
|
+
if (currentReleaseId === undefined ||
|
|
147
|
+
releaseId === undefined ||
|
|
148
|
+
sequence === undefined) {
|
|
149
|
+
return Promise.resolve(successful({
|
|
150
|
+
kind: "unselected",
|
|
151
|
+
app: copyApp(app),
|
|
152
|
+
surface: copyPublicSurface(surface),
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
const lifecycle = readMemoryReleaseLifecycle(releaseId);
|
|
156
|
+
return Promise.resolve(successful({
|
|
157
|
+
kind: "selected",
|
|
158
|
+
app: copyApp(app),
|
|
159
|
+
surface: copyPublicSurface(surface),
|
|
160
|
+
releaseId,
|
|
161
|
+
currentReleaseId,
|
|
162
|
+
sequence,
|
|
163
|
+
release: lifecycle,
|
|
164
|
+
}));
|
|
165
|
+
};
|
|
166
|
+
const store = {
|
|
167
|
+
readApp(appId) {
|
|
168
|
+
const app = apps.get(appId);
|
|
169
|
+
return Promise.resolve(successful(app === undefined
|
|
170
|
+
? { kind: "absent" }
|
|
171
|
+
: { kind: "found", app: copyApp(app) }));
|
|
172
|
+
},
|
|
173
|
+
createAppIfAbsent(app) {
|
|
174
|
+
const existing = apps.get(app.appId);
|
|
175
|
+
if (existing !== undefined) {
|
|
176
|
+
return Promise.resolve(successful({ kind: "existing", app: copyApp(existing) }));
|
|
177
|
+
}
|
|
178
|
+
const created = copyApp(app);
|
|
179
|
+
apps.set(created.appId, created);
|
|
180
|
+
return Promise.resolve(successful({ kind: "created", app: copyApp(created) }));
|
|
181
|
+
},
|
|
182
|
+
readRelease(releaseId) {
|
|
183
|
+
const release = releases.get(releaseId);
|
|
184
|
+
return Promise.resolve(successful(release === undefined
|
|
185
|
+
? { kind: "absent" }
|
|
186
|
+
: { kind: "found", release: copyRelease(release) }));
|
|
187
|
+
},
|
|
188
|
+
createReleaseIfAbsent(release) {
|
|
189
|
+
const existing = releases.get(release.releaseId);
|
|
190
|
+
if (existing !== undefined) {
|
|
191
|
+
return Promise.resolve(successful({ kind: "existing", release: copyRelease(existing) }));
|
|
192
|
+
}
|
|
193
|
+
const created = copyRelease(release);
|
|
194
|
+
releases.set(created.releaseId, created);
|
|
195
|
+
return Promise.resolve(successful({ kind: "created", release: copyRelease(created) }));
|
|
196
|
+
},
|
|
197
|
+
readPublicSurface(publicSurfaceId) {
|
|
198
|
+
const surface = publicSurfaces.get(publicSurfaceId);
|
|
199
|
+
return Promise.resolve(successful(surface === undefined
|
|
200
|
+
? { kind: "absent" }
|
|
201
|
+
: { kind: "found", surface: copyPublicSurface(surface) }));
|
|
202
|
+
},
|
|
203
|
+
createPublicSurfaceIfAbsent(surface) {
|
|
204
|
+
const existing = [...publicSurfaces.values()].find((value) => value.publisher.teamId === surface.publisher.teamId &&
|
|
205
|
+
value.publisher.requestKey === surface.publisher.requestKey);
|
|
206
|
+
if (existing !== undefined) {
|
|
207
|
+
if (existing.appId !== surface.appId ||
|
|
208
|
+
existing.stableRoute !== surface.stableRoute ||
|
|
209
|
+
existing.publisher.kind !== surface.publisher.kind ||
|
|
210
|
+
existing.publisher.userId !== surface.publisher.userId)
|
|
211
|
+
return Promise.resolve(successful({ kind: "conflict" }));
|
|
212
|
+
return Promise.resolve(successful({
|
|
213
|
+
kind: "existing",
|
|
214
|
+
surface: copyPublicSurface(existing),
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
if (publicSurfaces.has(surface.publicSurfaceId) ||
|
|
218
|
+
!eligibleSurface(surface) ||
|
|
219
|
+
[...publicSurfaces.values()].filter((value) => value.appId === surface.appId &&
|
|
220
|
+
!surfaceReleaseHistory.has(value.publicSurfaceId)).length >= publicRoutePolicy.maxUnusedSurfacesPerApp)
|
|
221
|
+
return Promise.resolve(successful({ kind: "conflict" }));
|
|
222
|
+
const created = {
|
|
223
|
+
...copyPublicSurface(surface),
|
|
224
|
+
publisher: { ...surface.publisher },
|
|
225
|
+
};
|
|
226
|
+
publicSurfaces.set(created.publicSurfaceId, created);
|
|
227
|
+
return Promise.resolve(successful({ kind: "created", surface: copyPublicSurface(created) }));
|
|
228
|
+
},
|
|
229
|
+
readReleaseLifecycle(releaseId) {
|
|
230
|
+
return Promise.resolve(successful(readMemoryReleaseLifecycle(releaseId)));
|
|
231
|
+
},
|
|
232
|
+
recordReleaseVerification(input) {
|
|
233
|
+
if (!releases.has(input.releaseId)) {
|
|
234
|
+
return Promise.resolve({
|
|
235
|
+
kind: "failure",
|
|
236
|
+
failure: { kind: "unavailable" },
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
const existing = verifications.get(input.releaseId);
|
|
240
|
+
if (existing === undefined) {
|
|
241
|
+
verifications.set(input.releaseId, {
|
|
242
|
+
digest: input.digest,
|
|
243
|
+
mediaType: input.mediaType,
|
|
244
|
+
description: copyVerifiedAppDescription(input.description),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
else if (existing.digest !== input.digest ||
|
|
248
|
+
JSON.stringify(existing.description) !==
|
|
249
|
+
JSON.stringify(input.description)) {
|
|
250
|
+
return Promise.resolve({
|
|
251
|
+
kind: "failure",
|
|
252
|
+
failure: { kind: "unavailable" },
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
return Promise.resolve(successful({ kind: "recorded" }));
|
|
256
|
+
},
|
|
257
|
+
readTransition(request) {
|
|
258
|
+
const stored = transitions.get(transitionKey(request));
|
|
259
|
+
return Promise.resolve(successful(transitionLookup(stored, request)));
|
|
260
|
+
},
|
|
261
|
+
persistTransition(request, receipt) {
|
|
262
|
+
const key = transitionKey(request);
|
|
263
|
+
const stored = transitions.get(key);
|
|
264
|
+
if (stored !== undefined) {
|
|
265
|
+
const persistence = sameReleaseTransitionMaterial(request, stored.request)
|
|
266
|
+
? { kind: "existing", receipt: copyReceipt(stored.receipt) }
|
|
267
|
+
: { kind: "conflict" };
|
|
268
|
+
return Promise.resolve(successful(persistence));
|
|
269
|
+
}
|
|
270
|
+
if (request.kind !== "revocation" &&
|
|
271
|
+
revokedReleases.has(request.releaseId)) {
|
|
272
|
+
return Promise.resolve(successful({ kind: "lifecycle-conflict" }));
|
|
273
|
+
}
|
|
274
|
+
if (transitionIds.has(receipt.transitionId)) {
|
|
275
|
+
return Promise.resolve(successful({ kind: "identity-collision" }));
|
|
276
|
+
}
|
|
277
|
+
if (request.kind !== "revocation") {
|
|
278
|
+
const surface = publicSurfaces.get(request.publicSurfaceId);
|
|
279
|
+
const release = releases.get(request.releaseId);
|
|
280
|
+
const verified = verifications.get(request.releaseId);
|
|
281
|
+
if (!surface ||
|
|
282
|
+
surface.appId !== release?.appId ||
|
|
283
|
+
verified?.digest !== release.artifact.artifactDigest ||
|
|
284
|
+
!eligibleSurface(surface) ||
|
|
285
|
+
(request.kind === "rollback" &&
|
|
286
|
+
!surfaceReleaseHistory
|
|
287
|
+
.get(request.publicSurfaceId)
|
|
288
|
+
?.has(request.releaseId)))
|
|
289
|
+
return Promise.resolve(successful({ kind: "lifecycle-conflict" }));
|
|
290
|
+
claims.set(publicRouteKey(surface.stableRoute), surface);
|
|
291
|
+
}
|
|
292
|
+
const savedRequest = copyTransition(request);
|
|
293
|
+
const savedReceipt = copyReceipt(receipt);
|
|
294
|
+
transitions.set(key, { request: savedRequest, receipt: savedReceipt });
|
|
295
|
+
transitionIds.add(savedReceipt.transitionId);
|
|
296
|
+
if (request.kind === "revocation") {
|
|
297
|
+
revokedReleases.add(request.releaseId);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
selectedReleases.set(request.publicSurfaceId, request.releaseId);
|
|
301
|
+
const history = new Map(surfaceReleaseHistory.get(request.publicSurfaceId));
|
|
302
|
+
history.set(request.releaseId, transitions.size);
|
|
303
|
+
surfaceReleaseHistory.set(request.publicSurfaceId, history);
|
|
304
|
+
}
|
|
305
|
+
return Promise.resolve(successful({ kind: "created", receipt: copyReceipt(savedReceipt) }));
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
registerPromisePublicWebsiteAuthorityReader(store, async (requests) => {
|
|
309
|
+
const results = await Promise.all(requests.map(readPublicWebsiteAuthority));
|
|
310
|
+
const values = [];
|
|
311
|
+
for (const result of results) {
|
|
312
|
+
if (result.kind === "failure")
|
|
313
|
+
return result;
|
|
314
|
+
values.push(result.value);
|
|
315
|
+
}
|
|
316
|
+
return successful(values);
|
|
317
|
+
});
|
|
318
|
+
return store;
|
|
319
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as Redacted from "effect/Redacted";
|
|
2
|
+
/** Report a failed operation without exposing artifact or request contents. */
|
|
3
|
+
export function reportFailure(operation, cause) {
|
|
4
|
+
console.error(JSON.stringify({
|
|
5
|
+
event: "app_release_operation_failed",
|
|
6
|
+
operation,
|
|
7
|
+
causeKind: cause instanceof Error ? "error" : "non_error",
|
|
8
|
+
}), Redacted.make(cause));
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Compare every write-once fact carried by an immutable Release. */
|
|
2
|
+
export function sameImmutableReleaseMaterial(left, right) {
|
|
3
|
+
return (left.releaseId === right.releaseId &&
|
|
4
|
+
left.appId === right.appId &&
|
|
5
|
+
left.artifact.artifactRef === right.artifact.artifactRef &&
|
|
6
|
+
left.artifact.artifactDigest === right.artifact.artifactDigest &&
|
|
7
|
+
left.artifact.provenance.sourceRevision ===
|
|
8
|
+
right.artifact.provenance.sourceRevision &&
|
|
9
|
+
left.artifact.provenance.sourceTreeDigest ===
|
|
10
|
+
right.artifact.provenance.sourceTreeDigest &&
|
|
11
|
+
left.artifact.provenance.buildRecipeDigest ===
|
|
12
|
+
right.artifact.provenance.buildRecipeDigest);
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PromiseAppReleaseStore } from "./app-release-contract.js";
|
|
2
|
+
import type { AppReleaseStore } from "./app-release-store.js";
|
|
3
|
+
/**
|
|
4
|
+
* Adapt the Promise store projection at the one outer edge.
|
|
5
|
+
*
|
|
6
|
+
* Rejected Promise work is deliberately collapsed into the closed unavailable
|
|
7
|
+
* store outcome; infrastructure detail never crosses into catalog policy.
|
|
8
|
+
*/
|
|
9
|
+
export declare function adaptPromiseAppReleaseStore(projection: PromiseAppReleaseStore): AppReleaseStore;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Result from "effect/Result";
|
|
3
|
+
import { decodePublicWebsiteAuthorityStoreResult, getPromisePublicWebsiteAuthorityReader, registerEffectPublicWebsiteAuthorityReader, } from "./public-website-authority.js";
|
|
4
|
+
import { decodeAppCreateStoreResult, decodeAppReadStoreResult, decodePublicSurfaceCreateStoreResult, decodePublicSurfaceReadStoreResult, decodeReleaseLifecycleStoreResult, decodeReleaseVerificationRecordStoreResult, decodeReleaseCreateStoreResult, decodeReleaseReadStoreResult, decodeTransitionLookupStoreResult, decodeTransitionPersistenceStoreResult, } from "./app-release-schema.js";
|
|
5
|
+
/**
|
|
6
|
+
* Adapt the Promise store projection at the one outer edge.
|
|
7
|
+
*
|
|
8
|
+
* Rejected Promise work is deliberately collapsed into the closed unavailable
|
|
9
|
+
* store outcome; infrastructure detail never crosses into catalog policy.
|
|
10
|
+
*/
|
|
11
|
+
export function adaptPromiseAppReleaseStore(projection) {
|
|
12
|
+
const store = {
|
|
13
|
+
readApp: (appId) => fromPromise(() => projection.readApp(appId), decodeAppReadStoreResult),
|
|
14
|
+
createAppIfAbsent: (app) => fromPromise(() => projection.createAppIfAbsent(app), decodeAppCreateStoreResult),
|
|
15
|
+
readRelease: (releaseId) => fromPromise(() => projection.readRelease(releaseId), decodeReleaseReadStoreResult),
|
|
16
|
+
createReleaseIfAbsent: (release) => fromPromise(() => projection.createReleaseIfAbsent(release), decodeReleaseCreateStoreResult),
|
|
17
|
+
readPublicSurface: (publicSurfaceId) => fromPromise(() => projection.readPublicSurface(publicSurfaceId), decodePublicSurfaceReadStoreResult),
|
|
18
|
+
createPublicSurfaceIfAbsent: (surface) => fromPromise(() => projection.createPublicSurfaceIfAbsent(surface), decodePublicSurfaceCreateStoreResult),
|
|
19
|
+
readReleaseLifecycle: (releaseId) => fromOptionalPromise(projection.readReleaseLifecycle === undefined
|
|
20
|
+
? undefined
|
|
21
|
+
: () => projection.readReleaseLifecycle?.(releaseId), decodeReleaseLifecycleStoreResult),
|
|
22
|
+
recordReleaseVerification: (input) => fromOptionalPromise(projection.recordReleaseVerification === undefined
|
|
23
|
+
? undefined
|
|
24
|
+
: () => projection.recordReleaseVerification?.(input), decodeReleaseVerificationRecordStoreResult),
|
|
25
|
+
readTransition: (request) => fromOptionalPromise(projection.readTransition === undefined
|
|
26
|
+
? undefined
|
|
27
|
+
: () => projection.readTransition?.(request), decodeTransitionLookupStoreResult),
|
|
28
|
+
persistTransition: (request, receipt) => fromOptionalPromise(projection.persistTransition === undefined
|
|
29
|
+
? undefined
|
|
30
|
+
: () => projection.persistTransition?.(request, receipt), decodeTransitionPersistenceStoreResult),
|
|
31
|
+
};
|
|
32
|
+
const websiteReader = getPromisePublicWebsiteAuthorityReader(projection);
|
|
33
|
+
if (websiteReader !== undefined)
|
|
34
|
+
registerEffectPublicWebsiteAuthorityReader(store, (request) => fromPromise(() => websiteReader(request), decodePublicWebsiteAuthorityStoreResult));
|
|
35
|
+
return store;
|
|
36
|
+
}
|
|
37
|
+
function fromOptionalPromise(operation, decodeResult) {
|
|
38
|
+
return operation === undefined
|
|
39
|
+
? Effect.fail(unavailable())
|
|
40
|
+
: fromPromise(() => operation() ??
|
|
41
|
+
Promise.reject(new Error("App Release store operation unavailable")), decodeResult);
|
|
42
|
+
}
|
|
43
|
+
function fromPromise(operation, decodeResult) {
|
|
44
|
+
return Effect.tryPromise({
|
|
45
|
+
try: operation,
|
|
46
|
+
catch: unavailable,
|
|
47
|
+
}).pipe(Effect.flatMap((result) => Effect.try({
|
|
48
|
+
try: () => decodeResult(result),
|
|
49
|
+
catch: invalidStoreResult,
|
|
50
|
+
}).pipe(Effect.flatMap(storeOutcome))));
|
|
51
|
+
}
|
|
52
|
+
function storeOutcome(decoded) {
|
|
53
|
+
return Result.match(decoded, {
|
|
54
|
+
onFailure: () => Effect.fail(invalidStoreResult()),
|
|
55
|
+
onSuccess: (result) => result.kind === "success"
|
|
56
|
+
? Effect.succeed(result.value)
|
|
57
|
+
: Effect.fail(result.failure),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function unavailable() {
|
|
61
|
+
return { kind: "unavailable" };
|
|
62
|
+
}
|
|
63
|
+
function invalidStoreResult() {
|
|
64
|
+
return { kind: "invalid-store-result" };
|
|
65
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ArtifactReader } from "./app-release-contract.js";
|
|
2
|
+
import type { AppReleaseArtifactReader } from "./app-release-artifact-reader.js";
|
|
3
|
+
/**
|
|
4
|
+
* Adapt the Promise artifact-reader projection at the one outer edge.
|
|
5
|
+
*
|
|
6
|
+
* Successful values remain hostile until the core's canonical schema decodes
|
|
7
|
+
* them. A rejected Promise is closed to the reader's unavailable failure.
|
|
8
|
+
*/
|
|
9
|
+
export declare function adaptPromiseArtifactReader(projection: ArtifactReader): AppReleaseArtifactReader;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
/**
|
|
3
|
+
* Adapt the Promise artifact-reader projection at the one outer edge.
|
|
4
|
+
*
|
|
5
|
+
* Successful values remain hostile until the core's canonical schema decodes
|
|
6
|
+
* them. A rejected Promise is closed to the reader's unavailable failure.
|
|
7
|
+
*/
|
|
8
|
+
export function adaptPromiseArtifactReader(projection) {
|
|
9
|
+
return {
|
|
10
|
+
read: (input) => Effect.tryPromise({
|
|
11
|
+
try: () => projection.read(input),
|
|
12
|
+
catch: unavailable,
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function unavailable() {
|
|
17
|
+
return { kind: "unavailable" };
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Only trusted column expressions enter DDL; all request values are bound. */
|
|
2
|
+
export declare function routeKeySql(column: string): string;
|
|
3
|
+
/** Immutable Surface metadata and database-enforced policy comparison key. */
|
|
4
|
+
export declare const publicSurfaceTableSql: string;
|
|
5
|
+
/** One write: pre-check custody and bound unused rows before minting any Surface. */
|
|
6
|
+
export declare const insertPublicSurfaceSql = "\nWITH candidate(public_surface_id,app_id,stable_route,route_key,\n publisher_team_id,publisher_user_id,authority_kind,request_key)\nAS (VALUES (?,?,?,?,?,?,?,?))\nINSERT INTO app_release_public_surfaces\n (public_surface_id,app_id,stable_route,route_key,publisher_team_id,publisher_user_id,authority_kind,request_key)\nSELECT s.public_surface_id,s.app_id,s.stable_route,s.route_key,\n s.publisher_team_id,s.publisher_user_id,s.authority_kind,s.request_key\nFROM candidate s\nJOIN app_release_route_policy policy ON policy.singleton=1\nLEFT JOIN app_release_route_claims c ON c.route_key=s.route_key\nWHERE \nNOT EXISTS (SELECT 1 FROM app_release_safety_takedowns a\n WHERE a.app_id=s.app_id AND a.stable_route=s.stable_route)\nAND\nNOT EXISTS (\n SELECT 1 FROM app_release_surface_retirements x\n WHERE x.public_surface_id=s.public_surface_id\n)\nAND NOT EXISTS (\n SELECT 1 FROM app_release_team_route_blocks b\n WHERE b.team_id=s.publisher_team_id AND b.route_key=s.route_key\n)\nAND (c.route_key IS NOT NULL OR NOT EXISTS (\n SELECT 1 FROM app_release_route_holds h\n WHERE h.route_key=s.route_key AND h.until_s>unixepoch()\n))\nAND (c.route_key IS NULL OR\n (c.public_surface_id=s.public_surface_id AND c.state='held'))\nAND (s.authority_kind='platform' OR c.public_surface_id=s.public_surface_id OR\n (SELECT count(*) FROM app_release_route_claims own\n JOIN app_release_public_surfaces owned_surface ON owned_surface.public_surface_id=own.public_surface_id\n WHERE own.team_id=s.publisher_team_id AND owned_surface.authority_kind='customer')<policy.max_team_routes)\nAND (\n (s.authority_kind='platform' AND EXISTS (\n SELECT 1 FROM app_release_platform_routes p\n WHERE p.stable_route=s.stable_route AND p.app_id=s.app_id\n ))\n OR\n (s.authority_kind='customer' AND EXISTS (\n SELECT 1 FROM app_release_team_publishers p\n WHERE p.app_id=s.app_id AND p.team_id=s.publisher_team_id\n AND p.source_provider='artifacts' AND p.source_repository=p.app_id\n AND p.source_branch='main' AND p.template_app_id IS NOT NULL\n AND p.template_app_id<>p.app_id AND p.template_seed_revision IS NOT NULL\n ) AND (\n c.public_surface_id=s.public_surface_id OR (\n NOT EXISTS (SELECT 1 FROM app_release_platform_routes p\n WHERE p.route_key=s.route_key)\n AND NOT EXISTS (\n SELECT 1 FROM app_release_reserved_route_keys k\n WHERE (k.match_kind='exact' AND k.route_key=s.route_key)\n OR (k.match_kind='prefix' AND\n substr(s.route_key,1,length(k.route_key))=k.route_key\n AND NOT EXISTS (SELECT 1 FROM app_release_route_brand_exceptions x WHERE x.route_key=s.route_key))\n )\n )\n ))\n)\nAND (SELECT count(*) FROM app_release_public_surfaces unused\n WHERE unused.app_id=s.app_id AND NOT EXISTS (\n SELECT 1 FROM app_release_transitions t\n WHERE t.public_surface_id=unused.public_surface_id\n AND t.operation_kind IN ('promotion','rollback')\n ))<policy.max_unused_surfaces_per_app\nON CONFLICT DO NOTHING\nRETURNING *";
|
|
7
|
+
/** Current route custody is separate from immutable Surface and transition history. */
|
|
8
|
+
export declare const publicRouteSchemaSql: string;
|
|
9
|
+
/** Operator replay history preserves original Surface attribution. */
|
|
10
|
+
export declare const platformOwnerReplaySchemaSql = "\nCREATE TABLE app_release_platform_owner_replays (sequence INTEGER PRIMARY KEY AUTOINCREMENT, public_surface_id TEXT NOT NULL, app_id TEXT NOT NULL, stable_route TEXT NOT NULL, owner_team_id TEXT NOT NULL, publisher_user_id TEXT NOT NULL, operator_user_id TEXT NOT NULL, recorded_at INTEGER NOT NULL) STRICT;\nCREATE INDEX app_release_platform_owner_replays_app ON app_release_platform_owner_replays(app_id);\n";
|