stitchkit 0.80.0 → 0.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-runtime/provider-failure.d.ts.map +1 -1
- package/dist/agent-runtime-coding-tools.js +1 -1
- package/dist/agent-runtime-harness.js +7 -5
- package/dist/agent-runtime.js +15 -19
- package/dist/application/notification-outbox.d.ts +80 -0
- package/dist/application/notification-outbox.d.ts.map +1 -0
- package/dist/application/process-lifecycle.d.ts +166 -0
- package/dist/application/process-lifecycle.d.ts.map +1 -0
- package/dist/application/revision-signal.d.ts +86 -0
- package/dist/application/revision-signal.d.ts.map +1 -0
- package/dist/application/state-store.d.ts +18 -0
- package/dist/application/state-store.d.ts.map +1 -0
- package/dist/application.d.ts +4 -0
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +725 -1
- package/dist/browser/http.d.ts +14 -0
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/browser/resumable.d.ts +6 -0
- package/dist/browser/resumable.d.ts.map +1 -1
- package/dist/cli.js +9 -7
- package/dist/contract/define.d.ts +27 -4
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/errors-factory.d.ts +22 -3
- package/dist/contract/errors-factory.d.ts.map +1 -1
- package/dist/contract/index.d.ts +2 -2
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +1 -1
- package/dist/geo/index.d.ts +4 -0
- package/dist/geo/index.d.ts.map +1 -0
- package/dist/geo/maxmind.d.ts +4 -0
- package/dist/geo/maxmind.d.ts.map +1 -0
- package/dist/geo/resolver.d.ts +9 -0
- package/dist/geo/resolver.d.ts.map +1 -0
- package/dist/geo/types.d.ts +59 -0
- package/dist/geo/types.d.ts.map +1 -0
- package/dist/geo.d.ts +3 -0
- package/dist/geo.d.ts.map +1 -0
- package/dist/geo.js +245 -0
- package/dist/{index-9s6z8vgf.js → index-1dd82z59.js} +1 -1
- package/dist/{index-vwp69871.js → index-27487ay5.js} +4 -4
- package/dist/{index-7ag81v2f.js → index-27gm3d6g.js} +10 -321
- package/dist/{index-y01f4223.js → index-2bxcmqkb.js} +127 -43
- package/dist/{index-dxesg10d.js → index-3ds42w1w.js} +3 -3
- package/dist/index-3xwxfj4z.js +334 -0
- package/dist/{index-egaabxkz.js → index-5z8tezpx.js} +2 -2
- package/dist/{index-fxz0v7vg.js → index-7evdsyez.js} +1 -83
- package/dist/{index-xcetq6j3.js → index-90d7h2cr.js} +32 -13
- package/dist/{index-t0z5p5w6.js → index-apn8repm.js} +1 -1
- package/dist/{index-758zx2ha.js → index-dggwnnjv.js} +2 -2
- package/dist/{index-dwf1f2ap.js → index-f9mb610r.js} +1 -62
- package/dist/{index-5mtc969c.js → index-fwbkp2jn.js} +1 -1
- package/dist/index-p13mwz16.js +16 -0
- package/dist/{index-m9kzax52.js → index-p18f59zy.js} +21 -5
- package/dist/{index-hb5pf3s4.js → index-qmgs7y68.js} +1 -1
- package/dist/{index-2rwwmr7v.js → index-s4ckmrac.js} +1 -1
- package/dist/{index-h4pj6fta.js → index-s8cnx02s.js} +8 -5
- package/dist/{index-k9ay6v6d.js → index-vchzar7e.js} +74 -23
- package/dist/index-vsbzgd7b.js +84 -0
- package/dist/{index-dxjjv399.js → index-xbppf54a.js} +95 -15
- package/dist/index-y91ry9h2.js +4 -0
- package/dist/index-zcgf3gqf.js +83 -0
- package/dist/index.js +17 -13
- package/dist/internal/error-message.d.ts +3 -0
- package/dist/internal/error-message.d.ts.map +1 -0
- package/dist/internal/media-type.d.ts +8 -0
- package/dist/internal/media-type.d.ts.map +1 -0
- package/dist/internal/route-pattern.d.ts +21 -0
- package/dist/internal/route-pattern.d.ts.map +1 -1
- package/dist/node.js +9 -6
- package/dist/observability/audit.d.ts +28 -1
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/bounded-logger.d.ts +20 -0
- package/dist/observability/bounded-logger.d.ts.map +1 -0
- package/dist/observability/context.d.ts +12 -3
- package/dist/observability/context.d.ts.map +1 -1
- package/dist/observability/index.d.ts +3 -2
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +138 -8
- package/dist/observability/sanitize.d.ts +17 -0
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/primitives.js +1 -1
- package/dist/react/query-client.d.ts +33 -0
- package/dist/react/query-client.d.ts.map +1 -0
- package/dist/react.d.ts +1 -0
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +83 -1
- package/dist/realtime/registry.d.ts +93 -0
- package/dist/realtime/registry.d.ts.map +1 -0
- package/dist/release/header.d.ts +9 -0
- package/dist/release/header.d.ts.map +1 -0
- package/dist/release/marker.d.ts +39 -0
- package/dist/release/marker.d.ts.map +1 -0
- package/dist/release/socket.d.ts +44 -0
- package/dist/release/socket.d.ts.map +1 -0
- package/dist/release/watcher.d.ts +66 -0
- package/dist/release/watcher.d.ts.map +1 -0
- package/dist/release.d.ts +15 -0
- package/dist/release.d.ts.map +1 -0
- package/dist/release.js +196 -0
- package/dist/remote.js +12 -7
- package/dist/server/context.d.ts +2 -1
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +17 -16
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/file-state-store.d.ts +23 -0
- package/dist/server/file-state-store.d.ts.map +1 -0
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.d.ts +5 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +469 -11
- package/dist/server/middleware/cookies.d.ts +33 -1
- package/dist/server/middleware/cookies.d.ts.map +1 -1
- package/dist/server/middleware/cors.d.ts +16 -1
- package/dist/server/middleware/cors.d.ts.map +1 -1
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/release-signal.d.ts +35 -0
- package/dist/server/release-signal.d.ts.map +1 -0
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/types.d.ts +26 -2
- package/dist/server/types.d.ts.map +1 -1
- package/dist/testing/surface-manifest.d.ts.map +1 -1
- package/dist/testing.js +7 -4
- package/dist/tool-invoker.js +8 -6
- package/dist/tools/download-core.d.ts.map +1 -1
- package/dist/tools/remote.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools.js +20 -16
- package/dist/tracking/attribution.d.ts +51 -0
- package/dist/tracking/attribution.d.ts.map +1 -0
- package/dist/tracking/beacon.d.ts +14 -0
- package/dist/tracking/beacon.d.ts.map +1 -0
- package/dist/tracking/clicks.d.ts +46 -0
- package/dist/tracking/clicks.d.ts.map +1 -0
- package/dist/tracking/client.d.ts +121 -0
- package/dist/tracking/client.d.ts.map +1 -0
- package/dist/tracking/contract.d.ts +46 -0
- package/dist/tracking/contract.d.ts.map +1 -0
- package/dist/tracking/delivery.d.ts +24 -0
- package/dist/tracking/delivery.d.ts.map +1 -0
- package/dist/tracking/host.d.ts +43 -0
- package/dist/tracking/host.d.ts.map +1 -0
- package/dist/tracking/once-per-page.d.ts +17 -0
- package/dist/tracking/once-per-page.d.ts.map +1 -0
- package/dist/tracking/outbox-storage-indexeddb.d.ts +12 -0
- package/dist/tracking/outbox-storage-indexeddb.d.ts.map +1 -0
- package/dist/tracking/outbox-storage-memory.d.ts +12 -0
- package/dist/tracking/outbox-storage-memory.d.ts.map +1 -0
- package/dist/tracking/outbox.d.ts +98 -0
- package/dist/tracking/outbox.d.ts.map +1 -0
- package/dist/tracking/schemas.d.ts +183 -0
- package/dist/tracking/schemas.d.ts.map +1 -0
- package/dist/tracking/scroll.d.ts +22 -0
- package/dist/tracking/scroll.d.ts.map +1 -0
- package/dist/tracking/sequence-reserve.d.ts +34 -0
- package/dist/tracking/sequence-reserve.d.ts.map +1 -0
- package/dist/tracking/server/active-interval.d.ts +26 -0
- package/dist/tracking/server/active-interval.d.ts.map +1 -0
- package/dist/tracking/server/bot.d.ts +9 -0
- package/dist/tracking/server/bot.d.ts.map +1 -0
- package/dist/tracking/server/disposition.d.ts +55 -0
- package/dist/tracking/server/disposition.d.ts.map +1 -0
- package/dist/tracking/server/hash.d.ts +13 -0
- package/dist/tracking/server/hash.d.ts.map +1 -0
- package/dist/tracking/server/presence.d.ts +27 -0
- package/dist/tracking/server/presence.d.ts.map +1 -0
- package/dist/tracking/server/visit-lease.d.ts +92 -0
- package/dist/tracking/server/visit-lease.d.ts.map +1 -0
- package/dist/tracking/visible-time.d.ts +38 -0
- package/dist/tracking/visible-time.d.ts.map +1 -0
- package/dist/tracking-server.d.ts +14 -0
- package/dist/tracking-server.d.ts.map +1 -0
- package/dist/tracking-server.js +173 -0
- package/dist/tracking.d.ts +24 -0
- package/dist/tracking.d.ts.map +1 -0
- package/dist/tracking.js +1090 -0
- package/llms-full.txt +986 -30
- package/llms.txt +4 -0
- package/package.json +26 -5
package/dist/server/index.js
CHANGED
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
sseRoute,
|
|
14
14
|
streamingRoute,
|
|
15
15
|
webSocketLane
|
|
16
|
-
} from "../index-
|
|
16
|
+
} from "../index-vchzar7e.js";
|
|
17
|
+
import"../index-y91ry9h2.js";
|
|
17
18
|
import {
|
|
18
19
|
withDeadline
|
|
19
20
|
} from "../index-b9aefrqd.js";
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
ShutdownStatusSchema,
|
|
25
26
|
createServerLifecycle
|
|
26
27
|
} from "../index-3z73fh2c.js";
|
|
27
|
-
import"../index-
|
|
28
|
+
import"../index-apn8repm.js";
|
|
28
29
|
import"../index-2qzskm8h.js";
|
|
29
30
|
import {
|
|
30
31
|
composeAuthHooks,
|
|
@@ -33,12 +34,13 @@ import {
|
|
|
33
34
|
defineCookie,
|
|
34
35
|
deriveCodeChallenge,
|
|
35
36
|
extractToken,
|
|
37
|
+
parseCookieHeader,
|
|
36
38
|
parseCookies,
|
|
37
39
|
serializeCookie,
|
|
38
40
|
signJwt,
|
|
39
41
|
verifyJwt,
|
|
40
42
|
verifyPkce
|
|
41
|
-
} from "../index-
|
|
43
|
+
} from "../index-90d7h2cr.js";
|
|
42
44
|
import {
|
|
43
45
|
DEFAULT_CORS_ALLOW_HEADERS,
|
|
44
46
|
DEFAULT_CORS_EXPOSE_HEADERS,
|
|
@@ -52,20 +54,22 @@ import {
|
|
|
52
54
|
defineMultipartStream,
|
|
53
55
|
implement,
|
|
54
56
|
implementRegistry
|
|
55
|
-
} from "../index-
|
|
57
|
+
} from "../index-p18f59zy.js";
|
|
56
58
|
import {
|
|
57
59
|
isWithinDir,
|
|
58
60
|
realPathWithinDir
|
|
59
61
|
} from "../index-sbdmyz75.js";
|
|
62
|
+
import {
|
|
63
|
+
getTraceId
|
|
64
|
+
} from "../index-vsbzgd7b.js";
|
|
60
65
|
import {
|
|
61
66
|
extractIp,
|
|
62
67
|
generateTraceId,
|
|
63
68
|
getClientInfo,
|
|
64
|
-
getTraceId,
|
|
65
69
|
isPublicIp,
|
|
66
70
|
resolveSocketIp,
|
|
67
71
|
resolveTraceId
|
|
68
|
-
} from "../index-
|
|
72
|
+
} from "../index-f9mb610r.js";
|
|
69
73
|
import {
|
|
70
74
|
jsonSchemaFields,
|
|
71
75
|
toJsonSchema
|
|
@@ -74,18 +78,18 @@ import {
|
|
|
74
78
|
inputIsQuery,
|
|
75
79
|
parseSSE,
|
|
76
80
|
streamSSE
|
|
77
|
-
} from "../index-
|
|
81
|
+
} from "../index-qmgs7y68.js";
|
|
78
82
|
import {
|
|
79
83
|
errorCode,
|
|
80
84
|
formatZodError,
|
|
81
85
|
normalizeError,
|
|
82
86
|
zodIssues
|
|
83
|
-
} from "../index-
|
|
87
|
+
} from "../index-7evdsyez.js";
|
|
84
88
|
import {
|
|
85
89
|
DEFAULT_CONTRACT_STREAM_FRAME_BYTES,
|
|
86
90
|
joinRoutePath,
|
|
87
91
|
parseTrailingWildcard
|
|
88
|
-
} from "../index-
|
|
92
|
+
} from "../index-2bxcmqkb.js";
|
|
89
93
|
import"../index-6k1937bx.js";
|
|
90
94
|
import {
|
|
91
95
|
AppError,
|
|
@@ -99,10 +103,223 @@ import {
|
|
|
99
103
|
rateLimited,
|
|
100
104
|
unauthorized
|
|
101
105
|
} from "../index-0w9abg87.js";
|
|
106
|
+
import"../index-zcgf3gqf.js";
|
|
102
107
|
import"../index-kzfs85xp.js";
|
|
103
108
|
import {
|
|
104
109
|
isRecord
|
|
105
110
|
} from "../index-77fekveh.js";
|
|
111
|
+
// src/realtime/registry.ts
|
|
112
|
+
var authorizedRoom = Symbol("stitchkit.authorized-room");
|
|
113
|
+
function freezeRoom(name) {
|
|
114
|
+
return Object.freeze({ name, [authorizedRoom]: true });
|
|
115
|
+
}
|
|
116
|
+
function bindSocketRegistry(server, options) {
|
|
117
|
+
const members = new Map;
|
|
118
|
+
const minted = new WeakSet;
|
|
119
|
+
let revision = 0;
|
|
120
|
+
let closed = false;
|
|
121
|
+
const setups = new Set;
|
|
122
|
+
const maxBufferedFrames = Math.max(1, options.maxBufferedFrames ?? 1000);
|
|
123
|
+
const token = (name) => {
|
|
124
|
+
const created = freezeRoom(name);
|
|
125
|
+
minted.add(created);
|
|
126
|
+
return created;
|
|
127
|
+
};
|
|
128
|
+
const assertToken = (room) => {
|
|
129
|
+
if (!minted.has(room))
|
|
130
|
+
throw new Error("Room was not authorized by this registry");
|
|
131
|
+
return room.name;
|
|
132
|
+
};
|
|
133
|
+
const leaveQuietly = async (member, room) => {
|
|
134
|
+
try {
|
|
135
|
+
await member.connection.raw.leave(room);
|
|
136
|
+
} catch {}
|
|
137
|
+
};
|
|
138
|
+
const remove = async (member) => {
|
|
139
|
+
if (member.disconnected)
|
|
140
|
+
return;
|
|
141
|
+
member.disconnected = true;
|
|
142
|
+
member.connection.raw.off?.("disconnect", member.disconnect);
|
|
143
|
+
members.delete(member.connection.raw.id);
|
|
144
|
+
for (const room of member.joined)
|
|
145
|
+
await leaveQuietly(member, room);
|
|
146
|
+
member.joined.clear();
|
|
147
|
+
member.buffer = null;
|
|
148
|
+
revision += 1;
|
|
149
|
+
};
|
|
150
|
+
const replayMember = async (member) => {
|
|
151
|
+
if (!options.replay || member.disconnected || closed)
|
|
152
|
+
return !member.disconnected && !closed;
|
|
153
|
+
const attempts = Math.max(1, options.replayAttempts ?? 3);
|
|
154
|
+
for (let attempt = 0;attempt < attempts && !member.disconnected && !closed; attempt += 1) {
|
|
155
|
+
member.buffer = [];
|
|
156
|
+
member.overflow = false;
|
|
157
|
+
const before = await options.revision?.();
|
|
158
|
+
if (member.disconnected || closed)
|
|
159
|
+
break;
|
|
160
|
+
const frames = await options.replay({
|
|
161
|
+
identity: member.identity,
|
|
162
|
+
rooms: Object.freeze([...member.joined].map(token)),
|
|
163
|
+
revision: before
|
|
164
|
+
});
|
|
165
|
+
const after = await options.revision?.();
|
|
166
|
+
if (member.disconnected || closed)
|
|
167
|
+
break;
|
|
168
|
+
if (options.revision && !Object.is(before, after))
|
|
169
|
+
continue;
|
|
170
|
+
if (member.overflow)
|
|
171
|
+
continue;
|
|
172
|
+
for (const frame of frames)
|
|
173
|
+
member.connection.events.emit(frame.event, ...frame.args);
|
|
174
|
+
const buffered = member.buffer;
|
|
175
|
+
member.buffer = null;
|
|
176
|
+
for (const deferred of buffered ?? [])
|
|
177
|
+
deferred();
|
|
178
|
+
return !member.disconnected && !closed;
|
|
179
|
+
}
|
|
180
|
+
member.buffer = null;
|
|
181
|
+
member.overflow = false;
|
|
182
|
+
if (!member.disconnected)
|
|
183
|
+
await options.onResyncRequired?.(member.connection.raw.id, member.identity);
|
|
184
|
+
return false;
|
|
185
|
+
};
|
|
186
|
+
const resyncMember = (member) => {
|
|
187
|
+
if (member.replayPromise)
|
|
188
|
+
return member.replayPromise;
|
|
189
|
+
const active = replayMember(member).finally(() => {
|
|
190
|
+
if (member.replayPromise === active)
|
|
191
|
+
member.replayPromise = undefined;
|
|
192
|
+
});
|
|
193
|
+
member.replayPromise = active;
|
|
194
|
+
return active;
|
|
195
|
+
};
|
|
196
|
+
const setupConnection = async (connection) => {
|
|
197
|
+
if (closed)
|
|
198
|
+
return;
|
|
199
|
+
const member = {
|
|
200
|
+
connection,
|
|
201
|
+
identity: connection.raw.data,
|
|
202
|
+
authorized: new Set,
|
|
203
|
+
joined: new Set,
|
|
204
|
+
buffer: null,
|
|
205
|
+
overflow: false,
|
|
206
|
+
disconnected: false,
|
|
207
|
+
disconnect: () => {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
member.disconnect = () => void remove(member);
|
|
212
|
+
connection.raw.on("disconnect", member.disconnect);
|
|
213
|
+
members.set(connection.raw.id, member);
|
|
214
|
+
try {
|
|
215
|
+
const authorized = new Set(await options.rooms(connection.raw.data));
|
|
216
|
+
if (member.disconnected || closed) {
|
|
217
|
+
await remove(member);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
member.authorized = authorized;
|
|
221
|
+
for (const name of authorized) {
|
|
222
|
+
await connection.raw.join(name);
|
|
223
|
+
if (member.disconnected || closed) {
|
|
224
|
+
await leaveQuietly(member, name);
|
|
225
|
+
await remove(member);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
member.joined.add(name);
|
|
229
|
+
}
|
|
230
|
+
if (closed) {
|
|
231
|
+
await remove(member);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
revision += 1;
|
|
235
|
+
await resyncMember(member);
|
|
236
|
+
} catch {
|
|
237
|
+
await remove(member);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const stopListening = server.onConnection((connection) => {
|
|
241
|
+
let setup;
|
|
242
|
+
setup = setupConnection(connection).finally(() => setups.delete(setup));
|
|
243
|
+
setups.add(setup);
|
|
244
|
+
return setup;
|
|
245
|
+
});
|
|
246
|
+
return {
|
|
247
|
+
room: token,
|
|
248
|
+
async join(socketId, room) {
|
|
249
|
+
const name = assertToken(room);
|
|
250
|
+
const member = members.get(socketId);
|
|
251
|
+
if (!member)
|
|
252
|
+
throw new Error(`Unknown socket: ${socketId}`);
|
|
253
|
+
if (!member.authorized.has(name))
|
|
254
|
+
throw new Error(`Socket is not authorized for room: ${name}`);
|
|
255
|
+
if (member.joined.has(name))
|
|
256
|
+
return;
|
|
257
|
+
await member.connection.raw.join(name);
|
|
258
|
+
if (!member.disconnected)
|
|
259
|
+
member.joined.add(name);
|
|
260
|
+
revision += 1;
|
|
261
|
+
},
|
|
262
|
+
async leave(socketId, room) {
|
|
263
|
+
const name = assertToken(room);
|
|
264
|
+
const member = members.get(socketId);
|
|
265
|
+
if (!member?.joined.has(name))
|
|
266
|
+
return;
|
|
267
|
+
await member.connection.raw.leave(name);
|
|
268
|
+
member.joined.delete(name);
|
|
269
|
+
revision += 1;
|
|
270
|
+
},
|
|
271
|
+
emitTo(room, event, ...args) {
|
|
272
|
+
const name = assertToken(room);
|
|
273
|
+
let recipients = 0;
|
|
274
|
+
for (const member of members.values()) {
|
|
275
|
+
if (!member.joined.has(name) || member.disconnected)
|
|
276
|
+
continue;
|
|
277
|
+
recipients += 1;
|
|
278
|
+
if (!member.buffer) {
|
|
279
|
+
member.connection.events.emit(event, ...args);
|
|
280
|
+
} else if (member.buffer.length >= maxBufferedFrames) {
|
|
281
|
+
member.overflow = true;
|
|
282
|
+
} else {
|
|
283
|
+
member.buffer.push(() => member.connection.events.emit(event, ...args));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return recipients;
|
|
287
|
+
},
|
|
288
|
+
resync: async (socketId) => {
|
|
289
|
+
const member = members.get(socketId);
|
|
290
|
+
return member ? resyncMember(member) : false;
|
|
291
|
+
},
|
|
292
|
+
snapshot() {
|
|
293
|
+
const connections = [...members.values()].map((member) => Object.freeze({
|
|
294
|
+
socketId: member.connection.raw.id,
|
|
295
|
+
identity: member.identity,
|
|
296
|
+
rooms: Object.freeze([...member.joined].sort()),
|
|
297
|
+
replaying: member.buffer !== null
|
|
298
|
+
}));
|
|
299
|
+
const roomMap = {};
|
|
300
|
+
for (const entry of connections) {
|
|
301
|
+
for (const name of entry.rooms) {
|
|
302
|
+
roomMap[name] = Object.freeze([...roomMap[name] ?? [], entry.socketId]);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return Object.freeze({
|
|
306
|
+
revision,
|
|
307
|
+
connections: Object.freeze(connections),
|
|
308
|
+
rooms: Object.freeze(roomMap)
|
|
309
|
+
});
|
|
310
|
+
},
|
|
311
|
+
async unbind() {
|
|
312
|
+
if (closed)
|
|
313
|
+
return;
|
|
314
|
+
closed = true;
|
|
315
|
+
stopListening();
|
|
316
|
+
await Promise.allSettled([...setups]);
|
|
317
|
+
const active = [...members.values()];
|
|
318
|
+
for (const member of active)
|
|
319
|
+
await remove(member);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
}
|
|
106
323
|
// src/server/bun.ts
|
|
107
324
|
import { chmodSync, statSync, unlinkSync } from "node:fs";
|
|
108
325
|
function reclaimStaleUnixSocket(path) {
|
|
@@ -389,10 +606,15 @@ function cacheHeaders(maxAge, scope = "public") {
|
|
|
389
606
|
}
|
|
390
607
|
// src/server/error-hook.ts
|
|
391
608
|
function createErrorHook(config) {
|
|
609
|
+
if (config.vocabulary && (config.codeMap !== undefined || config.unmappedCode !== undefined)) {
|
|
610
|
+
throw new Error("[stitchkit] createErrorHook: `vocabulary` carries the wire map — it cannot be combined with `codeMap` or `unmappedCode`");
|
|
611
|
+
}
|
|
392
612
|
return async (ctx, error, endpoint) => {
|
|
393
613
|
const appErr = normalizeError(error);
|
|
394
614
|
const stitchCode = isStitchErrorCode(appErr.code) ? appErr.code : undefined;
|
|
395
|
-
const
|
|
615
|
+
const vocabularyMap = config.vocabulary?.codeMap;
|
|
616
|
+
const configuredMap = vocabularyMap ?? config.codeMap;
|
|
617
|
+
const mapped = stitchCode === undefined ? undefined : configuredMap?.[stitchCode];
|
|
396
618
|
const fallback = mapped === undefined && stitchCode !== undefined ? typeof config.unmappedCode === "function" ? config.unmappedCode(stitchCode) : config.unmappedCode : undefined;
|
|
397
619
|
const code = mapped ?? fallback ?? appErr.code;
|
|
398
620
|
const info = {
|
|
@@ -775,6 +997,221 @@ async function serveFile(req, opts) {
|
|
|
775
997
|
base.set("Content-Length", String(size));
|
|
776
998
|
return new Response(isHead ? null : file, { status: 200, headers: base });
|
|
777
999
|
}
|
|
1000
|
+
// src/server/file-state-store.ts
|
|
1001
|
+
import { mkdir, open, readdir, readFile as readFile2, rename, stat as stat2, unlink } from "node:fs/promises";
|
|
1002
|
+
import { basename, dirname, join } from "node:path";
|
|
1003
|
+
var ABANDONED_LOCK_MULTIPLIER = 10;
|
|
1004
|
+
var ABANDONED_HEARTBEATS = 30;
|
|
1005
|
+
var MIN_HEARTBEAT_MS = 10;
|
|
1006
|
+
var delay = (milliseconds) => new Promise((resolve2) => setTimeout(resolve2, milliseconds));
|
|
1007
|
+
async function missing(error) {
|
|
1008
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
1009
|
+
}
|
|
1010
|
+
function existing(error) {
|
|
1011
|
+
return error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
1012
|
+
}
|
|
1013
|
+
async function ownerState(lockPath) {
|
|
1014
|
+
let owner;
|
|
1015
|
+
try {
|
|
1016
|
+
owner = JSON.parse(await readFile2(lockPath, "utf8"));
|
|
1017
|
+
} catch {
|
|
1018
|
+
return "unknown";
|
|
1019
|
+
}
|
|
1020
|
+
if (typeof owner !== "object" || owner === null || !("pid" in owner) || typeof owner.pid !== "number" || !Number.isSafeInteger(owner.pid) || owner.pid <= 0) {
|
|
1021
|
+
return "unknown";
|
|
1022
|
+
}
|
|
1023
|
+
try {
|
|
1024
|
+
process.kill(owner.pid, 0);
|
|
1025
|
+
return "alive";
|
|
1026
|
+
} catch (error) {
|
|
1027
|
+
return error instanceof Error && "code" in error && error.code === "ESRCH" ? "gone" : "unknown";
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
async function sweepTemporaries(path, staleLockMs) {
|
|
1031
|
+
const prefix = `${basename(path)}.tmp.`;
|
|
1032
|
+
const directory = dirname(path);
|
|
1033
|
+
let entries;
|
|
1034
|
+
try {
|
|
1035
|
+
entries = await readdir(directory);
|
|
1036
|
+
} catch {
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
for (const entry of entries) {
|
|
1040
|
+
if (!entry.startsWith(prefix))
|
|
1041
|
+
continue;
|
|
1042
|
+
const candidate = join(directory, entry);
|
|
1043
|
+
const info = await stat2(candidate).catch(() => null);
|
|
1044
|
+
if (info && Date.now() - info.mtimeMs > staleLockMs) {
|
|
1045
|
+
await unlink(candidate).catch(() => {
|
|
1046
|
+
return;
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
async function reclaim(lockPath, observed, staleLockMs, abandonedAfterMs) {
|
|
1052
|
+
const guardPath = `${lockPath}.reclaim`;
|
|
1053
|
+
let guard;
|
|
1054
|
+
try {
|
|
1055
|
+
guard = await open(guardPath, "wx");
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
if (!existing(error))
|
|
1058
|
+
throw error;
|
|
1059
|
+
const orphan = await stat2(guardPath).catch(() => null);
|
|
1060
|
+
if (orphan && Date.now() - orphan.mtimeMs > staleLockMs) {
|
|
1061
|
+
await unlink(guardPath).catch(() => {
|
|
1062
|
+
return;
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
return false;
|
|
1066
|
+
}
|
|
1067
|
+
try {
|
|
1068
|
+
const current = await stat2(lockPath).catch(() => null);
|
|
1069
|
+
if (!current || current.dev !== observed.dev || current.ino !== observed.ino || current.mtimeMs !== observed.mtimeMs) {
|
|
1070
|
+
return false;
|
|
1071
|
+
}
|
|
1072
|
+
const owner = await ownerState(lockPath);
|
|
1073
|
+
const age = Date.now() - current.mtimeMs;
|
|
1074
|
+
if (owner !== "gone" && age < abandonedAfterMs)
|
|
1075
|
+
return false;
|
|
1076
|
+
await unlink(lockPath);
|
|
1077
|
+
return true;
|
|
1078
|
+
} finally {
|
|
1079
|
+
await guard.close().catch(() => {
|
|
1080
|
+
return;
|
|
1081
|
+
});
|
|
1082
|
+
await unlink(guardPath).catch(() => {
|
|
1083
|
+
return;
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
function createFileStateStore(path, options) {
|
|
1088
|
+
const lockPath = `${path}.lock`;
|
|
1089
|
+
const lockTimeoutMs = options.lockTimeoutMs ?? 1e4;
|
|
1090
|
+
const staleLockMs = options.staleLockMs ?? 3000;
|
|
1091
|
+
const retryMs = options.retryMs ?? 10;
|
|
1092
|
+
const heartbeatMs = Math.max(MIN_HEARTBEAT_MS, Math.floor(staleLockMs / 3));
|
|
1093
|
+
const abandonedAfterMs = Math.max(staleLockMs * ABANDONED_LOCK_MULTIPLIER, heartbeatMs * ABANDONED_HEARTBEATS);
|
|
1094
|
+
if (staleLockMs >= lockTimeoutMs) {
|
|
1095
|
+
throw new Error(`[stitchkit] createFileStateStore: staleLockMs (${staleLockMs}) must be below lockTimeoutMs (${lockTimeoutMs}), or a crashed holder blocks every update until the timeout`);
|
|
1096
|
+
}
|
|
1097
|
+
let sweptTemporaries = false;
|
|
1098
|
+
const parseFile = async () => {
|
|
1099
|
+
let source;
|
|
1100
|
+
try {
|
|
1101
|
+
source = await readFile2(path, "utf8");
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
if (await missing(error))
|
|
1104
|
+
return null;
|
|
1105
|
+
throw error;
|
|
1106
|
+
}
|
|
1107
|
+
try {
|
|
1108
|
+
return options.schema.parse(JSON.parse(source));
|
|
1109
|
+
} catch (error) {
|
|
1110
|
+
await options.onCorrupt?.({ path, error });
|
|
1111
|
+
if ((options.corrupt ?? "throw") === "empty")
|
|
1112
|
+
return null;
|
|
1113
|
+
throw error;
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
const acquire = async () => {
|
|
1117
|
+
await mkdir(dirname(path), { recursive: true });
|
|
1118
|
+
if (!sweptTemporaries) {
|
|
1119
|
+
sweptTemporaries = true;
|
|
1120
|
+
await sweepTemporaries(path, staleLockMs);
|
|
1121
|
+
}
|
|
1122
|
+
const deadline = Date.now() + lockTimeoutMs;
|
|
1123
|
+
while (true) {
|
|
1124
|
+
try {
|
|
1125
|
+
const handle = await open(lockPath, "wx");
|
|
1126
|
+
const token = crypto.randomUUID();
|
|
1127
|
+
try {
|
|
1128
|
+
await handle.writeFile(JSON.stringify({ token, pid: process.pid, acquiredAt: Date.now() }));
|
|
1129
|
+
await handle.sync();
|
|
1130
|
+
} catch (error) {
|
|
1131
|
+
await handle.close().catch(() => {
|
|
1132
|
+
return;
|
|
1133
|
+
});
|
|
1134
|
+
await unlink(lockPath).catch(() => {
|
|
1135
|
+
return;
|
|
1136
|
+
});
|
|
1137
|
+
throw error;
|
|
1138
|
+
}
|
|
1139
|
+
return {
|
|
1140
|
+
refresh: async () => {
|
|
1141
|
+
const now = new Date;
|
|
1142
|
+
await handle.utimes(now, now);
|
|
1143
|
+
},
|
|
1144
|
+
release: async () => {
|
|
1145
|
+
await handle.close();
|
|
1146
|
+
try {
|
|
1147
|
+
const owner = JSON.parse(await readFile2(lockPath, "utf8"));
|
|
1148
|
+
if (isRecord(owner) && owner.token === token)
|
|
1149
|
+
await unlink(lockPath);
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
if (!await missing(error))
|
|
1152
|
+
throw error;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
if (!existing(error))
|
|
1158
|
+
throw error;
|
|
1159
|
+
try {
|
|
1160
|
+
const lock = await stat2(lockPath);
|
|
1161
|
+
if (Date.now() - lock.mtimeMs > staleLockMs) {
|
|
1162
|
+
if (await reclaim(lockPath, lock, staleLockMs, abandonedAfterMs))
|
|
1163
|
+
continue;
|
|
1164
|
+
}
|
|
1165
|
+
} catch (statError) {
|
|
1166
|
+
if (await missing(statError))
|
|
1167
|
+
continue;
|
|
1168
|
+
throw statError;
|
|
1169
|
+
}
|
|
1170
|
+
if (Date.now() >= deadline) {
|
|
1171
|
+
throw new Error(`[stitchkit] timed out acquiring state lock for ${path}`);
|
|
1172
|
+
}
|
|
1173
|
+
await delay(retryMs);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
const write = async (state) => {
|
|
1178
|
+
const validated = options.schema.parse(state);
|
|
1179
|
+
const temporary = `${path}.tmp.${process.pid}.${crypto.randomUUID()}`;
|
|
1180
|
+
const handle = await open(temporary, "wx");
|
|
1181
|
+
try {
|
|
1182
|
+
try {
|
|
1183
|
+
await handle.writeFile(`${JSON.stringify(validated)}
|
|
1184
|
+
`);
|
|
1185
|
+
await handle.sync();
|
|
1186
|
+
} finally {
|
|
1187
|
+
await handle.close();
|
|
1188
|
+
}
|
|
1189
|
+
await rename(temporary, path);
|
|
1190
|
+
} catch (error) {
|
|
1191
|
+
await unlink(temporary).catch(() => {
|
|
1192
|
+
return;
|
|
1193
|
+
});
|
|
1194
|
+
throw error;
|
|
1195
|
+
}
|
|
1196
|
+
};
|
|
1197
|
+
return {
|
|
1198
|
+
read: parseFile,
|
|
1199
|
+
async update(transition) {
|
|
1200
|
+
const lock = await acquire();
|
|
1201
|
+
const heartbeat = setInterval(() => void lock.refresh().catch(() => {
|
|
1202
|
+
return;
|
|
1203
|
+
}), heartbeatMs);
|
|
1204
|
+
try {
|
|
1205
|
+
const next = await transition(await parseFile());
|
|
1206
|
+
await write(next.state);
|
|
1207
|
+
return next.result;
|
|
1208
|
+
} finally {
|
|
1209
|
+
clearInterval(heartbeat);
|
|
1210
|
+
await lock.release();
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
778
1215
|
// src/server/lifecycle.ts
|
|
779
1216
|
function composeLifecycleHooks(...hooks) {
|
|
780
1217
|
return {
|
|
@@ -1105,9 +1542,10 @@ function generateOpenApiDocument(config) {
|
|
|
1105
1542
|
};
|
|
1106
1543
|
} else if (method.inputSchema) {
|
|
1107
1544
|
const required = jsonSchemaFields(safeJson(method.inputSchema, "input")).some((f) => f.required);
|
|
1545
|
+
const schema = safeJson(method.inputSchema, "input");
|
|
1108
1546
|
operation.requestBody = {
|
|
1109
1547
|
required,
|
|
1110
|
-
content: { "application/json": { schema
|
|
1548
|
+
content: method.safelistedBody ? { "application/json": { schema }, "text/plain": { schema } } : { "application/json": { schema } }
|
|
1111
1549
|
};
|
|
1112
1550
|
}
|
|
1113
1551
|
}
|
|
@@ -1198,6 +1636,22 @@ async function parseBody(req, schema) {
|
|
|
1198
1636
|
const parsed = schema.safeParse(raw);
|
|
1199
1637
|
return parsed.success ? parsed.data : null;
|
|
1200
1638
|
}
|
|
1639
|
+
// src/server/release-signal.ts
|
|
1640
|
+
function bindReleaseRefreshSignal(marker, options = {}) {
|
|
1641
|
+
const signal = options.signal ?? "SIGUSR2";
|
|
1642
|
+
const source = options.source ?? process;
|
|
1643
|
+
const handler = () => {
|
|
1644
|
+
try {
|
|
1645
|
+
options.onRefresh?.(marker.refresh());
|
|
1646
|
+
} catch (error) {
|
|
1647
|
+
try {
|
|
1648
|
+
options.onError?.(error);
|
|
1649
|
+
} catch {}
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
source.on(signal, handler);
|
|
1653
|
+
return () => source.off(signal, handler);
|
|
1654
|
+
}
|
|
1201
1655
|
export {
|
|
1202
1656
|
zodIssues,
|
|
1203
1657
|
webSocketLane,
|
|
@@ -1220,6 +1674,7 @@ export {
|
|
|
1220
1674
|
parseSSE,
|
|
1221
1675
|
parseMultipart,
|
|
1222
1676
|
parseCookies,
|
|
1677
|
+
parseCookieHeader,
|
|
1223
1678
|
parseByteRange,
|
|
1224
1679
|
parseBody,
|
|
1225
1680
|
openApiRoute,
|
|
@@ -1255,6 +1710,7 @@ export {
|
|
|
1255
1710
|
createImplementRegistry,
|
|
1256
1711
|
createImplement,
|
|
1257
1712
|
createHandler,
|
|
1713
|
+
createFileStateStore,
|
|
1258
1714
|
createEventBus,
|
|
1259
1715
|
createErrorHook,
|
|
1260
1716
|
createCache,
|
|
@@ -1267,6 +1723,8 @@ export {
|
|
|
1267
1723
|
composeLifecycleHooks,
|
|
1268
1724
|
composeAuthHooks,
|
|
1269
1725
|
cacheHeaders,
|
|
1726
|
+
bindSocketRegistry,
|
|
1727
|
+
bindReleaseRefreshSignal,
|
|
1270
1728
|
bindRealtimeServer,
|
|
1271
1729
|
bindProcessSignals,
|
|
1272
1730
|
badRequest,
|
|
@@ -1,4 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every `name=value` pair of a `Cookie` header, in header order, duplicates
|
|
3
|
+
* kept. The order is the browser's (RFC 6265 §5.4: longer path first, then
|
|
4
|
+
* earlier creation time) and a server "SHOULD NOT rely upon" it (§4.2.2) — so
|
|
5
|
+
* this list is what a reader consults when it needs to see *all* candidates
|
|
6
|
+
* rather than the one the order happened to put last.
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseCookieHeader(header: string | null): Array<[string, string]>;
|
|
9
|
+
/**
|
|
10
|
+
* The `Cookie` header as a record. Two cookies with one name collapse to the
|
|
11
|
+
* **last** occurrence — a fact of the record shape, not a rule about cookies;
|
|
12
|
+
* `parseCookieHeader` keeps both, and `defineCookie` lets a handle declare
|
|
13
|
+
* which it wants.
|
|
14
|
+
*/
|
|
1
15
|
export declare function parseCookies(header: string | null): Record<string, string>;
|
|
16
|
+
/**
|
|
17
|
+
* What `get` answers when the request carries more than one cookie of this
|
|
18
|
+
* name. The browser decides their order, so `'last'` and `'first'` are each a
|
|
19
|
+
* coin the browser flips; they exist so an application can *name* the
|
|
20
|
+
* behaviour it relies on. `'reject'` refuses to flip it: two **different**
|
|
21
|
+
* values yield `undefined`, so a session reader sees "no session" instead of
|
|
22
|
+
* a session that works in one browser and not another. Two identical values
|
|
23
|
+
* are one value — a host cookie and a parent-domain cookie set by the same
|
|
24
|
+
* application — and are returned.
|
|
25
|
+
*/
|
|
26
|
+
export type CookieDuplicatesPolicy = 'last' | 'first' | 'reject';
|
|
2
27
|
export interface CookieOptions {
|
|
3
28
|
maxAge?: number;
|
|
4
29
|
path?: string;
|
|
@@ -6,12 +31,19 @@ export interface CookieOptions {
|
|
|
6
31
|
secure?: boolean;
|
|
7
32
|
httpOnly?: boolean;
|
|
8
33
|
sameSite?: 'Strict' | 'Lax' | 'None';
|
|
34
|
+
/** How `get` resolves two cookies of this name; default `'last'`. */
|
|
35
|
+
duplicates?: CookieDuplicatesPolicy;
|
|
9
36
|
}
|
|
10
37
|
export declare function serializeCookie(name: string, value: string, opts?: CookieOptions): string;
|
|
11
38
|
/** A cookie's name + options bundled into a typed read/set/clear handle. */
|
|
12
39
|
export interface CookieDef {
|
|
13
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Read this cookie's value from a request (`undefined` if absent). With two
|
|
42
|
+
* cookies of this name the answer follows the handle's `duplicates` policy.
|
|
43
|
+
*/
|
|
14
44
|
get(req: Request): string | undefined;
|
|
45
|
+
/** Every value of this name the request carries, in header order. */
|
|
46
|
+
getAll(req: Request): string[];
|
|
15
47
|
/** Build a `Set-Cookie` header value for `value` with the baked-in options. */
|
|
16
48
|
set(value: string): string;
|
|
17
49
|
/** Build a `Set-Cookie` header value that clears the cookie. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cookies.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cookies.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAmBhF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ1E;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IACrC,qEAAqE;IACrE,UAAU,CAAC,EAAE,sBAAsB,CAAC;CACrC;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,aAAkB,GACvB,MAAM,CASR;AAED,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACxB;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACtC,qEAAqE;IACrE,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,EAAE,CAAC;IAC/B,+EAA+E;IAC/E,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,gEAAgE;IAChE,KAAK,IAAI,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAwBhF"}
|
|
@@ -43,8 +43,12 @@ export declare const DEFAULT_CORS_ALLOW_HEADERS = "Content-Type, Authorization,
|
|
|
43
43
|
* Note the deliberate asymmetry with {@link DEFAULT_CORS_ALLOW_HEADERS}:
|
|
44
44
|
* inbound the id may arrive as `X-Trace-Id`, outbound it is always
|
|
45
45
|
* `X-Request-Id` — one name on the wire out, no alias.
|
|
46
|
+
*
|
|
47
|
+
* `X-Build-Id` is the release a handler configured with a release marker
|
|
48
|
+
* names on every response; a cross-origin page reads it to learn it is stale
|
|
49
|
+
* (`stitchkit/release`).
|
|
46
50
|
*/
|
|
47
|
-
export declare const DEFAULT_CORS_EXPOSE_HEADERS = "Content-Disposition, Content-Length, Content-Range, Accept-Ranges, ETag, Last-Modified, X-Request-Id";
|
|
51
|
+
export declare const DEFAULT_CORS_EXPOSE_HEADERS = "Content-Disposition, Content-Length, Content-Range, Accept-Ranges, ETag, Last-Modified, X-Request-Id, X-Build-Id";
|
|
48
52
|
/**
|
|
49
53
|
* Reject an unsafe CORS config at construction. `credentials: true` with a
|
|
50
54
|
* wildcard origin would reflect *any* caller's `Origin` with
|
|
@@ -52,6 +56,17 @@ export declare const DEFAULT_CORS_EXPOSE_HEADERS = "Content-Disposition, Content
|
|
|
52
56
|
* requests. Credentials require an explicit origin allow-list.
|
|
53
57
|
*/
|
|
54
58
|
export declare function assertCorsConfig(config: CorsConfig): void;
|
|
59
|
+
/**
|
|
60
|
+
* Is `requestOrigin` on the server's **explicit** allow-list? The question a
|
|
61
|
+
* safelisted request body asks before it is read (→ ADR 0165), and it is
|
|
62
|
+
* deliberately stricter than `resolveOrigin`: a wildcard is not an allow-list
|
|
63
|
+
* (a simple request is sent with cookies from any site, so `'*'` would admit
|
|
64
|
+
* every site), `null` is what a sandboxed iframe, a cross-origin redirect and
|
|
65
|
+
* `file://` send and names no site, and an absent header is no answer at all.
|
|
66
|
+
* A string origin is compared, not echoed. Case-insensitive, like
|
|
67
|
+
* `resolveOrigin`'s list branch.
|
|
68
|
+
*/
|
|
69
|
+
export declare function isOriginAllowed(config: CorsConfig | undefined, requestOrigin: string | null | undefined): boolean;
|
|
55
70
|
export declare function corsHeaders(config: CorsConfig, requestOrigin?: string | null): Record<string, string>;
|
|
56
71
|
export declare function corsPreflightResponse(config: CorsConfig, req: Request): Response;
|
|
57
72
|
//# sourceMappingURL=cors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,qEAC6B,CAAC;AAErE
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/cors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,qEAC6B,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,2BAA2B,qHAC4E,CAAC;AAErH;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAoBzD;AAwBD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACvC,OAAO,CAMT;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA4BxB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,QAAQ,CAKhF"}
|