@selvajs/server 1.2.0 → 1.3.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/dist/access/guards.d.ts +213 -0
- package/dist/access/guards.d.ts.map +1 -0
- package/dist/access/guards.js +417 -0
- package/dist/access/guards.js.map +1 -0
- package/dist/access/index.d.ts +2 -0
- package/dist/access/index.d.ts.map +1 -1
- package/dist/access/index.js +6 -0
- package/dist/access/index.js.map +1 -1
- package/dist/access/permissions-scope.d.ts +15 -0
- package/dist/access/permissions-scope.d.ts.map +1 -0
- package/dist/access/permissions-scope.js +23 -0
- package/dist/access/permissions-scope.js.map +1 -0
- package/dist/api/bodies.d.ts +104 -0
- package/dist/api/bodies.d.ts.map +1 -0
- package/dist/api/bodies.js +87 -0
- package/dist/api/bodies.js.map +1 -0
- package/dist/api/deps.d.ts +135 -0
- package/dist/api/deps.d.ts.map +1 -0
- package/dist/api/deps.js +55 -0
- package/dist/api/deps.js.map +1 -0
- package/dist/api/errors.d.ts +35 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +63 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/index.d.ts +12 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +15 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/map-core-error.d.ts +19 -0
- package/dist/api/map-core-error.d.ts.map +1 -0
- package/dist/api/map-core-error.js +49 -0
- package/dist/api/map-core-error.js.map +1 -0
- package/dist/api/pagination.d.ts +19 -0
- package/dist/api/pagination.d.ts.map +1 -0
- package/dist/api/pagination.js +40 -0
- package/dist/api/pagination.js.map +1 -0
- package/dist/api/request.d.ts +93 -0
- package/dist/api/request.d.ts.map +1 -0
- package/dist/api/request.js +150 -0
- package/dist/api/request.js.map +1 -0
- package/dist/api/respond.d.ts +29 -0
- package/dist/api/respond.d.ts.map +1 -0
- package/dist/api/respond.js +59 -0
- package/dist/api/respond.js.map +1 -0
- package/dist/api/responses-schema.d.ts +158 -0
- package/dist/api/responses-schema.d.ts.map +1 -0
- package/dist/api/responses-schema.js +104 -0
- package/dist/api/responses-schema.js.map +1 -0
- package/dist/api/responses.d.ts +19 -0
- package/dist/api/responses.d.ts.map +1 -0
- package/dist/api/responses.js +21 -0
- package/dist/api/responses.js.map +1 -0
- package/dist/api/shaped.d.ts +23 -0
- package/dist/api/shaped.d.ts.map +1 -0
- package/dist/api/shaped.js +29 -0
- package/dist/api/shaped.js.map +1 -0
- package/dist/api/types.d.ts +41 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +12 -0
- package/dist/api/types.js.map +1 -0
- package/dist/compute/errors.d.ts +18 -0
- package/dist/compute/errors.d.ts.map +1 -0
- package/dist/compute/errors.js +21 -0
- package/dist/compute/errors.js.map +1 -0
- package/dist/compute/evict-changed-servers.d.ts +22 -0
- package/dist/compute/evict-changed-servers.d.ts.map +1 -0
- package/dist/compute/evict-changed-servers.js +31 -0
- package/dist/compute/evict-changed-servers.js.map +1 -0
- package/dist/compute/idempotency-http.d.ts +47 -0
- package/dist/compute/idempotency-http.d.ts.map +1 -0
- package/dist/compute/idempotency-http.js +51 -0
- package/dist/compute/idempotency-http.js.map +1 -0
- package/dist/compute/index.d.ts +5 -0
- package/dist/compute/index.d.ts.map +1 -1
- package/dist/compute/index.js +5 -0
- package/dist/compute/index.js.map +1 -1
- package/dist/compute/resolve-server.d.ts +25 -0
- package/dist/compute/resolve-server.d.ts.map +1 -0
- package/dist/compute/resolve-server.js +35 -0
- package/dist/compute/resolve-server.js.map +1 -0
- package/dist/compute/server-config-write.d.ts +52 -0
- package/dist/compute/server-config-write.d.ts.map +1 -0
- package/dist/compute/server-config-write.js +100 -0
- package/dist/compute/server-config-write.js.map +1 -0
- package/dist/definitions/index.d.ts +1 -0
- package/dist/definitions/index.d.ts.map +1 -1
- package/dist/definitions/index.js +1 -0
- package/dist/definitions/index.js.map +1 -1
- package/dist/definitions/visibility.d.ts +74 -0
- package/dist/definitions/visibility.d.ts.map +1 -0
- package/dist/definitions/visibility.js +117 -0
- package/dist/definitions/visibility.js.map +1 -0
- package/dist/handlers/definitionVersions.d.ts +30 -0
- package/dist/handlers/definitionVersions.d.ts.map +1 -0
- package/dist/handlers/definitionVersions.js +93 -0
- package/dist/handlers/definitionVersions.js.map +1 -0
- package/dist/handlers/definitions.d.ts +35 -0
- package/dist/handlers/definitions.d.ts.map +1 -0
- package/dist/handlers/definitions.js +207 -0
- package/dist/handlers/definitions.js.map +1 -0
- package/dist/handlers/index.d.ts +27 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +29 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/invite-lookup.d.ts +11 -0
- package/dist/handlers/invite-lookup.d.ts.map +1 -0
- package/dist/handlers/invite-lookup.js +20 -0
- package/dist/handlers/invite-lookup.js.map +1 -0
- package/dist/handlers/invites.d.ts +31 -0
- package/dist/handlers/invites.d.ts.map +1 -0
- package/dist/handlers/invites.js +148 -0
- package/dist/handlers/invites.js.map +1 -0
- package/dist/handlers/me.d.ts +8 -0
- package/dist/handlers/me.d.ts.map +1 -0
- package/dist/handlers/me.js +21 -0
- package/dist/handlers/me.js.map +1 -0
- package/dist/handlers/me.starred.d.ts +11 -0
- package/dist/handlers/me.starred.d.ts.map +1 -0
- package/dist/handlers/me.starred.js +21 -0
- package/dist/handlers/me.starred.js.map +1 -0
- package/dist/handlers/orgAssets.d.ts +13 -0
- package/dist/handlers/orgAssets.d.ts.map +1 -0
- package/dist/handlers/orgAssets.js +54 -0
- package/dist/handlers/orgAssets.js.map +1 -0
- package/dist/handlers/orgCompute.d.ts +16 -0
- package/dist/handlers/orgCompute.d.ts.map +1 -0
- package/dist/handlers/orgCompute.js +96 -0
- package/dist/handlers/orgCompute.js.map +1 -0
- package/dist/handlers/orgMembers.d.ts +29 -0
- package/dist/handlers/orgMembers.d.ts.map +1 -0
- package/dist/handlers/orgMembers.js +200 -0
- package/dist/handlers/orgMembers.js.map +1 -0
- package/dist/handlers/orgs.d.ts +16 -0
- package/dist/handlers/orgs.d.ts.map +1 -0
- package/dist/handlers/orgs.js +26 -0
- package/dist/handlers/orgs.js.map +1 -0
- package/dist/handlers/projectMembers.d.ts +15 -0
- package/dist/handlers/projectMembers.d.ts.map +1 -0
- package/dist/handlers/projectMembers.js +95 -0
- package/dist/handlers/projectMembers.js.map +1 -0
- package/dist/handlers/projects.d.ts +21 -0
- package/dist/handlers/projects.d.ts.map +1 -0
- package/dist/handlers/projects.js +164 -0
- package/dist/handlers/projects.js.map +1 -0
- package/dist/handlers/reclaim.d.ts +10 -0
- package/dist/handlers/reclaim.d.ts.map +1 -0
- package/dist/handlers/reclaim.js +38 -0
- package/dist/handlers/reclaim.js.map +1 -0
- package/dist/handlers/services.d.ts +22 -0
- package/dist/handlers/services.d.ts.map +1 -0
- package/dist/handlers/services.js +37 -0
- package/dist/handlers/services.js.map +1 -0
- package/dist/handlers/shareLinks.d.ts +23 -0
- package/dist/handlers/shareLinks.d.ts.map +1 -0
- package/dist/handlers/shareLinks.js +85 -0
- package/dist/handlers/shareLinks.js.map +1 -0
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +1 -0
- package/dist/http/index.js.map +1 -1
- package/dist/http/login-rate-limit.d.ts +72 -0
- package/dist/http/login-rate-limit.d.ts.map +1 -0
- package/dist/http/login-rate-limit.js +84 -0
- package/dist/http/login-rate-limit.js.map +1 -0
- package/dist/logging/requestId.d.ts +4 -0
- package/dist/logging/requestId.d.ts.map +1 -1
- package/dist/logging/requestId.js +15 -2
- package/dist/logging/requestId.js.map +1 -1
- package/dist/notifications/deliver-invite.d.ts +27 -0
- package/dist/notifications/deliver-invite.d.ts.map +1 -0
- package/dist/notifications/deliver-invite.js +32 -0
- package/dist/notifications/deliver-invite.js.map +1 -0
- package/dist/notifications/index.d.ts +1 -0
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +1 -0
- package/dist/notifications/index.js.map +1 -1
- package/dist/organizations/index.d.ts +3 -0
- package/dist/organizations/index.d.ts.map +1 -0
- package/dist/organizations/index.js +5 -0
- package/dist/organizations/index.js.map +1 -0
- package/dist/organizations/list-all-members.d.ts +10 -0
- package/dist/organizations/list-all-members.d.ts.map +1 -0
- package/dist/organizations/list-all-members.js +36 -0
- package/dist/organizations/list-all-members.js.map +1 -0
- package/dist/organizations/org-asset-service.d.ts +39 -0
- package/dist/organizations/org-asset-service.d.ts.map +1 -0
- package/dist/organizations/org-asset-service.js +71 -0
- package/dist/organizations/org-asset-service.js.map +1 -0
- package/dist/projects/create-project.d.ts +30 -0
- package/dist/projects/create-project.d.ts.map +1 -0
- package/dist/projects/create-project.js +56 -0
- package/dist/projects/create-project.js.map +1 -0
- package/dist/projects/index.d.ts +2 -0
- package/dist/projects/index.d.ts.map +1 -0
- package/dist/projects/index.js +3 -0
- package/dist/projects/index.js.map +1 -0
- package/dist/providers/auth-lookup.d.ts +14 -0
- package/dist/providers/auth-lookup.d.ts.map +1 -0
- package/dist/providers/auth-lookup.js +34 -0
- package/dist/providers/auth-lookup.js.map +1 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +1 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/testing/harness.d.ts +168 -0
- package/dist/testing/harness.d.ts.map +1 -0
- package/dist/testing/harness.js +314 -0
- package/dist/testing/harness.js.map +1 -0
- package/dist/testing/index.d.ts +18 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +15 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/scenarios.d.ts +67 -0
- package/dist/testing/scenarios.d.ts.map +1 -0
- package/dist/testing/scenarios.js +104 -0
- package/dist/testing/scenarios.js.map +1 -0
- package/dist/testing/seed-adapter.d.ts +31 -0
- package/dist/testing/seed-adapter.d.ts.map +1 -0
- package/dist/testing/seed-adapter.js +19 -0
- package/dist/testing/seed-adapter.js.map +1 -0
- package/package.json +32 -4
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response schemas for the v1 payloads that must not carry a stored secret.
|
|
3
|
+
*
|
|
4
|
+
* Three store types hold a credential next to fields a client legitimately
|
|
5
|
+
* reads: `ShareLink.tokenHash` and `Invite.tokenHash` are HMACs of a token,
|
|
6
|
+
* `ComputeServerCommon.apiKey` is a live Rhino.Compute key. Handlers used to
|
|
7
|
+
* strip them by destructuring — `const { tokenHash: _omit, ...rest } = invite`
|
|
8
|
+
* — which works until someone adds a field to the stored type or edits the line
|
|
9
|
+
* away. Nothing fails at build time; the credential just starts appearing in a
|
|
10
|
+
* response.
|
|
11
|
+
*
|
|
12
|
+
* Parsing through these schemas inverts the default: the response carries only
|
|
13
|
+
* what the schema names, so a new field on the stored type is invisible to
|
|
14
|
+
* clients until added here on purpose. Use them via `shaped`/`shapedCollection`
|
|
15
|
+
* in `./route.ts`.
|
|
16
|
+
*
|
|
17
|
+
* These describe **responses**, not stored records — where the two differ (an
|
|
18
|
+
* invite's one-time `acceptUrl`, a share link's `hasToken` sentinel) the
|
|
19
|
+
* response is the authority.
|
|
20
|
+
*/
|
|
21
|
+
import { z } from 'zod';
|
|
22
|
+
export declare const ShareLinkResponseSchema: z.ZodObject<{
|
|
23
|
+
id: z.ZodString;
|
|
24
|
+
definitionId: z.ZodString;
|
|
25
|
+
channel: z.ZodEnum<{
|
|
26
|
+
draft: "draft";
|
|
27
|
+
live: "live";
|
|
28
|
+
}>;
|
|
29
|
+
name: z.ZodOptional<z.ZodString>;
|
|
30
|
+
createdBy: z.ZodString;
|
|
31
|
+
createdAt: z.ZodString;
|
|
32
|
+
expiresAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
33
|
+
revokedAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
34
|
+
allowSolve: z.ZodBoolean;
|
|
35
|
+
maxSolves: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
36
|
+
solveCount: z.ZodNumber;
|
|
37
|
+
hasToken: z.ZodLiteral<true>;
|
|
38
|
+
}, z.core.$strip>;
|
|
39
|
+
export type ShareLinkResponse = z.infer<typeof ShareLinkResponseSchema>;
|
|
40
|
+
/** The mint response. `token` is the plaintext, returned exactly once. */
|
|
41
|
+
export declare const CreatedShareLinkResponseSchema: z.ZodObject<{
|
|
42
|
+
link: z.ZodObject<{
|
|
43
|
+
id: z.ZodString;
|
|
44
|
+
definitionId: z.ZodString;
|
|
45
|
+
channel: z.ZodEnum<{
|
|
46
|
+
draft: "draft";
|
|
47
|
+
live: "live";
|
|
48
|
+
}>;
|
|
49
|
+
name: z.ZodOptional<z.ZodString>;
|
|
50
|
+
createdBy: z.ZodString;
|
|
51
|
+
createdAt: z.ZodString;
|
|
52
|
+
expiresAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
53
|
+
revokedAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
54
|
+
allowSolve: z.ZodBoolean;
|
|
55
|
+
maxSolves: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
56
|
+
solveCount: z.ZodNumber;
|
|
57
|
+
hasToken: z.ZodLiteral<true>;
|
|
58
|
+
}, z.core.$strip>;
|
|
59
|
+
token: z.ZodString;
|
|
60
|
+
}, z.core.$strip>;
|
|
61
|
+
export declare const InviteResponseSchema: z.ZodObject<{
|
|
62
|
+
id: z.ZodString;
|
|
63
|
+
email: z.ZodString;
|
|
64
|
+
orgId: z.ZodString;
|
|
65
|
+
orgRole: z.ZodString;
|
|
66
|
+
orgPermissions: z.ZodArray<z.ZodString>;
|
|
67
|
+
platformPermissions: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
68
|
+
invitedBy: z.ZodString;
|
|
69
|
+
createdAt: z.ZodString;
|
|
70
|
+
expiresAt: z.ZodString;
|
|
71
|
+
acceptedAt: z.ZodOptional<z.ZodString>;
|
|
72
|
+
acceptedByUserId: z.ZodOptional<z.ZodString>;
|
|
73
|
+
}, z.core.$strip>;
|
|
74
|
+
/**
|
|
75
|
+
* The create response: the invite plus the link to send, built per request.
|
|
76
|
+
*
|
|
77
|
+
* `acceptUrl` is returned whatever `delivery` says — mail is best-effort, and
|
|
78
|
+
* manual sharing is the fallback when it is off or the send failed.
|
|
79
|
+
*/
|
|
80
|
+
export declare const CreatedInviteResponseSchema: z.ZodObject<{
|
|
81
|
+
id: z.ZodString;
|
|
82
|
+
email: z.ZodString;
|
|
83
|
+
orgId: z.ZodString;
|
|
84
|
+
orgRole: z.ZodString;
|
|
85
|
+
orgPermissions: z.ZodArray<z.ZodString>;
|
|
86
|
+
platformPermissions: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
87
|
+
invitedBy: z.ZodString;
|
|
88
|
+
createdAt: z.ZodString;
|
|
89
|
+
expiresAt: z.ZodString;
|
|
90
|
+
acceptedAt: z.ZodOptional<z.ZodString>;
|
|
91
|
+
acceptedByUserId: z.ZodOptional<z.ZodString>;
|
|
92
|
+
acceptUrl: z.ZodString;
|
|
93
|
+
delivery: z.ZodEnum<{
|
|
94
|
+
sent: "sent";
|
|
95
|
+
"not-configured": "not-configured";
|
|
96
|
+
failed: "failed";
|
|
97
|
+
}>;
|
|
98
|
+
}, z.core.$strip>;
|
|
99
|
+
/**
|
|
100
|
+
* An org-owned compute server. `apiKey` is replaced by `hasApiKey`, so a picker
|
|
101
|
+
* can render "key set" without any store decrypting a secret to throw away.
|
|
102
|
+
*/
|
|
103
|
+
export declare const OrgComputeServerResponseSchema: z.ZodObject<{
|
|
104
|
+
id: z.ZodString;
|
|
105
|
+
label: z.ZodString;
|
|
106
|
+
serverUrl: z.ZodString;
|
|
107
|
+
scope: z.ZodLiteral<"org">;
|
|
108
|
+
ownerOrgId: z.ZodString;
|
|
109
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
110
|
+
retryCount: z.ZodOptional<z.ZodNumber>;
|
|
111
|
+
hasApiKey: z.ZodBoolean;
|
|
112
|
+
}, z.core.$strip>;
|
|
113
|
+
/** A server this org may select but not edit. Never carries a key either. */
|
|
114
|
+
export declare const ComputeCatalogEntrySchema: z.ZodObject<{
|
|
115
|
+
id: z.ZodString;
|
|
116
|
+
label: z.ZodString;
|
|
117
|
+
serverUrl: z.ZodString;
|
|
118
|
+
scope: z.ZodEnum<{
|
|
119
|
+
org: "org";
|
|
120
|
+
platform: "platform";
|
|
121
|
+
}>;
|
|
122
|
+
source: z.ZodEnum<{
|
|
123
|
+
org: "org";
|
|
124
|
+
platform: "platform";
|
|
125
|
+
}>;
|
|
126
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
127
|
+
retryCount: z.ZodOptional<z.ZodNumber>;
|
|
128
|
+
}, z.core.$strip>;
|
|
129
|
+
export declare const OrgComputeResponseSchema: z.ZodObject<{
|
|
130
|
+
servers: z.ZodArray<z.ZodObject<{
|
|
131
|
+
id: z.ZodString;
|
|
132
|
+
label: z.ZodString;
|
|
133
|
+
serverUrl: z.ZodString;
|
|
134
|
+
scope: z.ZodLiteral<"org">;
|
|
135
|
+
ownerOrgId: z.ZodString;
|
|
136
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
137
|
+
retryCount: z.ZodOptional<z.ZodNumber>;
|
|
138
|
+
hasApiKey: z.ZodBoolean;
|
|
139
|
+
}, z.core.$strip>>;
|
|
140
|
+
defaultServerId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
141
|
+
globalDefaultServerId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
142
|
+
catalog: z.ZodArray<z.ZodObject<{
|
|
143
|
+
id: z.ZodString;
|
|
144
|
+
label: z.ZodString;
|
|
145
|
+
serverUrl: z.ZodString;
|
|
146
|
+
scope: z.ZodEnum<{
|
|
147
|
+
org: "org";
|
|
148
|
+
platform: "platform";
|
|
149
|
+
}>;
|
|
150
|
+
source: z.ZodEnum<{
|
|
151
|
+
org: "org";
|
|
152
|
+
platform: "platform";
|
|
153
|
+
}>;
|
|
154
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
155
|
+
retryCount: z.ZodOptional<z.ZodNumber>;
|
|
156
|
+
}, z.core.$strip>>;
|
|
157
|
+
}, z.core.$strip>;
|
|
158
|
+
//# sourceMappingURL=responses-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responses-schema.d.ts","sourceRoot":"","sources":["../../src/api/responses-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;iBAclC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,0EAA0E;AAC1E,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;iBAGzC,CAAC;AAMH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;iBAY/B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;iBAGtC,CAAC;AAMH;;;GAGG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;iBASzC,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;iBAQpC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAKnC,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response schemas for the v1 payloads that must not carry a stored secret.
|
|
3
|
+
*
|
|
4
|
+
* Three store types hold a credential next to fields a client legitimately
|
|
5
|
+
* reads: `ShareLink.tokenHash` and `Invite.tokenHash` are HMACs of a token,
|
|
6
|
+
* `ComputeServerCommon.apiKey` is a live Rhino.Compute key. Handlers used to
|
|
7
|
+
* strip them by destructuring — `const { tokenHash: _omit, ...rest } = invite`
|
|
8
|
+
* — which works until someone adds a field to the stored type or edits the line
|
|
9
|
+
* away. Nothing fails at build time; the credential just starts appearing in a
|
|
10
|
+
* response.
|
|
11
|
+
*
|
|
12
|
+
* Parsing through these schemas inverts the default: the response carries only
|
|
13
|
+
* what the schema names, so a new field on the stored type is invisible to
|
|
14
|
+
* clients until added here on purpose. Use them via `shaped`/`shapedCollection`
|
|
15
|
+
* in `./route.ts`.
|
|
16
|
+
*
|
|
17
|
+
* These describe **responses**, not stored records — where the two differ (an
|
|
18
|
+
* invite's one-time `acceptUrl`, a share link's `hasToken` sentinel) the
|
|
19
|
+
* response is the authority.
|
|
20
|
+
*/
|
|
21
|
+
import { z } from 'zod';
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Share links
|
|
24
|
+
// ============================================================================
|
|
25
|
+
export const ShareLinkResponseSchema = z.object({
|
|
26
|
+
id: z.string(),
|
|
27
|
+
definitionId: z.string(),
|
|
28
|
+
channel: z.enum(['live', 'draft']),
|
|
29
|
+
name: z.string().optional(),
|
|
30
|
+
createdBy: z.string(),
|
|
31
|
+
createdAt: z.string(),
|
|
32
|
+
expiresAt: z.string().nullish(),
|
|
33
|
+
revokedAt: z.string().nullish(),
|
|
34
|
+
allowSolve: z.boolean(),
|
|
35
|
+
maxSolves: z.number().nullish(),
|
|
36
|
+
solveCount: z.number(),
|
|
37
|
+
/** Sentinel replacing `tokenHash`: a token exists, its hash is not yours. */
|
|
38
|
+
hasToken: z.literal(true)
|
|
39
|
+
});
|
|
40
|
+
/** The mint response. `token` is the plaintext, returned exactly once. */
|
|
41
|
+
export const CreatedShareLinkResponseSchema = z.object({
|
|
42
|
+
link: ShareLinkResponseSchema,
|
|
43
|
+
token: z.string()
|
|
44
|
+
});
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Invites
|
|
47
|
+
// ============================================================================
|
|
48
|
+
export const InviteResponseSchema = z.object({
|
|
49
|
+
id: z.string(),
|
|
50
|
+
email: z.string(),
|
|
51
|
+
orgId: z.string(),
|
|
52
|
+
orgRole: z.string(),
|
|
53
|
+
orgPermissions: z.array(z.string()),
|
|
54
|
+
platformPermissions: z.array(z.string()).default([]),
|
|
55
|
+
invitedBy: z.string(),
|
|
56
|
+
createdAt: z.string(),
|
|
57
|
+
expiresAt: z.string(),
|
|
58
|
+
acceptedAt: z.string().optional(),
|
|
59
|
+
acceptedByUserId: z.string().optional()
|
|
60
|
+
});
|
|
61
|
+
/**
|
|
62
|
+
* The create response: the invite plus the link to send, built per request.
|
|
63
|
+
*
|
|
64
|
+
* `acceptUrl` is returned whatever `delivery` says — mail is best-effort, and
|
|
65
|
+
* manual sharing is the fallback when it is off or the send failed.
|
|
66
|
+
*/
|
|
67
|
+
export const CreatedInviteResponseSchema = InviteResponseSchema.extend({
|
|
68
|
+
acceptUrl: z.string(),
|
|
69
|
+
delivery: z.enum(['sent', 'not-configured', 'failed'])
|
|
70
|
+
});
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// Org compute
|
|
73
|
+
// ============================================================================
|
|
74
|
+
/**
|
|
75
|
+
* An org-owned compute server. `apiKey` is replaced by `hasApiKey`, so a picker
|
|
76
|
+
* can render "key set" without any store decrypting a secret to throw away.
|
|
77
|
+
*/
|
|
78
|
+
export const OrgComputeServerResponseSchema = z.object({
|
|
79
|
+
id: z.string(),
|
|
80
|
+
label: z.string(),
|
|
81
|
+
serverUrl: z.string(),
|
|
82
|
+
scope: z.literal('org'),
|
|
83
|
+
ownerOrgId: z.string(),
|
|
84
|
+
timeoutMs: z.number().optional(),
|
|
85
|
+
retryCount: z.number().optional(),
|
|
86
|
+
hasApiKey: z.boolean()
|
|
87
|
+
});
|
|
88
|
+
/** A server this org may select but not edit. Never carries a key either. */
|
|
89
|
+
export const ComputeCatalogEntrySchema = z.object({
|
|
90
|
+
id: z.string(),
|
|
91
|
+
label: z.string(),
|
|
92
|
+
serverUrl: z.string(),
|
|
93
|
+
scope: z.enum(['platform', 'org']),
|
|
94
|
+
source: z.enum(['platform', 'org']),
|
|
95
|
+
timeoutMs: z.number().optional(),
|
|
96
|
+
retryCount: z.number().optional()
|
|
97
|
+
});
|
|
98
|
+
export const OrgComputeResponseSchema = z.object({
|
|
99
|
+
servers: z.array(OrgComputeServerResponseSchema),
|
|
100
|
+
defaultServerId: z.string().nullish(),
|
|
101
|
+
globalDefaultServerId: z.string().nullish(),
|
|
102
|
+
catalog: z.array(ComputeCatalogEntrySchema)
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=responses-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responses-schema.js","sourceRoot":"","sources":["../../src/api/responses-schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response shapes shared by every handler.
|
|
3
|
+
*
|
|
4
|
+
* The pagination envelope in particular: a resource-named key would force each
|
|
5
|
+
* client to write one unwrapper per endpoint instead of one pagination helper,
|
|
6
|
+
* so every list endpoint returns `{ items, nextCursor? }` through `collection`
|
|
7
|
+
* rather than assembling it by hand.
|
|
8
|
+
*/
|
|
9
|
+
import type { ApiResponse } from './types.js';
|
|
10
|
+
/** A paginated collection. The one envelope every list endpoint returns. */
|
|
11
|
+
export declare function collection<T>(page: {
|
|
12
|
+
items: T[];
|
|
13
|
+
nextCursor?: string;
|
|
14
|
+
}): ApiResponse;
|
|
15
|
+
/** A created resource. */
|
|
16
|
+
export declare function created<T>(body: T): ApiResponse;
|
|
17
|
+
/** A successful mutation with nothing to say. */
|
|
18
|
+
export declare function noContent(): ApiResponse;
|
|
19
|
+
//# sourceMappingURL=responses.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../src/api/responses.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CAEpF;AAED,0BAA0B;AAC1B,wBAAgB,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,WAAW,CAE/C;AAED,iDAAiD;AACjD,wBAAgB,SAAS,IAAI,WAAW,CAEvC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response shapes shared by every handler.
|
|
3
|
+
*
|
|
4
|
+
* The pagination envelope in particular: a resource-named key would force each
|
|
5
|
+
* client to write one unwrapper per endpoint instead of one pagination helper,
|
|
6
|
+
* so every list endpoint returns `{ items, nextCursor? }` through `collection`
|
|
7
|
+
* rather than assembling it by hand.
|
|
8
|
+
*/
|
|
9
|
+
/** A paginated collection. The one envelope every list endpoint returns. */
|
|
10
|
+
export function collection(page) {
|
|
11
|
+
return { body: { items: page.items, nextCursor: page.nextCursor } };
|
|
12
|
+
}
|
|
13
|
+
/** A created resource. */
|
|
14
|
+
export function created(body) {
|
|
15
|
+
return { status: 201, body };
|
|
16
|
+
}
|
|
17
|
+
/** A successful mutation with nothing to say. */
|
|
18
|
+
export function noContent() {
|
|
19
|
+
return { status: 204 };
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=responses.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responses.js","sourceRoot":"","sources":["../../src/api/responses.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,4EAA4E;AAC5E,MAAM,UAAU,UAAU,CAAI,IAAyC;IACtE,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;AACrE,CAAC;AAED,0BAA0B;AAC1B,MAAM,UAAU,OAAO,CAAI,IAAO;IACjC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AAC9B,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,SAAS;IACxB,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serializing a response through an explicit schema.
|
|
3
|
+
*
|
|
4
|
+
* **This is what keeps a secret from reaching a client by accident.** Several
|
|
5
|
+
* responses are store records with a credential removed — `tokenHash` on a
|
|
6
|
+
* share link or invite, `apiKey` on an org compute server. Parsing through a
|
|
7
|
+
* schema means the response carries only what the schema names, so a new field
|
|
8
|
+
* on the stored type is invisible until someone adds it here deliberately.
|
|
9
|
+
*
|
|
10
|
+
* Returning the record directly would put a credential hash on the wire with
|
|
11
|
+
* nothing failing at build time, which is why these live beside the handler
|
|
12
|
+
* contract rather than in each host.
|
|
13
|
+
*/
|
|
14
|
+
import type { ZodType } from 'zod';
|
|
15
|
+
import type { ApiResponse } from './types.js';
|
|
16
|
+
/** Serialize a payload through an explicit response schema. */
|
|
17
|
+
export declare function shaped<T>(schema: ZodType<T>, payload: unknown, status?: number): ApiResponse;
|
|
18
|
+
/** `shaped`, mapped over a page of records. */
|
|
19
|
+
export declare function shapedCollection<T>(schema: ZodType<T>, page: {
|
|
20
|
+
items: unknown[];
|
|
21
|
+
nextCursor?: string;
|
|
22
|
+
}): ApiResponse;
|
|
23
|
+
//# sourceMappingURL=shaped.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shaped.d.ts","sourceRoot":"","sources":["../../src/api/shaped.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,+DAA+D;AAC/D,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAM,GAAG,WAAW,CAIzF;AAED,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,CAAC,EACjC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAClB,IAAI,EAAE;IAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,WAAW,CAOb"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serializing a response through an explicit schema.
|
|
3
|
+
*
|
|
4
|
+
* **This is what keeps a secret from reaching a client by accident.** Several
|
|
5
|
+
* responses are store records with a credential removed — `tokenHash` on a
|
|
6
|
+
* share link or invite, `apiKey` on an org compute server. Parsing through a
|
|
7
|
+
* schema means the response carries only what the schema names, so a new field
|
|
8
|
+
* on the stored type is invisible until someone adds it here deliberately.
|
|
9
|
+
*
|
|
10
|
+
* Returning the record directly would put a credential hash on the wire with
|
|
11
|
+
* nothing failing at build time, which is why these live beside the handler
|
|
12
|
+
* contract rather than in each host.
|
|
13
|
+
*/
|
|
14
|
+
/** Serialize a payload through an explicit response schema. */
|
|
15
|
+
export function shaped(schema, payload, status = 200) {
|
|
16
|
+
// A response failing its own schema is a bug in the handler, not bad caller
|
|
17
|
+
// input — let it surface as a 500 rather than ship a half-valid body.
|
|
18
|
+
return { status, body: schema.parse(payload) };
|
|
19
|
+
}
|
|
20
|
+
/** `shaped`, mapped over a page of records. */
|
|
21
|
+
export function shapedCollection(schema, page) {
|
|
22
|
+
return {
|
|
23
|
+
body: {
|
|
24
|
+
items: page.items.map((item) => schema.parse(item)),
|
|
25
|
+
nextCursor: page.nextCursor
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=shaped.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shaped.js","sourceRoot":"","sources":["../../src/api/shaped.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,+DAA+D;AAC/D,MAAM,UAAU,MAAM,CAAI,MAAkB,EAAE,OAAgB,EAAE,MAAM,GAAG,GAAG;IAC3E,4EAA4E;IAC5E,sEAAsE;IACtE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;AAChD,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,gBAAgB,CAC/B,MAAkB,EAClB,IAA+C;IAE/C,OAAO;QACN,IAAI,EAAE;YACL,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnD,UAAU,EAAE,IAAI,CAAC,UAAU;SAC3B;KACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transport-free handler contract.
|
|
3
|
+
*
|
|
4
|
+
* A handler receives an `ApiRequest` and returns an `ApiResponse`. Neither type
|
|
5
|
+
* names a web framework, so the same handler mounts under SvelteKit, Next, Hono
|
|
6
|
+
* or an in-process call. What used to arrive as SvelteKit's `RequestEvent` is
|
|
7
|
+
* split into the four things handlers actually read — measured across the v1
|
|
8
|
+
* routes, that was `locals.ctx`, `locals.user`, `locals.log` and
|
|
9
|
+
* `locals.profile`, nothing else.
|
|
10
|
+
*/
|
|
11
|
+
import type { AuthUser, ILogger, RequestContext, UserProfile } from '@selvajs/platform';
|
|
12
|
+
import type { SelvaDeps } from './deps.js';
|
|
13
|
+
export interface ApiRequest {
|
|
14
|
+
/** Per-request identity + scope. Present only once authenticated. */
|
|
15
|
+
ctx?: RequestContext;
|
|
16
|
+
user?: AuthUser;
|
|
17
|
+
profile?: UserProfile;
|
|
18
|
+
/** Carries requestId/method/route. Prefer over a root logger. */
|
|
19
|
+
log: ILogger;
|
|
20
|
+
/** Path params, already extracted by the host router. */
|
|
21
|
+
params: Record<string, string | undefined>;
|
|
22
|
+
url: URL;
|
|
23
|
+
/** The raw request, for bodies and headers. */
|
|
24
|
+
request: Request;
|
|
25
|
+
/** Everything the handler talks to. Injected, never module-global. */
|
|
26
|
+
deps: SelvaDeps;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* What a handler returns. `body` is serialized as JSON unless it is already a
|
|
30
|
+
* `Response` (binary endpoints — file downloads, mesh streams) or `undefined`
|
|
31
|
+
* (204). Keeping this a value rather than a `Response` is what lets a host
|
|
32
|
+
* choose its own serialization; a handler that built its own `Response` would
|
|
33
|
+
* force every host to accept web-standard `Response` semantics.
|
|
34
|
+
*/
|
|
35
|
+
export interface ApiResponse {
|
|
36
|
+
status?: number;
|
|
37
|
+
body?: unknown;
|
|
38
|
+
headers?: Record<string, string>;
|
|
39
|
+
}
|
|
40
|
+
export type ApiHandler = (req: ApiRequest) => Promise<ApiResponse | Response>;
|
|
41
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACxF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,WAAW,UAAU;IAC1B,qEAAqE;IACrE,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,iEAAiE;IACjE,GAAG,EAAE,OAAO,CAAC;IACb,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC3C,GAAG,EAAE,GAAG,CAAC;IACT,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,sEAAsE;IACtE,IAAI,EAAE,SAAS,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,WAAW,GAAG,QAAQ,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transport-free handler contract.
|
|
3
|
+
*
|
|
4
|
+
* A handler receives an `ApiRequest` and returns an `ApiResponse`. Neither type
|
|
5
|
+
* names a web framework, so the same handler mounts under SvelteKit, Next, Hono
|
|
6
|
+
* or an in-process call. What used to arrive as SvelteKit's `RequestEvent` is
|
|
7
|
+
* split into the four things handlers actually read — measured across the v1
|
|
8
|
+
* routes, that was `locals.ctx`, `locals.user`, `locals.log` and
|
|
9
|
+
* `locals.profile`, nothing else.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute error types, kept in a leaf module with no imports.
|
|
3
|
+
*
|
|
4
|
+
* Separate from the function that throws them on purpose: an error mapper needs
|
|
5
|
+
* nothing but the class to `instanceof` against, and importing it from beside
|
|
6
|
+
* the throw site drags that module's dependencies into every mapper. Anything
|
|
7
|
+
* an error mapper imports belongs here.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Thrown when no compute server is configured or visible for the org — a
|
|
11
|
+
* misconfiguration an operator must fix in `/admin/compute`, not a bug. Routes
|
|
12
|
+
* map this to 503 instead of letting the pure helper's plain `Error` surface as
|
|
13
|
+
* a generic 500.
|
|
14
|
+
*/
|
|
15
|
+
export declare class ComputeServerUnconfiguredError extends Error {
|
|
16
|
+
constructor(message?: string);
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/compute/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,qBAAa,8BAA+B,SAAQ,KAAK;gBAEvD,OAAO,SAA6E;CAKrF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute error types, kept in a leaf module with no imports.
|
|
3
|
+
*
|
|
4
|
+
* Separate from the function that throws them on purpose: an error mapper needs
|
|
5
|
+
* nothing but the class to `instanceof` against, and importing it from beside
|
|
6
|
+
* the throw site drags that module's dependencies into every mapper. Anything
|
|
7
|
+
* an error mapper imports belongs here.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Thrown when no compute server is configured or visible for the org — a
|
|
11
|
+
* misconfiguration an operator must fix in `/admin/compute`, not a bug. Routes
|
|
12
|
+
* map this to 503 instead of letting the pure helper's plain `Error` surface as
|
|
13
|
+
* a generic 500.
|
|
14
|
+
*/
|
|
15
|
+
export class ComputeServerUnconfiguredError extends Error {
|
|
16
|
+
constructor(message = 'No compute server configured. Ask an admin to add one in /admin/compute.') {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'ComputeServerUnconfiguredError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/compute/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,MAAM,OAAO,8BAA+B,SAAQ,KAAK;IACxD,YACC,OAAO,GAAG,0EAA0E;QAEpF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gCAAgC,CAAC;IAC9C,CAAC;CACD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A warm compute client caches on server `id`, so a rotated `serverUrl`/`apiKey`
|
|
3
|
+
* keeps the same cache key with stale connection details — it never ages out on
|
|
4
|
+
* its own. The config-write path has to evict explicitly.
|
|
5
|
+
*
|
|
6
|
+
* The eviction sink is a parameter rather than an import: which cache holds the
|
|
7
|
+
* warm clients is the host's decision, and importing one would drag a whole
|
|
8
|
+
* solve engine into every consumer that only wants the diff.
|
|
9
|
+
*/
|
|
10
|
+
/** The connection-identifying fields — a change in either makes the warm client stale. */
|
|
11
|
+
export interface ServerConnection {
|
|
12
|
+
id: string;
|
|
13
|
+
serverUrl: string;
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Drop the warm client for every server that was removed or whose connection
|
|
18
|
+
* details changed. `prev` and `next` are the scoped server sets before and
|
|
19
|
+
* after the write; unchanged servers are left alone.
|
|
20
|
+
*/
|
|
21
|
+
export declare function evictChangedServers(prev: readonly ServerConnection[], next: readonly ServerConnection[], evict: (id: string) => void): void;
|
|
22
|
+
//# sourceMappingURL=evict-changed-servers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evict-changed-servers.d.ts","sourceRoot":"","sources":["../../src/compute/evict-changed-servers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,0FAA0F;AAC1F,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GACzB,IAAI,CAgBN"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A warm compute client caches on server `id`, so a rotated `serverUrl`/`apiKey`
|
|
3
|
+
* keeps the same cache key with stale connection details — it never ages out on
|
|
4
|
+
* its own. The config-write path has to evict explicitly.
|
|
5
|
+
*
|
|
6
|
+
* The eviction sink is a parameter rather than an import: which cache holds the
|
|
7
|
+
* warm clients is the host's decision, and importing one would drag a whole
|
|
8
|
+
* solve engine into every consumer that only wants the diff.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Drop the warm client for every server that was removed or whose connection
|
|
12
|
+
* details changed. `prev` and `next` are the scoped server sets before and
|
|
13
|
+
* after the write; unchanged servers are left alone.
|
|
14
|
+
*/
|
|
15
|
+
export function evictChangedServers(prev, next, evict) {
|
|
16
|
+
const nextById = new Map(next.map((s) => [s.id, s]));
|
|
17
|
+
// Removed: no longer in `next`.
|
|
18
|
+
for (const before of prev) {
|
|
19
|
+
if (!nextById.has(before.id))
|
|
20
|
+
evict(before.id);
|
|
21
|
+
}
|
|
22
|
+
// Rotated: same id, different URL or key.
|
|
23
|
+
const prevById = new Map(prev.map((s) => [s.id, s]));
|
|
24
|
+
for (const after of next) {
|
|
25
|
+
const before = prevById.get(after.id);
|
|
26
|
+
if (before && (before.serverUrl !== after.serverUrl || before.apiKey !== after.apiKey)) {
|
|
27
|
+
evict(after.id);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=evict-changed-servers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evict-changed-servers.js","sourceRoot":"","sources":["../../src/compute/evict-changed-servers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAClC,IAAiC,EACjC,IAAiC,EACjC,KAA2B;IAE3B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAErD,gCAAgC;IAChC,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAAE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,0CAA0C;IAC1C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACxF,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;IACF,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HTTP layer over {@link createIdempotencyStore}: how a replayed response
|
|
3
|
+
* is keyed, snapshotted, and identified on the wire.
|
|
4
|
+
*
|
|
5
|
+
* The store itself is generic over `T` and knows nothing about requests. This
|
|
6
|
+
* is the part two hosts must agree on to implement the same `Idempotency-Key`
|
|
7
|
+
* contract, and the part each would otherwise reinvent:
|
|
8
|
+
*
|
|
9
|
+
* - **The key namespaces by caller.** `Idempotency-Key` is client-chosen, so
|
|
10
|
+
* two tenants can pick the same string. Without the caller in the key one
|
|
11
|
+
* would replay the other's result — that is the whole security property
|
|
12
|
+
* here, and a host that got it wrong would leak across tenants with
|
|
13
|
+
* nothing failing at build time.
|
|
14
|
+
* - **The response round-trips through a snapshot.** A `Response` body reads
|
|
15
|
+
* exactly once, so storing the object itself replays an empty body.
|
|
16
|
+
* - **A replay says so**, via `Idempotency-Replayed`. A client that cannot
|
|
17
|
+
* tell a replay from a fresh run cannot tell a successful retry from a
|
|
18
|
+
* second execution.
|
|
19
|
+
*
|
|
20
|
+
* How long a response stays replayable is *not* here: that trades a client's
|
|
21
|
+
* retry window against serving a stale result after the underlying resource
|
|
22
|
+
* moves, and only the host knows its own deadlines.
|
|
23
|
+
*/
|
|
24
|
+
/** Header stamped on a replayed response, so a client can distinguish one. */
|
|
25
|
+
export declare const IDEMPOTENCY_REPLAYED_HEADER = "Idempotency-Replayed";
|
|
26
|
+
/**
|
|
27
|
+
* A response flattened into something storable. Kept instead of the `Response`
|
|
28
|
+
* because a body can only be read once.
|
|
29
|
+
*/
|
|
30
|
+
export interface StoredResponse {
|
|
31
|
+
status: number;
|
|
32
|
+
headers: [string, string][];
|
|
33
|
+
body: ArrayBuffer;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Namespace a client-chosen key by its caller.
|
|
37
|
+
*
|
|
38
|
+
* `callerId` should be the narrowest identity available — a token id rather
|
|
39
|
+
* than a user id where the host issues per-token credentials, so two tokens
|
|
40
|
+
* held by one user do not share replays.
|
|
41
|
+
*/
|
|
42
|
+
export declare function idempotencyKey(callerId: string, clientKey: string): string;
|
|
43
|
+
/** Flatten a response so it can be replayed. Consumes the body. */
|
|
44
|
+
export declare function toStoredResponse(res: Response): Promise<StoredResponse>;
|
|
45
|
+
/** Rebuild a live `Response` from a snapshot, marking it when it is a replay. */
|
|
46
|
+
export declare function fromStoredResponse(stored: StoredResponse, replayed: boolean): Response;
|
|
47
|
+
//# sourceMappingURL=idempotency-http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotency-http.d.ts","sourceRoot":"","sources":["../../src/compute/idempotency-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,2BAA2B,yBAAyB,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,WAAW,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED,mEAAmE;AACnE,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,CAM7E;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,GAAG,QAAQ,CAItF"}
|