@orkestrel/middleware 0.0.5 → 0.0.7
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/src/core/index.cjs +81 -66
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +40 -23
- package/dist/src/core/index.d.ts +40 -23
- package/dist/src/core/index.js +81 -67
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +344 -256
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +51 -0
- package/dist/src/server/index.d.ts +51 -0
- package/dist/src/server/index.js +342 -257
- package/dist/src/server/index.js.map +1 -1
- package/package.json +25 -23
|
@@ -34,25 +34,24 @@ export declare interface BearerOptions {
|
|
|
34
34
|
* `context.state` once a token verifies.
|
|
35
35
|
*
|
|
36
36
|
* @remarks
|
|
37
|
-
* `token` is optional
|
|
38
|
-
*
|
|
37
|
+
* `token` is an optional readonly state contract: absent until
|
|
38
|
+
* `createBearer` runs, then installed on the request-owned state object. A
|
|
39
39
|
* consumer intersects only the slices it mounts into its own `TState`.
|
|
40
40
|
*/
|
|
41
41
|
export declare interface BearerState {
|
|
42
|
-
token?: string;
|
|
42
|
+
readonly token?: string;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* The body state slice `createBody` stashes.
|
|
47
47
|
*
|
|
48
48
|
* @remarks
|
|
49
|
-
* `body` holds the same value the cached `context.body()` resolved
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* await.
|
|
49
|
+
* `body` holds the same defined value the cached `context.body()` resolved
|
|
50
|
+
* to — a mid-handler read without a second `context.body()` await. The
|
|
51
|
+
* property remains absent when the body resolves `undefined`.
|
|
53
52
|
*/
|
|
54
53
|
export declare interface BodyState {
|
|
55
|
-
body?: unknown;
|
|
54
|
+
readonly body?: unknown;
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
/**
|
|
@@ -147,9 +146,25 @@ export declare interface ClientInfo {
|
|
|
147
146
|
* The client-facts state slice `createForwarded` stashes.
|
|
148
147
|
*/
|
|
149
148
|
export declare interface ClientState {
|
|
150
|
-
client?: ClientInfo;
|
|
149
|
+
readonly client?: ClientInfo;
|
|
151
150
|
}
|
|
152
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Compress bytes with the host-independent `CompressionStream` primitive.
|
|
154
|
+
*
|
|
155
|
+
* @param bytes - The uncompressed response bytes
|
|
156
|
+
* @param encoding - The negotiated actionable coding
|
|
157
|
+
* @returns The compressed bytes, or the original bytes when the platform
|
|
158
|
+
* stream unexpectedly has no readable body
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* const bytes = new TextEncoder().encode('compress me')
|
|
163
|
+
* const compressed = await compressBytes(bytes, 'gzip')
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export declare function compressBytes(bytes: Uint8Array<ArrayBuffer>, encoding: Exclude<Encoding, 'identity'>): Promise<Uint8Array<ArrayBuffer>>;
|
|
167
|
+
|
|
153
168
|
/**
|
|
154
169
|
* Options for `createCompression` — response-body compression.
|
|
155
170
|
*
|
|
@@ -576,7 +591,7 @@ export declare interface CSRFOptions {
|
|
|
576
591
|
* response exposes for a subsequent mutating request to submit back.
|
|
577
592
|
*/
|
|
578
593
|
export declare interface CSRFState {
|
|
579
|
-
csrf?: string;
|
|
594
|
+
readonly csrf?: string;
|
|
580
595
|
}
|
|
581
596
|
|
|
582
597
|
/**
|
|
@@ -835,7 +850,7 @@ export declare interface HeaderTransportOptions {
|
|
|
835
850
|
* `identifier` option is enabled.
|
|
836
851
|
*/
|
|
837
852
|
export declare interface IdentifierState {
|
|
838
|
-
identifier?: string;
|
|
853
|
+
readonly identifier?: string;
|
|
839
854
|
}
|
|
840
855
|
|
|
841
856
|
/**
|
|
@@ -1114,7 +1129,7 @@ export declare interface MultipartFile {
|
|
|
1114
1129
|
* cached body has nothing left to read.
|
|
1115
1130
|
*/
|
|
1116
1131
|
export declare interface MultipartState {
|
|
1117
|
-
multipart?: MultipartBody;
|
|
1132
|
+
readonly multipart?: MultipartBody;
|
|
1118
1133
|
}
|
|
1119
1134
|
|
|
1120
1135
|
/**
|
|
@@ -1312,17 +1327,19 @@ export declare class Session implements SessionInterface {
|
|
|
1312
1327
|
}
|
|
1313
1328
|
|
|
1314
1329
|
/**
|
|
1315
|
-
* The `@orkestrel/database` column shape for a
|
|
1316
|
-
*
|
|
1317
|
-
*
|
|
1330
|
+
* The `@orkestrel/database` column shape for a
|
|
1331
|
+
* {@link import('./types.js').SessionRow} table — pass as-is to
|
|
1332
|
+
* `createDatabase({ tables: { sessions: sessionColumns } })` so an app
|
|
1333
|
+
* declaring a durable session table never hand-writes the shape.
|
|
1318
1334
|
*
|
|
1319
1335
|
* @remarks
|
|
1320
|
-
* `lastSeen`/`createdAt` are `integerShape({ min: 0 })` — the table
|
|
1321
|
-
* them as integers, so
|
|
1322
|
-
*
|
|
1323
|
-
* clock
|
|
1324
|
-
*
|
|
1325
|
-
*
|
|
1336
|
+
* `lastSeen`/`createdAt` are `integerShape({ min: 0 })` — the table validates
|
|
1337
|
+
* them as integers, so
|
|
1338
|
+
* {@link import('./stores/DatabaseSessionStore.js').DatabaseSessionStore}'s
|
|
1339
|
+
* `now` clock must yield integer milliseconds (`Date.now()`, the implicit
|
|
1340
|
+
* default `createSession` clock). A fractional clock (`performance.now()`)
|
|
1341
|
+
* fails the write with a validation error; `MemorySessionStore` carries no
|
|
1342
|
+
* such column shape and accepts a fractional clock without complaint.
|
|
1326
1343
|
*
|
|
1327
1344
|
* @example
|
|
1328
1345
|
* ```ts
|
|
@@ -1449,8 +1466,8 @@ export declare interface SessionRow {
|
|
|
1449
1466
|
* `control` is present whenever `session` is (the handle to act on it).
|
|
1450
1467
|
*/
|
|
1451
1468
|
export declare interface SessionState {
|
|
1452
|
-
session?: SessionInterface;
|
|
1453
|
-
control?: SessionControlInterface;
|
|
1469
|
+
readonly session?: SessionInterface;
|
|
1470
|
+
readonly control?: SessionControlInterface;
|
|
1454
1471
|
}
|
|
1455
1472
|
|
|
1456
1473
|
/**
|
package/dist/src/core/index.d.ts
CHANGED
|
@@ -34,25 +34,24 @@ export declare interface BearerOptions {
|
|
|
34
34
|
* `context.state` once a token verifies.
|
|
35
35
|
*
|
|
36
36
|
* @remarks
|
|
37
|
-
* `token` is optional
|
|
38
|
-
*
|
|
37
|
+
* `token` is an optional readonly state contract: absent until
|
|
38
|
+
* `createBearer` runs, then installed on the request-owned state object. A
|
|
39
39
|
* consumer intersects only the slices it mounts into its own `TState`.
|
|
40
40
|
*/
|
|
41
41
|
export declare interface BearerState {
|
|
42
|
-
token?: string;
|
|
42
|
+
readonly token?: string;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* The body state slice `createBody` stashes.
|
|
47
47
|
*
|
|
48
48
|
* @remarks
|
|
49
|
-
* `body` holds the same value the cached `context.body()` resolved
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* await.
|
|
49
|
+
* `body` holds the same defined value the cached `context.body()` resolved
|
|
50
|
+
* to — a mid-handler read without a second `context.body()` await. The
|
|
51
|
+
* property remains absent when the body resolves `undefined`.
|
|
53
52
|
*/
|
|
54
53
|
export declare interface BodyState {
|
|
55
|
-
body?: unknown;
|
|
54
|
+
readonly body?: unknown;
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
/**
|
|
@@ -147,9 +146,25 @@ export declare interface ClientInfo {
|
|
|
147
146
|
* The client-facts state slice `createForwarded` stashes.
|
|
148
147
|
*/
|
|
149
148
|
export declare interface ClientState {
|
|
150
|
-
client?: ClientInfo;
|
|
149
|
+
readonly client?: ClientInfo;
|
|
151
150
|
}
|
|
152
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Compress bytes with the host-independent `CompressionStream` primitive.
|
|
154
|
+
*
|
|
155
|
+
* @param bytes - The uncompressed response bytes
|
|
156
|
+
* @param encoding - The negotiated actionable coding
|
|
157
|
+
* @returns The compressed bytes, or the original bytes when the platform
|
|
158
|
+
* stream unexpectedly has no readable body
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* const bytes = new TextEncoder().encode('compress me')
|
|
163
|
+
* const compressed = await compressBytes(bytes, 'gzip')
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export declare function compressBytes(bytes: Uint8Array<ArrayBuffer>, encoding: Exclude<Encoding, 'identity'>): Promise<Uint8Array<ArrayBuffer>>;
|
|
167
|
+
|
|
153
168
|
/**
|
|
154
169
|
* Options for `createCompression` — response-body compression.
|
|
155
170
|
*
|
|
@@ -576,7 +591,7 @@ export declare interface CSRFOptions {
|
|
|
576
591
|
* response exposes for a subsequent mutating request to submit back.
|
|
577
592
|
*/
|
|
578
593
|
export declare interface CSRFState {
|
|
579
|
-
csrf?: string;
|
|
594
|
+
readonly csrf?: string;
|
|
580
595
|
}
|
|
581
596
|
|
|
582
597
|
/**
|
|
@@ -835,7 +850,7 @@ export declare interface HeaderTransportOptions {
|
|
|
835
850
|
* `identifier` option is enabled.
|
|
836
851
|
*/
|
|
837
852
|
export declare interface IdentifierState {
|
|
838
|
-
identifier?: string;
|
|
853
|
+
readonly identifier?: string;
|
|
839
854
|
}
|
|
840
855
|
|
|
841
856
|
/**
|
|
@@ -1114,7 +1129,7 @@ export declare interface MultipartFile {
|
|
|
1114
1129
|
* cached body has nothing left to read.
|
|
1115
1130
|
*/
|
|
1116
1131
|
export declare interface MultipartState {
|
|
1117
|
-
multipart?: MultipartBody;
|
|
1132
|
+
readonly multipart?: MultipartBody;
|
|
1118
1133
|
}
|
|
1119
1134
|
|
|
1120
1135
|
/**
|
|
@@ -1312,17 +1327,19 @@ export declare class Session implements SessionInterface {
|
|
|
1312
1327
|
}
|
|
1313
1328
|
|
|
1314
1329
|
/**
|
|
1315
|
-
* The `@orkestrel/database` column shape for a
|
|
1316
|
-
*
|
|
1317
|
-
*
|
|
1330
|
+
* The `@orkestrel/database` column shape for a
|
|
1331
|
+
* {@link import('./types.js').SessionRow} table — pass as-is to
|
|
1332
|
+
* `createDatabase({ tables: { sessions: sessionColumns } })` so an app
|
|
1333
|
+
* declaring a durable session table never hand-writes the shape.
|
|
1318
1334
|
*
|
|
1319
1335
|
* @remarks
|
|
1320
|
-
* `lastSeen`/`createdAt` are `integerShape({ min: 0 })` — the table
|
|
1321
|
-
* them as integers, so
|
|
1322
|
-
*
|
|
1323
|
-
* clock
|
|
1324
|
-
*
|
|
1325
|
-
*
|
|
1336
|
+
* `lastSeen`/`createdAt` are `integerShape({ min: 0 })` — the table validates
|
|
1337
|
+
* them as integers, so
|
|
1338
|
+
* {@link import('./stores/DatabaseSessionStore.js').DatabaseSessionStore}'s
|
|
1339
|
+
* `now` clock must yield integer milliseconds (`Date.now()`, the implicit
|
|
1340
|
+
* default `createSession` clock). A fractional clock (`performance.now()`)
|
|
1341
|
+
* fails the write with a validation error; `MemorySessionStore` carries no
|
|
1342
|
+
* such column shape and accepts a fractional clock without complaint.
|
|
1326
1343
|
*
|
|
1327
1344
|
* @example
|
|
1328
1345
|
* ```ts
|
|
@@ -1449,8 +1466,8 @@ export declare interface SessionRow {
|
|
|
1449
1466
|
* `control` is present whenever `session` is (the handle to act on it).
|
|
1450
1467
|
*/
|
|
1451
1468
|
export declare interface SessionState {
|
|
1452
|
-
session?: SessionInterface;
|
|
1453
|
-
control?: SessionControlInterface;
|
|
1469
|
+
readonly session?: SessionInterface;
|
|
1470
|
+
readonly control?: SessionControlInterface;
|
|
1454
1471
|
}
|
|
1455
1472
|
|
|
1456
1473
|
/**
|
package/dist/src/core/index.js
CHANGED
|
@@ -305,6 +305,26 @@ function detectEncodings(candidates) {
|
|
|
305
305
|
return supported;
|
|
306
306
|
}
|
|
307
307
|
/**
|
|
308
|
+
* Compress bytes with the host-independent `CompressionStream` primitive.
|
|
309
|
+
*
|
|
310
|
+
* @param bytes - The uncompressed response bytes
|
|
311
|
+
* @param encoding - The negotiated actionable coding
|
|
312
|
+
* @returns The compressed bytes, or the original bytes when the platform
|
|
313
|
+
* stream unexpectedly has no readable body
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* const bytes = new TextEncoder().encode('compress me')
|
|
318
|
+
* const compressed = await compressBytes(bytes, 'gzip')
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
async function compressBytes(bytes, encoding) {
|
|
322
|
+
const source = new Response(bytes).body;
|
|
323
|
+
if (source === null) return bytes;
|
|
324
|
+
const compressed = await new Response(source.pipeThrough(new CompressionStream(encoding))).arrayBuffer();
|
|
325
|
+
return new Uint8Array(compressed);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
308
328
|
* Whether a response is eligible for the compression/ETag buffering pipeline
|
|
309
329
|
* (ruling J) — the shared cheap-skip predicate both batteries apply before
|
|
310
330
|
* ever touching `response.arrayBuffer()`.
|
|
@@ -568,7 +588,7 @@ function isPreflight(method, headers) {
|
|
|
568
588
|
* ```
|
|
569
589
|
*/
|
|
570
590
|
function buildClientInfo(ip) {
|
|
571
|
-
return { ip };
|
|
591
|
+
return { ...ip !== void 0 ? { ip } : {} };
|
|
572
592
|
}
|
|
573
593
|
/**
|
|
574
594
|
* Constant-time string equality — `createCSRF`'s double-submit token
|
|
@@ -664,6 +684,34 @@ function restoreSession(value) {
|
|
|
664
684
|
return session;
|
|
665
685
|
}
|
|
666
686
|
//#endregion
|
|
687
|
+
//#region src/core/shapers.ts
|
|
688
|
+
/**
|
|
689
|
+
* The `@orkestrel/database` column shape for a
|
|
690
|
+
* {@link import('./types.js').SessionRow} table — pass as-is to
|
|
691
|
+
* `createDatabase({ tables: { sessions: sessionColumns } })` so an app
|
|
692
|
+
* declaring a durable session table never hand-writes the shape.
|
|
693
|
+
*
|
|
694
|
+
* @remarks
|
|
695
|
+
* `lastSeen`/`createdAt` are `integerShape({ min: 0 })` — the table validates
|
|
696
|
+
* them as integers, so
|
|
697
|
+
* {@link import('./stores/DatabaseSessionStore.js').DatabaseSessionStore}'s
|
|
698
|
+
* `now` clock must yield integer milliseconds (`Date.now()`, the implicit
|
|
699
|
+
* default `createSession` clock). A fractional clock (`performance.now()`)
|
|
700
|
+
* fails the write with a validation error; `MemorySessionStore` carries no
|
|
701
|
+
* such column shape and accepts a fractional clock without complaint.
|
|
702
|
+
*
|
|
703
|
+
* @example
|
|
704
|
+
* ```ts
|
|
705
|
+
* const db = createDatabase({ driver, tables: { sessions: sessionColumns } })
|
|
706
|
+
* ```
|
|
707
|
+
*/
|
|
708
|
+
var sessionColumns = {
|
|
709
|
+
id: stringShape(),
|
|
710
|
+
session: jsonShape(),
|
|
711
|
+
lastSeen: integerShape({ min: 0 }),
|
|
712
|
+
createdAt: integerShape({ min: 0 })
|
|
713
|
+
};
|
|
714
|
+
//#endregion
|
|
667
715
|
//#region src/core/stores/MemorySessionStore.ts
|
|
668
716
|
/**
|
|
669
717
|
* The default in-process {@link SessionStoreInterface} — a `Map`-backed store
|
|
@@ -741,8 +789,8 @@ var MemorySessionStore = class {
|
|
|
741
789
|
}
|
|
742
790
|
#expired(entry, now) {
|
|
743
791
|
return sessionExpired(entry, now, {
|
|
744
|
-
ttl: this.#ttl,
|
|
745
|
-
lifetime: this.#lifetime
|
|
792
|
+
...this.#ttl !== void 0 ? { ttl: this.#ttl } : {},
|
|
793
|
+
...this.#lifetime !== void 0 ? { lifetime: this.#lifetime } : {}
|
|
746
794
|
});
|
|
747
795
|
}
|
|
748
796
|
#reserve(now) {
|
|
@@ -815,8 +863,8 @@ var DatabaseSessionStore = class {
|
|
|
815
863
|
const row = await this.#table.get(id);
|
|
816
864
|
if (row === void 0) return void 0;
|
|
817
865
|
if (sessionExpired(row, now, {
|
|
818
|
-
ttl: this.#ttl,
|
|
819
|
-
lifetime: this.#lifetime
|
|
866
|
+
...this.#ttl !== void 0 ? { ttl: this.#ttl } : {},
|
|
867
|
+
...this.#lifetime !== void 0 ? { lifetime: this.#lifetime } : {}
|
|
820
868
|
})) {
|
|
821
869
|
await this.#table.remove(id);
|
|
822
870
|
return;
|
|
@@ -931,18 +979,12 @@ function createCompression(options) {
|
|
|
931
979
|
const threshold = options?.threshold ?? 1024;
|
|
932
980
|
const encodings = detectEncodings(options?.encodings ?? DEFAULT_COMPRESSION_ENCODINGS);
|
|
933
981
|
const filter = options?.filter;
|
|
934
|
-
const compress = async (bytes, encoding) => {
|
|
935
|
-
const source = new Response(bytes).body;
|
|
936
|
-
if (source === null) return bytes;
|
|
937
|
-
const compressed = await new Response(source.pipeThrough(new CompressionStream(encoding))).arrayBuffer();
|
|
938
|
-
return new Uint8Array(compressed);
|
|
939
|
-
};
|
|
940
982
|
return async (request, context, next) => {
|
|
941
983
|
return compressResponse(request, context, await next(), {
|
|
942
984
|
threshold,
|
|
943
|
-
filter,
|
|
985
|
+
...filter !== void 0 ? { filter } : {},
|
|
944
986
|
encodings,
|
|
945
|
-
compress
|
|
987
|
+
compress: compressBytes
|
|
946
988
|
});
|
|
947
989
|
};
|
|
948
990
|
}
|
|
@@ -988,7 +1030,7 @@ function createSecurity(options) {
|
|
|
988
1030
|
if (identifierEnabled) {
|
|
989
1031
|
const incoming = request.headers.get(DEFAULT_IDENTIFIER_HEADER);
|
|
990
1032
|
identifier = trust && incoming !== null && isValidRequestId(incoming) ? incoming : crypto.randomUUID();
|
|
991
|
-
context.state
|
|
1033
|
+
Object.assign(context.state, { identifier });
|
|
992
1034
|
}
|
|
993
1035
|
const response = await next();
|
|
994
1036
|
response.headers.set("x-content-type-options", "nosniff");
|
|
@@ -1121,7 +1163,7 @@ function createForwarded(options) {
|
|
|
1121
1163
|
const trust = hasProxies ? { proxies: options.proxies } : { trusted: options.trusted };
|
|
1122
1164
|
return async (request, context, next) => {
|
|
1123
1165
|
const ip = resolveForwardedFor(request.headers.get("x-forwarded-for") ?? void 0, trust) ?? context.state.connection?.ip;
|
|
1124
|
-
context.state
|
|
1166
|
+
Object.assign(context.state, { client: buildClientInfo(ip) });
|
|
1125
1167
|
return next();
|
|
1126
1168
|
};
|
|
1127
1169
|
}
|
|
@@ -1195,7 +1237,7 @@ function createBearer(options) {
|
|
|
1195
1237
|
}
|
|
1196
1238
|
const verified = await verifyToken(candidate, secret);
|
|
1197
1239
|
if (verified === void 0) throw new HTTPError(401, "invalid token");
|
|
1198
|
-
context.state
|
|
1240
|
+
Object.assign(context.state, { token: verified });
|
|
1199
1241
|
return next();
|
|
1200
1242
|
};
|
|
1201
1243
|
}
|
|
@@ -1224,20 +1266,14 @@ function createLimiter(options) {
|
|
|
1224
1266
|
const max = options.max;
|
|
1225
1267
|
const window = options.window;
|
|
1226
1268
|
const capacity = options.capacity ?? 1e4;
|
|
1227
|
-
const deriveKey = options.key
|
|
1269
|
+
const deriveKey = options.key;
|
|
1228
1270
|
const message = options.message ?? "rate limit exceeded";
|
|
1229
1271
|
const clock = options.clock ?? Date.now;
|
|
1230
1272
|
const policy = options.policy ?? false;
|
|
1231
1273
|
const evict = options.evict;
|
|
1232
|
-
const notify = (evictedKey) => {
|
|
1233
|
-
if (evict === void 0) return;
|
|
1234
|
-
try {
|
|
1235
|
-
evict(evictedKey);
|
|
1236
|
-
} catch {}
|
|
1237
|
-
};
|
|
1238
1274
|
const buckets = /* @__PURE__ */ new Map();
|
|
1239
1275
|
return async (request, context, next) => {
|
|
1240
|
-
const key = deriveKey(context);
|
|
1276
|
+
const key = deriveKey === void 0 ? resolveKey(context.state) : deriveKey(context);
|
|
1241
1277
|
const now = clock();
|
|
1242
1278
|
let bucket = buckets.get(key);
|
|
1243
1279
|
if (bucket === void 0) {
|
|
@@ -1245,13 +1281,15 @@ function createLimiter(options) {
|
|
|
1245
1281
|
const oldest = buckets.keys().next().value;
|
|
1246
1282
|
if (oldest !== void 0) {
|
|
1247
1283
|
buckets.delete(oldest);
|
|
1248
|
-
|
|
1284
|
+
if (evict !== void 0) try {
|
|
1285
|
+
evict(oldest);
|
|
1286
|
+
} catch {}
|
|
1249
1287
|
}
|
|
1250
1288
|
}
|
|
1251
1289
|
bucket = {
|
|
1252
1290
|
budget: createBudget({
|
|
1253
1291
|
max,
|
|
1254
|
-
consume:
|
|
1292
|
+
consume: Number
|
|
1255
1293
|
}),
|
|
1256
1294
|
resetAt: now + window
|
|
1257
1295
|
};
|
|
@@ -1305,7 +1343,7 @@ function createLimiter(options) {
|
|
|
1305
1343
|
function createBody() {
|
|
1306
1344
|
return async (request, context, next) => {
|
|
1307
1345
|
const body = await context.body();
|
|
1308
|
-
context.state
|
|
1346
|
+
if (body !== void 0) Object.assign(context.state, { body });
|
|
1309
1347
|
const contentType = request.headers.get("content-type");
|
|
1310
1348
|
if (contentType !== null && contentType.toLowerCase().startsWith("application/json") && body === void 0) throw new HTTPError(400, "invalid json");
|
|
1311
1349
|
return next();
|
|
@@ -1340,12 +1378,12 @@ function createSession(options) {
|
|
|
1340
1378
|
const transport = options.transport;
|
|
1341
1379
|
const clock = options.clock ?? Date.now;
|
|
1342
1380
|
const store = options.store ?? new MemorySessionStore({
|
|
1343
|
-
ttl: options.ttl,
|
|
1344
|
-
lifetime: options.lifetime,
|
|
1345
|
-
capacity: options.capacity,
|
|
1346
|
-
evict: options.evict
|
|
1381
|
+
...options.ttl !== void 0 ? { ttl: options.ttl } : {},
|
|
1382
|
+
...options.lifetime !== void 0 ? { lifetime: options.lifetime } : {},
|
|
1383
|
+
...options.capacity !== void 0 ? { capacity: options.capacity } : {},
|
|
1384
|
+
...options.evict !== void 0 ? { evict: options.evict } : {}
|
|
1347
1385
|
});
|
|
1348
|
-
const create = options.create
|
|
1386
|
+
const create = options.create;
|
|
1349
1387
|
const mint = options.mint;
|
|
1350
1388
|
const requireSession = options.require ?? false;
|
|
1351
1389
|
const ends = options.ends ?? false;
|
|
@@ -1362,18 +1400,19 @@ function createSession(options) {
|
|
|
1362
1400
|
if (session === void 0) {
|
|
1363
1401
|
if (mint !== void 0 ? await mint(context) : true) {
|
|
1364
1402
|
const id = crypto.randomUUID();
|
|
1365
|
-
session = create(id);
|
|
1403
|
+
session = create === void 0 ? new Session(id) : create(id);
|
|
1366
1404
|
minted = true;
|
|
1367
1405
|
} else if (requireSession) throw new HTTPError(404, "session required");
|
|
1368
1406
|
}
|
|
1369
1407
|
let destroyed = false;
|
|
1370
1408
|
let regenerated;
|
|
1371
1409
|
const activeSession = session;
|
|
1372
|
-
if (activeSession !== void 0) {
|
|
1373
|
-
|
|
1410
|
+
if (activeSession !== void 0) Object.assign(context.state, {
|
|
1411
|
+
session: activeSession,
|
|
1412
|
+
control: {
|
|
1374
1413
|
regenerate() {
|
|
1375
1414
|
if (destroyed) return;
|
|
1376
|
-
const newSession = create(crypto.randomUUID());
|
|
1415
|
+
const newSession = create === void 0 ? new Session(crypto.randomUUID()) : create(crypto.randomUUID());
|
|
1377
1416
|
transferSessionData(activeSession, newSession);
|
|
1378
1417
|
regenerated = newSession;
|
|
1379
1418
|
},
|
|
@@ -1381,10 +1420,8 @@ function createSession(options) {
|
|
|
1381
1420
|
destroyed = true;
|
|
1382
1421
|
regenerated = void 0;
|
|
1383
1422
|
}
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
context.state.control = control;
|
|
1387
|
-
}
|
|
1423
|
+
}
|
|
1424
|
+
});
|
|
1388
1425
|
const response = await next();
|
|
1389
1426
|
if (activeSession !== void 0) if (destroyed) {
|
|
1390
1427
|
await store.delete(activeSession.id);
|
|
@@ -1427,8 +1464,9 @@ function createCSRF(options) {
|
|
|
1427
1464
|
const safe = options.safe ?? DEFAULT_CSRF_SAFE_METHODS;
|
|
1428
1465
|
return async (request, context, next) => {
|
|
1429
1466
|
if (safe.includes(context.method)) {
|
|
1430
|
-
const
|
|
1431
|
-
|
|
1467
|
+
const bound = context.state.session?.id ?? crypto.randomUUID();
|
|
1468
|
+
const token = await signToken(bound, { secret });
|
|
1469
|
+
Object.assign(context.state, { csrf: token });
|
|
1432
1470
|
const response = await next();
|
|
1433
1471
|
const secure = resolveSecure(void 0, context.state.connection?.encrypted ?? false);
|
|
1434
1472
|
await writeSignedCookie(response.headers, cookieName, token, secret, {
|
|
@@ -1586,30 +1624,6 @@ function createMemorySessionStore(options) {
|
|
|
1586
1624
|
return new MemorySessionStore(options);
|
|
1587
1625
|
}
|
|
1588
1626
|
/**
|
|
1589
|
-
* The `@orkestrel/database` column shape for a {@link SessionRow} table — pass
|
|
1590
|
-
* as-is to `createDatabase({ tables: { sessions: sessionColumns } })` so an
|
|
1591
|
-
* app declaring a durable session table never hand-writes the shape.
|
|
1592
|
-
*
|
|
1593
|
-
* @remarks
|
|
1594
|
-
* `lastSeen`/`createdAt` are `integerShape({ min: 0 })` — the table VALIDATES
|
|
1595
|
-
* them as integers, so `DatabaseSessionStore`'s `now` clock must yield
|
|
1596
|
-
* integer milliseconds (`Date.now()`, the implicit default `createSession`
|
|
1597
|
-
* clock). A fractional clock (`performance.now()`) fails the write with a
|
|
1598
|
-
* validation error; `MemorySessionStore` carries no such column shape and
|
|
1599
|
-
* accepts a fractional clock without complaint.
|
|
1600
|
-
*
|
|
1601
|
-
* @example
|
|
1602
|
-
* ```ts
|
|
1603
|
-
* const db = createDatabase({ driver, tables: { sessions: sessionColumns } })
|
|
1604
|
-
* ```
|
|
1605
|
-
*/
|
|
1606
|
-
var sessionColumns = {
|
|
1607
|
-
id: stringShape(),
|
|
1608
|
-
session: jsonShape(),
|
|
1609
|
-
lastSeen: integerShape({ min: 0 }),
|
|
1610
|
-
createdAt: integerShape({ min: 0 })
|
|
1611
|
-
};
|
|
1612
|
-
/**
|
|
1613
1627
|
* Create a {@link DatabaseSessionStore} as a {@link SessionStoreInterface} —
|
|
1614
1628
|
* the durable counterpart to `createMemorySessionStore`, over a caller-opened
|
|
1615
1629
|
* `@orkestrel/database` table (declare it with {@link sessionColumns}).
|
|
@@ -1634,6 +1648,6 @@ function createDatabaseSessionStore(table, is, options) {
|
|
|
1634
1648
|
return new DatabaseSessionStore(table, is, options);
|
|
1635
1649
|
}
|
|
1636
1650
|
//#endregion
|
|
1637
|
-
export { DEFAULT_BEARER_HEADER, DEFAULT_BEARER_SCHEME, DEFAULT_CLUSTER, DEFAULT_COEP, DEFAULT_COMPRESSION_ENCODINGS, DEFAULT_COMPRESSION_THRESHOLD, DEFAULT_COOP, DEFAULT_CORP, DEFAULT_CORS_HEADERS, DEFAULT_CORS_METHODS, DEFAULT_CSP, DEFAULT_CSRF_COOKIE, DEFAULT_CSRF_FIELD, DEFAULT_CSRF_HEADER, DEFAULT_CSRF_SAFE_METHODS, DEFAULT_DEADLINE_STATUS, DEFAULT_FRAME_OPTIONS, DEFAULT_HSTS, DEFAULT_IDENTIFIER_HEADER, DEFAULT_LIMITER_CAPACITY, DEFAULT_LIMITER_MESSAGE, DEFAULT_PERMISSIONS_POLICY, DEFAULT_REFERRER_POLICY, DEFAULT_SESSION_CAPACITY, DEFAULT_SESSION_COOKIE, DEFAULT_SESSION_HEADER, DatabaseSessionStore, MemorySessionStore, Session, buildClientInfo, buildRateLimitField, buildRateLimitPolicyField, buildRetryAfter, compressResponse, createBearer, createBody, createBoundary, createCSRF, createCompression, createCookieTransport, createCors, createDatabaseSessionStore, createDeadline, createETag, createForwarded, createHeaderTransport, createLimiter, createMemorySessionStore, createSecurity, createSession, createTelemetry, detectEncodings, equalsConstantTime, except, isBufferingIneligible, isCompressionNegotiated, isMultipartBody, isMultipartFile, isPreflight, isSession, isSessionControl, matchesTrustedEntry, only, rebuildResponse, resolveForwardedFor, resolveKey, resolveOptInHeader, restoreSession, sessionColumns, sessionExpired, snapshotSession, transferSessionData };
|
|
1651
|
+
export { DEFAULT_BEARER_HEADER, DEFAULT_BEARER_SCHEME, DEFAULT_CLUSTER, DEFAULT_COEP, DEFAULT_COMPRESSION_ENCODINGS, DEFAULT_COMPRESSION_THRESHOLD, DEFAULT_COOP, DEFAULT_CORP, DEFAULT_CORS_HEADERS, DEFAULT_CORS_METHODS, DEFAULT_CSP, DEFAULT_CSRF_COOKIE, DEFAULT_CSRF_FIELD, DEFAULT_CSRF_HEADER, DEFAULT_CSRF_SAFE_METHODS, DEFAULT_DEADLINE_STATUS, DEFAULT_FRAME_OPTIONS, DEFAULT_HSTS, DEFAULT_IDENTIFIER_HEADER, DEFAULT_LIMITER_CAPACITY, DEFAULT_LIMITER_MESSAGE, DEFAULT_PERMISSIONS_POLICY, DEFAULT_REFERRER_POLICY, DEFAULT_SESSION_CAPACITY, DEFAULT_SESSION_COOKIE, DEFAULT_SESSION_HEADER, DatabaseSessionStore, MemorySessionStore, Session, buildClientInfo, buildRateLimitField, buildRateLimitPolicyField, buildRetryAfter, compressBytes, compressResponse, createBearer, createBody, createBoundary, createCSRF, createCompression, createCookieTransport, createCors, createDatabaseSessionStore, createDeadline, createETag, createForwarded, createHeaderTransport, createLimiter, createMemorySessionStore, createSecurity, createSession, createTelemetry, detectEncodings, equalsConstantTime, except, isBufferingIneligible, isCompressionNegotiated, isMultipartBody, isMultipartFile, isPreflight, isSession, isSessionControl, matchesTrustedEntry, only, rebuildResponse, resolveForwardedFor, resolveKey, resolveOptInHeader, restoreSession, sessionColumns, sessionExpired, snapshotSession, transferSessionData };
|
|
1638
1652
|
|
|
1639
1653
|
//# sourceMappingURL=index.js.map
|