@fedify/netlify 2.4.0-pr.934.40 → 2.5.0-dev.2271
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/README.md +72 -25
- package/dist/mod.cjs +226 -107
- package/dist/mod.d.cts +82 -2
- package/dist/mod.d.ts +82 -2
- package/dist/mod.js +113 -2
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- deno-fmt-ignore-file -->
|
|
2
2
|
|
|
3
|
-
@fedify/netlify: Run Fedify
|
|
4
|
-
|
|
3
|
+
@fedify/netlify: Run Fedify with Netlify blobs and async workloads
|
|
4
|
+
==================================================================
|
|
5
5
|
|
|
6
6
|
[![JSR][JSR badge]][JSR]
|
|
7
7
|
[![npm][npm badge]][npm]
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
|
|
10
10
|
*This package is available since Fedify 2.4.0.*
|
|
11
11
|
|
|
12
|
-
This package connects [Fedify]'s [`MessageQueue`]
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
This package connects [Fedify]'s [`KvStore`] and [`MessageQueue`] APIs to
|
|
13
|
+
Netlify. `NetlifyBlobsKvStore` stores federation state in [Netlify Blobs],
|
|
14
|
+
`NetlifyMessageQueue` publishes durable events through
|
|
15
|
+
[Netlify Async Workloads], and `createNetlifyQueueHandler()` turns a Netlify
|
|
16
|
+
Function into their consumer.
|
|
16
17
|
|
|
17
18
|
The initial release targets Netlify Functions, not Netlify Edge Functions.
|
|
18
19
|
|
|
@@ -23,13 +24,17 @@ The initial release targets Netlify Functions, not Netlify Edge Functions.
|
|
|
23
24
|
[@fedify@hackers.pub badge]: https://fedi-badge.minhee.org/@fedify@hackers.pub/followers.svg
|
|
24
25
|
[@fedify@hackers.pub]: https://hackers.pub/@fedify
|
|
25
26
|
[Fedify]: https://fedify.dev/
|
|
27
|
+
[`KvStore`]: https://jsr.io/@fedify/fedify/doc/federation/~/KvStore
|
|
26
28
|
[`MessageQueue`]: https://jsr.io/@fedify/fedify/doc/federation/~/MessageQueue
|
|
29
|
+
[Netlify Blobs]: https://docs.netlify.com/build/data-and-storage/netlify-blobs/
|
|
27
30
|
[Netlify Async Workloads]: https://docs.netlify.com/build/async-workloads/get-started/
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
Installation
|
|
31
34
|
------------
|
|
32
35
|
|
|
36
|
+
For queue-only usage, install the package with Netlify Async Workloads:
|
|
37
|
+
|
|
33
38
|
~~~~ sh
|
|
34
39
|
deno add jsr:@fedify/netlify npm:@netlify/async-workloads # Deno
|
|
35
40
|
npm add @fedify/netlify @netlify/async-workloads # npm
|
|
@@ -38,8 +43,62 @@ yarn add @fedify/netlify @netlify/async-workloads # Yarn
|
|
|
38
43
|
bun add @fedify/netlify @netlify/async-workloads # Bun
|
|
39
44
|
~~~~
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
|
|
47
|
+
Usage
|
|
48
|
+
-----
|
|
49
|
+
|
|
50
|
+
To use `NetlifyBlobsKvStore`, also install Netlify Blobs:
|
|
51
|
+
|
|
52
|
+
~~~~ sh
|
|
53
|
+
deno add npm:@netlify/blobs # Deno
|
|
54
|
+
npm add @netlify/blobs # npm
|
|
55
|
+
pnpm add @netlify/blobs # pnpm
|
|
56
|
+
yarn add @netlify/blobs # Yarn
|
|
57
|
+
bun add @netlify/blobs # Bun
|
|
58
|
+
~~~~
|
|
59
|
+
|
|
60
|
+
Create one store and queue for both the web application and the workload
|
|
61
|
+
function. `NetlifyBlobsKvStore` provides the compare-and-set (CAS) support
|
|
62
|
+
required when Fedify emits messages with an `orderingKey`, without requiring a
|
|
63
|
+
separate database:
|
|
64
|
+
|
|
65
|
+
~~~~ typescript
|
|
66
|
+
import { AsyncWorkloadsClient } from "@netlify/async-workloads";
|
|
67
|
+
import { getStore } from "@netlify/blobs";
|
|
68
|
+
import {
|
|
69
|
+
NetlifyBlobsKvStore,
|
|
70
|
+
NetlifyMessageQueue,
|
|
71
|
+
} from "@fedify/netlify";
|
|
72
|
+
|
|
73
|
+
export const kv = new NetlifyBlobsKvStore(getStore({
|
|
74
|
+
name: "fedify",
|
|
75
|
+
consistency: "strong",
|
|
76
|
+
}));
|
|
77
|
+
export const queue = new NetlifyMessageQueue({
|
|
78
|
+
client: new AsyncWorkloadsClient(),
|
|
79
|
+
orderingKv: kv,
|
|
80
|
+
});
|
|
81
|
+
~~~~
|
|
82
|
+
|
|
83
|
+
CAS operations always use strong reads and conditional writes. The
|
|
84
|
+
`consistency: "strong"` setting above also gives ordinary `get()` calls and
|
|
85
|
+
listings strong consistency. You can omit it when lower-latency, eventually
|
|
86
|
+
consistent ordinary reads are acceptable.
|
|
87
|
+
|
|
88
|
+
TTL expiration is logical: expired blobs remain in Netlify Blobs but are
|
|
89
|
+
hidden by `get()` and `list()`. CAS deletion similarly writes a tombstone so
|
|
90
|
+
that a concurrent writer cannot recreate the key undetected. Tombstones are
|
|
91
|
+
also hidden and can be replaced by a later CAS operation, but they are not
|
|
92
|
+
removed automatically. Use a dedicated Netlify Blobs store and account for
|
|
93
|
+
these retained blobs. Only remove them during maintenance when concurrent CAS
|
|
94
|
+
operations for those keys have stopped; physical deletion during a race would
|
|
95
|
+
remove the version token that protects the key.
|
|
96
|
+
|
|
97
|
+
### PostgreSQL alternative
|
|
98
|
+
|
|
99
|
+
If the application already uses PostgreSQL, `PostgresKvStore` can provide the
|
|
100
|
+
same CAS-capable ordering storage. Install `@fedify/postgres`,
|
|
101
|
+
`@netlify/database`, and `postgres` in addition to the base dependencies:
|
|
43
102
|
|
|
44
103
|
~~~~ sh
|
|
45
104
|
deno add jsr:@fedify/postgres npm:@netlify/database npm:postgres # Deno
|
|
@@ -49,29 +108,20 @@ yarn add @fedify/postgres @netlify/database postgres # Yarn
|
|
|
49
108
|
bun add @fedify/postgres @netlify/database postgres # Bun
|
|
50
109
|
~~~~
|
|
51
110
|
|
|
52
|
-
|
|
53
|
-
Usage
|
|
54
|
-
-----
|
|
55
|
-
|
|
56
|
-
Create one queue for both the web application and the workload function. A
|
|
57
|
-
CAS-capable `KvStore`, such as `PostgresKvStore`, is required if Fedify emits
|
|
58
|
-
messages with an `orderingKey`:
|
|
111
|
+
Then replace the `kv` declaration from the main example:
|
|
59
112
|
|
|
60
113
|
~~~~ typescript
|
|
61
|
-
import { AsyncWorkloadsClient } from "@netlify/async-workloads";
|
|
62
114
|
import { getConnectionString } from "@netlify/database";
|
|
63
|
-
import { NetlifyMessageQueue } from "@fedify/netlify";
|
|
64
115
|
import { PostgresKvStore } from "@fedify/postgres";
|
|
65
116
|
import postgres from "postgres";
|
|
66
117
|
|
|
67
118
|
const sql = postgres(getConnectionString());
|
|
68
119
|
export const kv = new PostgresKvStore(sql);
|
|
69
|
-
export const queue = new NetlifyMessageQueue({
|
|
70
|
-
client: new AsyncWorkloadsClient(),
|
|
71
|
-
orderingKv: kv,
|
|
72
|
-
});
|
|
73
120
|
~~~~
|
|
74
121
|
|
|
122
|
+
Ordering state must use crash-safe storage. `PostgresKvStore` creates a logged
|
|
123
|
+
table by default; do not pass `unlogged: true` for `orderingKv`.
|
|
124
|
+
|
|
75
125
|
Pass the queue to Fedify with `manuallyStartQueue: true`. Async Workloads
|
|
76
126
|
invokes the consumer, so `NetlifyMessageQueue.listen()` is intentionally not
|
|
77
127
|
available:
|
|
@@ -142,9 +192,6 @@ because a lost response does not prove that the router rejected the event.
|
|
|
142
192
|
Use the error's `orderingKey` and `orderingSequence` for manual recovery only
|
|
143
193
|
after ruling out delivery.
|
|
144
194
|
|
|
145
|
-
Ordering state must use crash-safe storage. `PostgresKvStore` creates a logged
|
|
146
|
-
table by default; do not pass `unlogged: true` for `orderingKv`.
|
|
147
|
-
|
|
148
195
|
Netlify limits an event payload to 500 KB. Fedify messages, including any
|
|
149
196
|
embedded activity, must remain below that limit.
|
|
150
197
|
|
|
@@ -156,7 +203,7 @@ Fedify rejects a multi-message `enqueueTaskMany()` call with one
|
|
|
156
203
|
batch enqueue so a failed partial send cannot produce duplicates on retry.
|
|
157
204
|
|
|
158
205
|
See the [deployment manual] and the [Netlify Astro example] for a complete
|
|
159
|
-
setup with Netlify Database.
|
|
206
|
+
setup using the PostgreSQL alternative with Netlify Database.
|
|
160
207
|
|
|
161
208
|
[deployment manual]: https://fedify.dev/manual/deploy#netlify-functions
|
|
162
209
|
[Netlify Astro example]: https://github.com/fedify-dev/fedify/tree/main/examples/netlify-astro
|
package/dist/mod.cjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
let _netlify_async_workloads = require("@netlify/async-workloads");
|
|
4
|
+
let byte_encodings_base64url = require("byte-encodings/base64url");
|
|
5
|
+
let es_toolkit = require("es-toolkit");
|
|
4
6
|
//#region ../../node_modules/.pnpm/temporal-polyfill@1.0.1/node_modules/temporal-polyfill/chunks/root.js
|
|
5
7
|
const NativeTemporal = globalThis.Temporal;
|
|
6
8
|
//#endregion
|
|
@@ -152,7 +154,7 @@ function parseInt0(s) {
|
|
|
152
154
|
function padNumber(digits, num) {
|
|
153
155
|
return String(num).padStart(digits, "0");
|
|
154
156
|
}
|
|
155
|
-
const padNumber2 =
|
|
157
|
+
const padNumber2 = /*@__PURE__*/ bindArgs(padNumber, 2);
|
|
156
158
|
function compareNumbers(a, b) {
|
|
157
159
|
return Math.sign(a - b);
|
|
158
160
|
}
|
|
@@ -220,7 +222,7 @@ const unitNameMap = {
|
|
|
220
222
|
month: 8,
|
|
221
223
|
year: 9
|
|
222
224
|
};
|
|
223
|
-
const unitNamesAsc =
|
|
225
|
+
const unitNamesAsc = /*@__PURE__*/ Object.keys(unitNameMap);
|
|
224
226
|
const nanoInMicro = nanoInMicro$1;
|
|
225
227
|
const nanoInMilli = nanoInMilli$1;
|
|
226
228
|
const nanoInSec = nanoInSec$1;
|
|
@@ -236,19 +238,19 @@ const unitNanoMap = [
|
|
|
236
238
|
nanoInHour,
|
|
237
239
|
nanoInUtcDay
|
|
238
240
|
];
|
|
239
|
-
const bigNanoInMicro =
|
|
240
|
-
const bigNanoInMilli =
|
|
241
|
-
const bigNanoInSec =
|
|
242
|
-
const bigNanoInMinute =
|
|
243
|
-
const bigNanoInHour =
|
|
244
|
-
const bigNanoInUtcDay =
|
|
241
|
+
const bigNanoInMicro = /*@__PURE__*/ BigInt(nanoInMicro);
|
|
242
|
+
const bigNanoInMilli = /*@__PURE__*/ BigInt(nanoInMilli);
|
|
243
|
+
const bigNanoInSec = /*@__PURE__*/ BigInt(nanoInSec);
|
|
244
|
+
const bigNanoInMinute = /*@__PURE__*/ BigInt(nanoInMinute);
|
|
245
|
+
const bigNanoInHour = /*@__PURE__*/ BigInt(nanoInHour);
|
|
246
|
+
const bigNanoInUtcDay = /*@__PURE__*/ BigInt(nanoInUtcDay);
|
|
245
247
|
function divideBigNanoToExactNumber(bigNano, divisorNano) {
|
|
246
248
|
const days = Number(bigNano / bigNanoInUtcDay);
|
|
247
249
|
const timeNano = Number(bigNano % bigNanoInUtcDay);
|
|
248
250
|
return days * (nanoInUtcDay / divisorNano) + (Math.trunc(timeNano / divisorNano) + timeNano % divisorNano / divisorNano);
|
|
249
251
|
}
|
|
250
|
-
const timeFieldNamesAsc =
|
|
251
|
-
const timeGetters =
|
|
252
|
+
const timeFieldNamesAsc = /*@__PURE__*/ unitNamesAsc.slice(0, 6);
|
|
253
|
+
const timeGetters = /*@__PURE__*/ createPropGetters(timeFieldNamesAsc);
|
|
252
254
|
const yearFieldNamesAsc = ["year"];
|
|
253
255
|
const dayFieldNamesAsc = ["day"];
|
|
254
256
|
const calendarDateFieldNamesAsc = [
|
|
@@ -270,24 +272,24 @@ const monthDayFieldNames = [
|
|
|
270
272
|
"month",
|
|
271
273
|
"monthCode"
|
|
272
274
|
];
|
|
273
|
-
const timeFieldNamesAlpha =
|
|
274
|
-
const yearFieldNamesWithEraAlpha =
|
|
275
|
-
const yearMonthFieldNamesAlpha =
|
|
276
|
-
const yearMonthFieldNamesWithEraAlpha =
|
|
277
|
-
const yearMonthCodeFieldNamesAlpha =
|
|
278
|
-
const yearMonthCodeFieldNamesWithEraAlpha =
|
|
279
|
-
const monthCodeDayFieldNamesAlpha =
|
|
280
|
-
const dateFieldNamesAlpha =
|
|
281
|
-
const dateFieldNamesWithEraAlpha =
|
|
282
|
-
const dateTimeFieldNamesAlpha =
|
|
283
|
-
const dateTimeFieldNamesWithEraAlpha =
|
|
284
|
-
const dateTimeAndOffsetFieldNamesAlpha =
|
|
285
|
-
const dateTimeAndOffsetFieldNamesWithEraAlpha =
|
|
286
|
-
const dateTimeAndZoneFieldNamesAlpha =
|
|
287
|
-
const dateTimeAndZoneFieldNamesWithEraAlpha =
|
|
288
|
-
const yearMonthCodeDayFieldNamesAlpha =
|
|
289
|
-
const yearMonthCodeDayFieldNamesWithEraAlpha =
|
|
290
|
-
const timeFieldDefaults =
|
|
275
|
+
const timeFieldNamesAlpha = /*@__PURE__*/ sortStrings(timeFieldNamesAsc);
|
|
276
|
+
const yearFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(eraYearFieldNames, yearFieldNamesAsc);
|
|
277
|
+
const yearMonthFieldNamesAlpha = /*@__PURE__*/ sortStrings(monthFieldNames, yearFieldNamesAsc);
|
|
278
|
+
const yearMonthFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(eraYearFieldNames, yearMonthFieldNamesAlpha);
|
|
279
|
+
const yearMonthCodeFieldNamesAlpha = /*@__PURE__*/ sortStrings(["monthCode"], yearFieldNamesAsc);
|
|
280
|
+
const yearMonthCodeFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(eraYearFieldNames, yearMonthCodeFieldNamesAlpha);
|
|
281
|
+
const monthCodeDayFieldNamesAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, ["monthCode"]);
|
|
282
|
+
const dateFieldNamesAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, yearMonthFieldNamesAlpha);
|
|
283
|
+
const dateFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, eraYearFieldNames, yearMonthFieldNamesAlpha);
|
|
284
|
+
const dateTimeFieldNamesAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc);
|
|
285
|
+
const dateTimeFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc);
|
|
286
|
+
const dateTimeAndOffsetFieldNamesAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc, offsetFieldNames);
|
|
287
|
+
const dateTimeAndOffsetFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc, offsetFieldNames);
|
|
288
|
+
const dateTimeAndZoneFieldNamesAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc, offsetFieldNames, timeZoneFieldNames);
|
|
289
|
+
const dateTimeAndZoneFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc, offsetFieldNames, timeZoneFieldNames);
|
|
290
|
+
const yearMonthCodeDayFieldNamesAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, yearMonthCodeFieldNamesAlpha);
|
|
291
|
+
const yearMonthCodeDayFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, eraYearFieldNames, yearMonthCodeFieldNamesAlpha);
|
|
292
|
+
const timeFieldDefaults = /*@__PURE__*/ zipPropsConst(timeFieldNamesAsc, 0);
|
|
291
293
|
function validateTimeFields(timeFields) {
|
|
292
294
|
return constrainTimeFields(timeFields, 1), timeFields;
|
|
293
295
|
}
|
|
@@ -516,20 +518,20 @@ function computeCalendarWeekOfYear(calendar, isoDate) {
|
|
|
516
518
|
function computeCalendarYearOfWeek(calendar, isoDate) {
|
|
517
519
|
return calendar === void 0 ? computeIsoWeekFields(isoDate).yearOfWeek : void 0;
|
|
518
520
|
}
|
|
519
|
-
const durationFieldNamesAsc =
|
|
520
|
-
const durationGetters =
|
|
521
|
-
const durationFieldNamesAlpha =
|
|
522
|
-
const durationTimeFieldNamesAsc =
|
|
523
|
-
const durationDateFieldNamesAsc =
|
|
524
|
-
const durationCalendarFieldNamesAsc =
|
|
525
|
-
const durationFieldIndexes =
|
|
526
|
-
const durationFieldDefaults =
|
|
527
|
-
const durationTimeFieldDefaults =
|
|
528
|
-
const clearDurationFields =
|
|
521
|
+
const durationFieldNamesAsc = /*@__PURE__*/ unitNamesAsc.map((unitName) => unitName + "s");
|
|
522
|
+
const durationGetters = /*@__PURE__*/ createPropGetters(durationFieldNamesAsc);
|
|
523
|
+
const durationFieldNamesAlpha = /*@__PURE__*/ sortStrings(durationFieldNamesAsc);
|
|
524
|
+
const durationTimeFieldNamesAsc = /*@__PURE__*/ durationFieldNamesAsc.slice(0, 6);
|
|
525
|
+
const durationDateFieldNamesAsc = /*@__PURE__*/ durationFieldNamesAsc.slice(6);
|
|
526
|
+
const durationCalendarFieldNamesAsc = /*@__PURE__*/ durationDateFieldNamesAsc.slice(1);
|
|
527
|
+
const durationFieldIndexes = /*@__PURE__*/ durationFieldNamesAsc.reduce((indexes, fieldName, i) => (indexes[fieldName] = i, indexes), {});
|
|
528
|
+
const durationFieldDefaults = /*@__PURE__*/ zipPropsConst(durationFieldNamesAsc, 0);
|
|
529
|
+
const durationTimeFieldDefaults = /*@__PURE__*/ zipPropsConst(durationTimeFieldNamesAsc, 0);
|
|
530
|
+
const clearDurationFields = /*@__PURE__*/ bindArgs(zeroOutProps, durationFieldNamesAsc);
|
|
529
531
|
function requirePropDefined(optionName, optionVal) {
|
|
530
532
|
return optionVal ?? throwRangeError(missingField(optionName)), optionVal;
|
|
531
533
|
}
|
|
532
|
-
const requireString =
|
|
534
|
+
const requireString = /*@__PURE__*/ bindArgs(requireType, "string");
|
|
533
535
|
function requireType(typeName, arg, entityName = typeName) {
|
|
534
536
|
return typeof arg !== typeName && throwTypeError(invalidEntity(entityName, arg)), arg;
|
|
535
537
|
}
|
|
@@ -641,17 +643,17 @@ function coerceChoiceOption(optionName, enumNameMap, options, defaultChoice = 0)
|
|
|
641
643
|
const enumNum = enumNameMap[enumStr];
|
|
642
644
|
return void 0 === enumNum && throwRangeError(invalidChoice(optionName, enumStr, enumNameMap)), enumNum;
|
|
643
645
|
}
|
|
644
|
-
const coerceSmallestUnit =
|
|
645
|
-
const coerceLargestUnit =
|
|
646
|
-
const coerceTotalUnit =
|
|
647
|
-
const coerceOverflow =
|
|
648
|
-
const coerceEpochDisambig =
|
|
649
|
-
const coerceOffsetDisambig =
|
|
650
|
-
const coerceCalendarDisplay =
|
|
651
|
-
const coerceTimeZoneDisplay =
|
|
652
|
-
const coerceOffsetDisplay =
|
|
653
|
-
const coerceRoundingMode =
|
|
654
|
-
const coerceDirection =
|
|
646
|
+
const coerceSmallestUnit = /*@__PURE__*/ bindArgs(coerceUnitOption, smallestUnitStr);
|
|
647
|
+
const coerceLargestUnit = /*@__PURE__*/ bindArgs(coerceUnitOption, "largestUnit");
|
|
648
|
+
const coerceTotalUnit = /*@__PURE__*/ bindArgs(coerceUnitOption, "unit");
|
|
649
|
+
const coerceOverflow = /*@__PURE__*/ bindArgs(coerceChoiceOption, "overflow", overflowMap);
|
|
650
|
+
const coerceEpochDisambig = /*@__PURE__*/ bindArgs(coerceChoiceOption, "disambiguation", epochDisambigMap);
|
|
651
|
+
const coerceOffsetDisambig = /*@__PURE__*/ bindArgs(coerceChoiceOption, "offset", offsetDisambigMap);
|
|
652
|
+
const coerceCalendarDisplay = /*@__PURE__*/ bindArgs(coerceChoiceOption, "calendarName", calendarDisplayMap);
|
|
653
|
+
const coerceTimeZoneDisplay = /*@__PURE__*/ bindArgs(coerceChoiceOption, "timeZoneName", timeZoneDisplayMap);
|
|
654
|
+
const coerceOffsetDisplay = /*@__PURE__*/ bindArgs(coerceChoiceOption, "offset", offsetDisplayMap);
|
|
655
|
+
const coerceRoundingMode = /*@__PURE__*/ bindArgs(coerceChoiceOption, "roundingMode", roundingModeMap);
|
|
656
|
+
const coerceDirection = /*@__PURE__*/ bindArgs(coerceChoiceOption, "direction", directionMap);
|
|
655
657
|
function validateRoundingInc(roundingInc, smallestUnit, allowManyLargeUnits, solarMode) {
|
|
656
658
|
const upUnitNano = solarMode ? nanoInUtcDay : unitNanoMap[smallestUnit + 1];
|
|
657
659
|
if (upUnitNano) {
|
|
@@ -705,8 +707,8 @@ function refineZonedFieldOptions(options, defaultOffsetDisambig = 0) {
|
|
|
705
707
|
epochDisambig
|
|
706
708
|
];
|
|
707
709
|
}
|
|
708
|
-
const epochNanoMax =
|
|
709
|
-
const epochNanoMin =
|
|
710
|
+
const epochNanoMax = /*@__PURE__*/ BigInt(1e8) * bigNanoInUtcDay;
|
|
711
|
+
const epochNanoMin = /*@__PURE__*/ BigInt(-1e8) * bigNanoInUtcDay;
|
|
710
712
|
const plainDateEpochNanoMin = epochNanoMin - bigNanoInUtcDay;
|
|
711
713
|
const isoYearMonthIndexMin = -3261848;
|
|
712
714
|
function checkIsoYearMonthInBounds(isoDate) {
|
|
@@ -977,11 +979,12 @@ function nudgeRelativeDuration(sign, durationFields, endEpochNano, _largestUnit,
|
|
|
977
979
|
}
|
|
978
980
|
function getTimeZoneTransitionEpochNanoseconds(slots, options) {
|
|
979
981
|
return slots.timeZone.U(slots.epochNanoseconds, ((options) => {
|
|
980
|
-
const
|
|
982
|
+
const normalizedOptions = normalizeOptionsOrString(options, "direction");
|
|
983
|
+
const res = coerceDirection(normalizedOptions, 0);
|
|
981
984
|
return res || throwRangeError(invalidEntity("direction", res)), res;
|
|
982
985
|
})(options));
|
|
983
986
|
}
|
|
984
|
-
const zonedEpochSlotsToIso =
|
|
987
|
+
const zonedEpochSlotsToIso = /*@__PURE__*/ memoize(_zonedEpochSlotsToIso, WeakMap);
|
|
985
988
|
function _zonedEpochSlotsToIso(slots) {
|
|
986
989
|
const { epochNanoseconds, timeZone } = slots;
|
|
987
990
|
const offsetNanoseconds = timeZone.C(epochNanoseconds);
|
|
@@ -1547,7 +1550,7 @@ function resolveTimeFields(fields, overflow) {
|
|
|
1547
1550
|
...fields
|
|
1548
1551
|
}), overflow);
|
|
1549
1552
|
}
|
|
1550
|
-
const offsetRegExp =
|
|
1553
|
+
const offsetRegExp = /*@__PURE__*/ createRegExp("([+-])(\\d{2})(?::?(\\d{2})(?::?(\\d{2})(?:[.,](\\d{1,9}))?)?)?");
|
|
1551
1554
|
function parseOffsetNano(s) {
|
|
1552
1555
|
const offsetNano = parseOffsetNanoMaybe(s);
|
|
1553
1556
|
return void 0 === offsetNano && throwRangeError(failedParse(s)), offsetNano;
|
|
@@ -1582,13 +1585,13 @@ const dateFieldRefiners = {
|
|
|
1582
1585
|
},
|
|
1583
1586
|
day: toPositiveIntegerWithTruncation
|
|
1584
1587
|
};
|
|
1585
|
-
const timeFieldRefiners =
|
|
1586
|
-
const durationFieldRefiners =
|
|
1588
|
+
const timeFieldRefiners = /*@__PURE__*/ zipPropsConst(timeFieldNamesAsc, toIntegerWithTrunc);
|
|
1589
|
+
const durationFieldRefiners = /*@__PURE__*/ zipPropsConst(durationFieldNamesAsc, toStrictInteger);
|
|
1587
1590
|
const offsetFieldRefiners = { offset(offsetString) {
|
|
1588
1591
|
return parseOffsetNano(toStringViaPrimitive(offsetString));
|
|
1589
1592
|
} };
|
|
1590
|
-
const dateTimeFieldRefiners =
|
|
1591
|
-
const zonedDateTimeFieldRefiners =
|
|
1593
|
+
const dateTimeFieldRefiners = /*@__PURE__*/ Object.assign({}, dateFieldRefiners, timeFieldRefiners);
|
|
1594
|
+
const zonedDateTimeFieldRefiners = /*@__PURE__*/ Object.assign({}, dateTimeFieldRefiners, offsetFieldRefiners);
|
|
1592
1595
|
function readAndRefineBagFields(bag, validFieldNames, fieldRefiners, requiredFieldNames, disallowEmpty = !requiredFieldNames) {
|
|
1593
1596
|
const res = {};
|
|
1594
1597
|
let anyMatching = 0;
|
|
@@ -1691,7 +1694,9 @@ const timeZonePeriodDaysByName = {
|
|
|
1691
1694
|
const minPossibleTransitionSec = -388152e4;
|
|
1692
1695
|
function refineTimeDisplayTuple(options, maxSmallestUnit = 4) {
|
|
1693
1696
|
const subsecDigits = coerceFractionalSecondDigits(options);
|
|
1694
|
-
|
|
1697
|
+
const roundingMode = coerceRoundingMode(options, 4);
|
|
1698
|
+
const smallestUnit = coerceSmallestUnit(options);
|
|
1699
|
+
return [roundingMode, ...resolveSmallestUnitAndSubsecDigits(validateUnitRange(smallestUnitStr, smallestUnit, 0, maxSmallestUnit), subsecDigits)];
|
|
1695
1700
|
}
|
|
1696
1701
|
function refineDateDisplayOptions(options) {
|
|
1697
1702
|
return coerceCalendarDisplay(normalizeOptions(options));
|
|
@@ -1866,15 +1871,17 @@ function resolveTimeZoneRecord(rawId) {
|
|
|
1866
1871
|
kind: "fixed",
|
|
1867
1872
|
...offsetRecord
|
|
1868
1873
|
};
|
|
1869
|
-
|
|
1874
|
+
const normId = "UTC" === upperRawId ? "UTC" : ((rawId) => (badCharactersRegExp.test(rawId) && throwRangeError(invalidTimeZone(rawId)), icuRegExp.test(rawId) && throwRangeError("Forbidden ICU TimeZone"), rawId.toLowerCase().split("/").map((part, partI) => (part.length <= 3 || /\d/.test(part)) && !/etc|yap/.test(part) ? part.toUpperCase() : part.replace(/baja|dumont|[a-z]+/g, (a, i) => a.length <= 2 && !partI || "in" === a || "chat" === a ? a.toUpperCase() : a.length > 2 || !i ? capitalize(a).replace(/island|noronha|murdo|rivadavia|urville/, capitalize) : a)).join("/")))(rawId);
|
|
1875
|
+
return queryNamedTimeZoneRecord(normId);
|
|
1870
1876
|
}
|
|
1871
|
-
const queryNamedTimeZoneRecord =
|
|
1877
|
+
const queryNamedTimeZoneRecord = /*@__PURE__*/ memoize((normId) => {
|
|
1872
1878
|
if ("UTC" === normId) return {
|
|
1873
1879
|
kind: "utc",
|
|
1874
1880
|
id: normId,
|
|
1875
1881
|
o: normId
|
|
1876
1882
|
};
|
|
1877
|
-
const
|
|
1883
|
+
const upperNormId = normId.toUpperCase();
|
|
1884
|
+
const format = queryTimeZoneIntlFormat(upperNormId);
|
|
1878
1885
|
return {
|
|
1879
1886
|
kind: "named",
|
|
1880
1887
|
id: normId,
|
|
@@ -1882,7 +1889,7 @@ const queryNamedTimeZoneRecord = /* @__PURE__ */ memoize((normId) => {
|
|
|
1882
1889
|
o: format.resolvedOptions().timeZone
|
|
1883
1890
|
};
|
|
1884
1891
|
});
|
|
1885
|
-
const queryTimeZoneIntlFormat =
|
|
1892
|
+
const queryTimeZoneIntlFormat = /*@__PURE__*/ memoize((upperNormId) => new RawDateTimeFormat("en-u-hc-h23", {
|
|
1886
1893
|
calendar: "iso8601",
|
|
1887
1894
|
timeZone: upperNormId,
|
|
1888
1895
|
era: "short",
|
|
@@ -1897,7 +1904,7 @@ function queryTimeZone(rawTimeZoneId) {
|
|
|
1897
1904
|
const record = resolveTimeZoneRecord(rawTimeZoneId);
|
|
1898
1905
|
return queryTimeZoneRecord(record.id, record);
|
|
1899
1906
|
}
|
|
1900
|
-
const queryTimeZoneRecord =
|
|
1907
|
+
const queryTimeZoneRecord = /*@__PURE__*/ memoize((normTimeZoneId, record) => "named" === record.kind ? new IntlTimeZone(normTimeZoneId, record.o, record.format) : new FixedTimeZone(normTimeZoneId, record.o, "fixed" === record.kind ? record._ : 0));
|
|
1901
1908
|
var FixedTimeZone = class {
|
|
1902
1909
|
constructor(id, compareKey, offsetNano) {
|
|
1903
1910
|
this.id = id, this.o = compareKey, this._ = offsetNano;
|
|
@@ -1979,7 +1986,8 @@ var IntlTimeZone = class {
|
|
|
1979
1986
|
return void 0 !== intlParts.era && "bce" === normalizeEraName(intlParts.era) ? 1 - year : year;
|
|
1980
1987
|
})(intlParts), parseInt(intlParts.month), parseInt(intlParts.day)) + 3600 * parseInt(intlParts.hour) + 60 * parseInt(intlParts.minute) + parseInt(intlParts.second) - epochSec;
|
|
1981
1988
|
})(format), ((timeZoneId) => {
|
|
1982
|
-
|
|
1989
|
+
const timeZoneName = timeZoneId.split("/").pop();
|
|
1990
|
+
return timeZonePeriodDaysByName[timeZoneName] || 60;
|
|
1983
1991
|
})(id));
|
|
1984
1992
|
}
|
|
1985
1993
|
C(epochNano) {
|
|
@@ -2203,12 +2211,12 @@ function offsetRegExpStr(separatorIndex) {
|
|
|
2203
2211
|
return "([+-])" + timeRegExpStr(separatorIndex);
|
|
2204
2212
|
}
|
|
2205
2213
|
const dateTimeRegExpStr = "(?:(?:([+-])(\\d{6}))|(\\d{4}))(-?)(\\d{2})\\4(\\d{2})(?:[T ]" + timeRegExpStr(8) + "(Z|" + offsetRegExpStr(15) + ")?)?";
|
|
2206
|
-
const yearMonthRegExp =
|
|
2207
|
-
const monthDayRegExp =
|
|
2208
|
-
const dateTimeRegExp =
|
|
2209
|
-
const timeRegExp =
|
|
2210
|
-
const annotationRegExp =
|
|
2211
|
-
const durationRegExp =
|
|
2214
|
+
const yearMonthRegExp = /*@__PURE__*/ createRegExp("(?:(?:([+-])(\\d{6}))|(\\d{4}))-?(\\d{2})((?:\\[(!?)([^\\]]*)\\]){0,9})");
|
|
2215
|
+
const monthDayRegExp = /*@__PURE__*/ createRegExp("(?:--)?(\\d{2})-?(\\d{2})((?:\\[(!?)([^\\]]*)\\]){0,9})");
|
|
2216
|
+
const dateTimeRegExp = /*@__PURE__*/ createRegExp(dateTimeRegExpStr + "((?:\\[(!?)([^\\]]*)\\]){0,9})");
|
|
2217
|
+
const timeRegExp = /*@__PURE__*/ createRegExp("T?" + timeRegExpStr(2) + `(${offsetRegExpStr(9)})?((?:\\[(!?)([^\\]]*)\\]){0,9})`);
|
|
2218
|
+
const annotationRegExp = /*@__PURE__*/ new RegExp("\\[(!?)([^\\]]*)\\]", "g");
|
|
2219
|
+
const durationRegExp = /*@__PURE__*/ createRegExp("([+-])?P(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?(?:T(?:(\\d+)(?:[.,](\\d{1,9}))?H)?(?:(\\d+)(?:[.,](\\d{1,9}))?M)?(?:(\\d+)(?:[.,](\\d{1,9}))?S)?)?");
|
|
2212
2220
|
function parseDateTimeLike(s) {
|
|
2213
2221
|
const parts = dateTimeRegExp.exec(s);
|
|
2214
2222
|
return parts ? ((parts) => {
|
|
@@ -2486,8 +2494,8 @@ const timeDefaultShapeFields = {
|
|
|
2486
2494
|
minute: "numeric",
|
|
2487
2495
|
second: "numeric"
|
|
2488
2496
|
};
|
|
2489
|
-
const dateTimeDefaultShapeFields =
|
|
2490
|
-
const zonedDateTimeDefaultShapeFields =
|
|
2497
|
+
const dateTimeDefaultShapeFields = /*@__PURE__*/ Object.assign({}, dateDefaultShapeFields, timeDefaultShapeFields);
|
|
2498
|
+
const zonedDateTimeDefaultShapeFields = /*@__PURE__*/ Object.assign({}, dateTimeDefaultShapeFields, { timeZoneName: "short" });
|
|
2491
2499
|
const yearMonthDefaultShapeFields = {
|
|
2492
2500
|
year: "numeric",
|
|
2493
2501
|
month: "numeric"
|
|
@@ -2511,15 +2519,15 @@ const timeShapeFieldNames = [
|
|
|
2511
2519
|
"fractionalSecondDigits",
|
|
2512
2520
|
"timeStyle"
|
|
2513
2521
|
];
|
|
2514
|
-
const dateTimeShapeFieldNames =
|
|
2515
|
-
const yearMonthIgnoredFieldNames =
|
|
2516
|
-
const monthDayIgnoredFieldNames =
|
|
2517
|
-
const transformInstantOptions =
|
|
2518
|
-
const transformZonedOptions =
|
|
2519
|
-
const transformDateTimeOptions =
|
|
2520
|
-
const transformDateOptions =
|
|
2521
|
-
const transformTimeOptions =
|
|
2522
|
-
const transformYearMonthOptions =
|
|
2522
|
+
const dateTimeShapeFieldNames = /*@__PURE__*/ dateShapeFieldNames.concat(timeShapeFieldNames);
|
|
2523
|
+
const yearMonthIgnoredFieldNames = /*@__PURE__*/ ["weekday", "day"].concat(timeShapeFieldNames);
|
|
2524
|
+
const monthDayIgnoredFieldNames = /*@__PURE__*/ ["weekday", "year"].concat(timeShapeFieldNames);
|
|
2525
|
+
const transformInstantOptions = /*@__PURE__*/ createOptionsTransformer(dateTimeShapeFieldNames, [], [], dateTimeDefaultShapeFields);
|
|
2526
|
+
const transformZonedOptions = /*@__PURE__*/ createOptionsTransformer(dateTimeShapeFieldNames, [], [], zonedDateTimeDefaultShapeFields);
|
|
2527
|
+
const transformDateTimeOptions = /*@__PURE__*/ createOptionsTransformer(dateTimeShapeFieldNames, [], ["timeZoneName"], dateTimeDefaultShapeFields);
|
|
2528
|
+
const transformDateOptions = /*@__PURE__*/ createOptionsTransformer(dateShapeFieldNames, timeShapeFieldNames, ["timeZoneName"], dateDefaultShapeFields);
|
|
2529
|
+
const transformTimeOptions = /*@__PURE__*/ createOptionsTransformer(timeShapeFieldNames, dateShapeFieldNames, ["timeZoneName", "era"], timeDefaultShapeFields);
|
|
2530
|
+
const transformYearMonthOptions = /*@__PURE__*/ createOptionsTransformer([
|
|
2523
2531
|
"year",
|
|
2524
2532
|
"month",
|
|
2525
2533
|
"dateStyle"
|
|
@@ -2541,7 +2549,7 @@ const transformYearMonthOptions = /* @__PURE__ */ createOptionsTransformer([
|
|
|
2541
2549
|
month: "numeric"
|
|
2542
2550
|
}
|
|
2543
2551
|
});
|
|
2544
|
-
const transformMonthDayOptions =
|
|
2552
|
+
const transformMonthDayOptions = /*@__PURE__*/ createOptionsTransformer([
|
|
2545
2553
|
"month",
|
|
2546
2554
|
"day",
|
|
2547
2555
|
"dateStyle"
|
|
@@ -2715,8 +2723,8 @@ function resolveBasicCalendarId(rawCalendarId) {
|
|
|
2715
2723
|
function resolveBasicCalendarArg(rawCalendarId = isoCalendarId) {
|
|
2716
2724
|
return resolveBasicCalendarId(rawCalendarId);
|
|
2717
2725
|
}
|
|
2718
|
-
const zonedDateTimeSlotsMap =
|
|
2719
|
-
const ZonedDateTime =
|
|
2726
|
+
const zonedDateTimeSlotsMap = /*@__PURE__*/ new WeakMap();
|
|
2727
|
+
const ZonedDateTime = /*@__PURE__*/ defineTemporalClass(ZonedDateTimeBranding, class {
|
|
2720
2728
|
constructor(epochNanoseconds, timeZoneId, calendar = void 0) {
|
|
2721
2729
|
const epochNano = checkEpochNanoInBounds(toBigInt(epochNanoseconds));
|
|
2722
2730
|
const timeZone = queryTimeZone(refineTimeZoneId(timeZoneId));
|
|
@@ -2864,8 +2872,8 @@ function refineTimeZoneArg(arg) {
|
|
|
2864
2872
|
}
|
|
2865
2873
|
return ((arg) => resolveTimeZoneId(parseTimeZoneId(requireString(arg))))(arg);
|
|
2866
2874
|
}
|
|
2867
|
-
const instantSlotsMap =
|
|
2868
|
-
const Instant =
|
|
2875
|
+
const instantSlotsMap = /*@__PURE__*/ new WeakMap();
|
|
2876
|
+
const Instant = /*@__PURE__*/ defineTemporalClass(InstantBranding, class {
|
|
2869
2877
|
constructor(epochNanoseconds) {
|
|
2870
2878
|
const epochNano = checkEpochNanoInBounds(toBigInt(epochNanoseconds));
|
|
2871
2879
|
initInstant(this, createEpochNanoSlots(epochNano));
|
|
@@ -2950,8 +2958,8 @@ const { toTemporalInstant: toTemporalInstant$1 } = { toTemporalInstant() {
|
|
|
2950
2958
|
function initInstant(instance, slots) {
|
|
2951
2959
|
return instantSlotsMap.set(instance, slots), attachDebugString(instance), instance;
|
|
2952
2960
|
}
|
|
2953
|
-
const plainMonthDaySlotsMap =
|
|
2954
|
-
const PlainMonthDay =
|
|
2961
|
+
const plainMonthDaySlotsMap = /*@__PURE__*/ new WeakMap();
|
|
2962
|
+
const PlainMonthDay = /*@__PURE__*/ defineTemporalClass(PlainMonthDayBranding, class {
|
|
2955
2963
|
constructor(isoMonth, isoDay, calendar = void 0, referenceIsoYear) {
|
|
2956
2964
|
const isoMonthInt = toIntegerWithTrunc(isoMonth);
|
|
2957
2965
|
const isoDayInt = toIntegerWithTrunc(isoDay);
|
|
@@ -3015,8 +3023,8 @@ function toPlainMonthDaySlots(arg, options) {
|
|
|
3015
3023
|
function initPlainMonthDay(instance, slots) {
|
|
3016
3024
|
return plainMonthDaySlotsMap.set(instance, slots), attachDebugString(instance), instance;
|
|
3017
3025
|
}
|
|
3018
|
-
const plainYearMonthSlotsMap =
|
|
3019
|
-
const PlainYearMonth =
|
|
3026
|
+
const plainYearMonthSlotsMap = /*@__PURE__*/ new WeakMap();
|
|
3027
|
+
const PlainYearMonth = /*@__PURE__*/ defineTemporalClass(PlainYearMonthBranding, class {
|
|
3020
3028
|
constructor(isoYear, isoMonth, calendar = void 0, referenceIsoDay) {
|
|
3021
3029
|
const isoYearInt = toIntegerWithTrunc(isoYear);
|
|
3022
3030
|
const isoMonthInt = toIntegerWithTrunc(isoMonth);
|
|
@@ -3108,8 +3116,8 @@ function getTemporalBrandingAndSlots(obj) {
|
|
|
3108
3116
|
function validateBag(bag) {
|
|
3109
3117
|
return (getTemporalBrandingAndSlots(bag) || void 0 !== bag.calendar || void 0 !== bag.timeZone) && throwTypeError("Invalid bag"), bag;
|
|
3110
3118
|
}
|
|
3111
|
-
const plainTimeSlotsMap =
|
|
3112
|
-
const PlainTime =
|
|
3119
|
+
const plainTimeSlotsMap = /*@__PURE__*/ new WeakMap();
|
|
3120
|
+
const PlainTime = /*@__PURE__*/ defineTemporalClass(PlainTimeBranding, class {
|
|
3113
3121
|
constructor(hour = 0, minute = 0, second = 0, millisecond = 0, microsecond = 0, nanosecond = 0) {
|
|
3114
3122
|
const fields = validateTimeFields(mapProps(toIntegerWithTrunc, {
|
|
3115
3123
|
hour,
|
|
@@ -3191,8 +3199,8 @@ function optionalToPlainTimeFields(timeArg) {
|
|
|
3191
3199
|
function initPlainTime(instance, slots) {
|
|
3192
3200
|
return plainTimeSlotsMap.set(instance, slots), attachDebugString(instance), instance;
|
|
3193
3201
|
}
|
|
3194
|
-
const plainDateTimeSlotsMap =
|
|
3195
|
-
const PlainDateTime =
|
|
3202
|
+
const plainDateTimeSlotsMap = /*@__PURE__*/ new WeakMap();
|
|
3203
|
+
const PlainDateTime = /*@__PURE__*/ defineTemporalClass(PlainDateTimeBranding, class {
|
|
3196
3204
|
constructor(isoYear, isoMonth, isoDay, hour = 0, minute = 0, second = 0, millisecond = 0, microsecond = 0, nanosecond = 0, calendar = void 0) {
|
|
3197
3205
|
const fields = checkIsoDateTimeInBounds(validateIsoDateTimeFields(mapProps(toIntegerWithTrunc, {
|
|
3198
3206
|
year: isoYear,
|
|
@@ -3303,8 +3311,8 @@ function toPlainDateTimeSlots(arg, options) {
|
|
|
3303
3311
|
function initPlainDateTime(instance, slots) {
|
|
3304
3312
|
return plainDateTimeSlotsMap.set(instance, slots), attachDebugString(instance), instance;
|
|
3305
3313
|
}
|
|
3306
|
-
const plainDateSlotsMap =
|
|
3307
|
-
const PlainDate =
|
|
3314
|
+
const plainDateSlotsMap = /*@__PURE__*/ new WeakMap();
|
|
3315
|
+
const PlainDate = /*@__PURE__*/ defineTemporalClass(PlainDateBranding, class {
|
|
3308
3316
|
constructor(isoYear, isoMonth, isoDay, calendar = void 0) {
|
|
3309
3317
|
const fields = checkIsoDateInBounds(validateIsoDateFields(mapProps(toIntegerWithTrunc, {
|
|
3310
3318
|
year: isoYear,
|
|
@@ -3422,8 +3430,8 @@ function refineCalendarArg(arg) {
|
|
|
3422
3430
|
}
|
|
3423
3431
|
return ((arg) => resolveBasicCalendarId(parseCalendarId(requireString(arg))))(arg);
|
|
3424
3432
|
}
|
|
3425
|
-
const durationSlotsMap =
|
|
3426
|
-
const Duration =
|
|
3433
|
+
const durationSlotsMap = /*@__PURE__*/ new WeakMap();
|
|
3434
|
+
const Duration = /*@__PURE__*/ defineTemporalClass(DurationBranding, class {
|
|
3427
3435
|
constructor(years = 0, months = 0, weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0, milliseconds = 0, microseconds = 0, nanoseconds = 0) {
|
|
3428
3436
|
const fields = validateDurationFields(mapProps(toStrictInteger, {
|
|
3429
3437
|
years,
|
|
@@ -3516,7 +3524,7 @@ function refinePublicRelativeTo(relativeTo) {
|
|
|
3516
3524
|
function initDuration(instance, slots) {
|
|
3517
3525
|
return durationSlotsMap.set(instance, slots), attachDebugString(instance), instance;
|
|
3518
3526
|
}
|
|
3519
|
-
const Now =
|
|
3527
|
+
const Now = /*@__PURE__*/ Object.defineProperties({}, {
|
|
3520
3528
|
...createStringTagDescriptors("Temporal.Now"),
|
|
3521
3529
|
...createPropDescriptors({
|
|
3522
3530
|
timeZoneId() {
|
|
@@ -3540,7 +3548,7 @@ const Now = /* @__PURE__ */ Object.defineProperties({}, {
|
|
|
3540
3548
|
}
|
|
3541
3549
|
})
|
|
3542
3550
|
});
|
|
3543
|
-
const Temporal$
|
|
3551
|
+
const Temporal$3 = /*@__PURE__*/ Object.defineProperties({}, {
|
|
3544
3552
|
...createStringTagDescriptors("Temporal"),
|
|
3545
3553
|
...createPropDescriptors({
|
|
3546
3554
|
PlainYearMonth,
|
|
@@ -3556,13 +3564,13 @@ const Temporal$2 = /* @__PURE__ */ Object.defineProperties({}, {
|
|
|
3556
3564
|
});
|
|
3557
3565
|
//#endregion
|
|
3558
3566
|
//#region ../../node_modules/.pnpm/temporal-polyfill@1.0.1/node_modules/temporal-polyfill/index.js
|
|
3559
|
-
const Temporal$
|
|
3567
|
+
const Temporal$2 = NativeTemporal || Temporal$3;
|
|
3560
3568
|
NativeTemporal && Date.prototype.toTemporalInstant;
|
|
3561
3569
|
//#endregion
|
|
3562
3570
|
//#region src/mq.ts
|
|
3563
|
-
const Temporal = Temporal$
|
|
3571
|
+
const Temporal$1 = Temporal$2;
|
|
3564
3572
|
const defaultEventName = "fedify:queue";
|
|
3565
|
-
const defaultOrderingRetryDelay = Temporal.Duration.from({ seconds: 5 });
|
|
3573
|
+
const defaultOrderingRetryDelay = Temporal$1.Duration.from({ seconds: 5 });
|
|
3566
3574
|
/**
|
|
3567
3575
|
* An error raised when an event send is not acknowledged.
|
|
3568
3576
|
*
|
|
@@ -3591,7 +3599,7 @@ var NetlifyMessageQueueSendError = class extends Error {
|
|
|
3591
3599
|
};
|
|
3592
3600
|
const orderingOptions = /* @__PURE__ */ new WeakMap();
|
|
3593
3601
|
function duration(value, defaultValue, name) {
|
|
3594
|
-
const result = value == null ? defaultValue : Temporal.Duration.from(value);
|
|
3602
|
+
const result = value == null ? defaultValue : Temporal$1.Duration.from(value);
|
|
3595
3603
|
const milliseconds = result.total("milliseconds");
|
|
3596
3604
|
if (!Number.isFinite(milliseconds) || milliseconds <= 0) throw new RangeError(`${name} must be a positive finite duration.`);
|
|
3597
3605
|
return result;
|
|
@@ -3782,7 +3790,7 @@ var NetlifyMessageQueue = class {
|
|
|
3782
3790
|
};
|
|
3783
3791
|
//#endregion
|
|
3784
3792
|
//#region src/handler.ts
|
|
3785
|
-
const messageTypes = new Set([
|
|
3793
|
+
const messageTypes = /* @__PURE__ */ new Set([
|
|
3786
3794
|
"fanout",
|
|
3787
3795
|
"inbox",
|
|
3788
3796
|
"outbox",
|
|
@@ -3878,6 +3886,117 @@ function createNetlifyQueueHandler(options) {
|
|
|
3878
3886
|
return createNetlifyQueueHandlerWith(_netlify_async_workloads.asyncWorkloadFn, options);
|
|
3879
3887
|
}
|
|
3880
3888
|
//#endregion
|
|
3889
|
+
//#region src/kv.ts
|
|
3890
|
+
const Temporal = Temporal$2;
|
|
3891
|
+
const MAX_BLOB_KEY_BYTES = 600;
|
|
3892
|
+
const BLOB_KEY_PREFIX = "fedify1.";
|
|
3893
|
+
const BLOB_KEY_SEPARATOR = ".";
|
|
3894
|
+
const textDecoder = new TextDecoder();
|
|
3895
|
+
/**
|
|
3896
|
+
* A key-value store that uses Netlify Blobs.
|
|
3897
|
+
*
|
|
3898
|
+
* @since 2.4.0
|
|
3899
|
+
*/
|
|
3900
|
+
var NetlifyBlobsKvStore = class {
|
|
3901
|
+
#store;
|
|
3902
|
+
/**
|
|
3903
|
+
* Creates a new Netlify Blobs-backed key-value store.
|
|
3904
|
+
* @param store The Netlify Blobs store to use.
|
|
3905
|
+
*/
|
|
3906
|
+
constructor(store) {
|
|
3907
|
+
this.#store = store;
|
|
3908
|
+
}
|
|
3909
|
+
serializeKey(key) {
|
|
3910
|
+
const serialized = BLOB_KEY_PREFIX + key.map((part) => (0, byte_encodings_base64url.encodeBase64Url)(part)).join(BLOB_KEY_SEPARATOR) + BLOB_KEY_SEPARATOR;
|
|
3911
|
+
if (new TextEncoder().encode(serialized).byteLength > MAX_BLOB_KEY_BYTES) throw new RangeError("The encoded key exceeds Netlify Blobs' 600-byte key limit.");
|
|
3912
|
+
return serialized;
|
|
3913
|
+
}
|
|
3914
|
+
deserializeKey(key) {
|
|
3915
|
+
return key.slice(8, -1).split(BLOB_KEY_SEPARATOR).map((part) => textDecoder.decode((0, byte_encodings_base64url.decodeBase64Url)(part)));
|
|
3916
|
+
}
|
|
3917
|
+
serializePrefix(prefix) {
|
|
3918
|
+
return prefix == null ? BLOB_KEY_PREFIX : this.serializeKey(prefix);
|
|
3919
|
+
}
|
|
3920
|
+
isAbsent(metadata) {
|
|
3921
|
+
if (metadata.tombstone === true) return true;
|
|
3922
|
+
const expireIn = metadata.expireIn;
|
|
3923
|
+
return typeof expireIn === "number" && expireIn <= Temporal.Now.instant().epochMilliseconds;
|
|
3924
|
+
}
|
|
3925
|
+
getEtag(entry) {
|
|
3926
|
+
if (entry?.etag == null) throw new Error("Netlify Blobs did not return an ETag.");
|
|
3927
|
+
return entry.etag;
|
|
3928
|
+
}
|
|
3929
|
+
/**
|
|
3930
|
+
* {@inheritDoc KvStore.get}
|
|
3931
|
+
*/
|
|
3932
|
+
async get(key) {
|
|
3933
|
+
const result = await this.#store.getWithMetadata(this.serializeKey(key), { type: "json" });
|
|
3934
|
+
if (result === null || this.isAbsent(result.metadata)) return void 0;
|
|
3935
|
+
return result.data;
|
|
3936
|
+
}
|
|
3937
|
+
/**
|
|
3938
|
+
* {@inheritDoc KvStore.set}
|
|
3939
|
+
*/
|
|
3940
|
+
async set(key, value, options) {
|
|
3941
|
+
const expireIn = options?.ttl == null ? null : Temporal.Now.instant().epochMilliseconds + options.ttl.total("millisecond");
|
|
3942
|
+
await this.#store.setJSON(this.serializeKey(key), value, { metadata: { "expireIn": expireIn } });
|
|
3943
|
+
}
|
|
3944
|
+
/**
|
|
3945
|
+
* {@inheritDoc KvStore.delete}
|
|
3946
|
+
*/
|
|
3947
|
+
delete(key) {
|
|
3948
|
+
return this.#store.delete(this.serializeKey(key));
|
|
3949
|
+
}
|
|
3950
|
+
/**
|
|
3951
|
+
* {@inheritDoc KvStore.list}
|
|
3952
|
+
*/
|
|
3953
|
+
async *list(prefix) {
|
|
3954
|
+
const pages = this.#store.list({
|
|
3955
|
+
paginate: true,
|
|
3956
|
+
prefix: this.serializePrefix(prefix)
|
|
3957
|
+
});
|
|
3958
|
+
for await (const page of pages) for (const blob of page.blobs) {
|
|
3959
|
+
const key = this.deserializeKey(blob.key);
|
|
3960
|
+
const value = await this.get(key);
|
|
3961
|
+
if (value === void 0) continue;
|
|
3962
|
+
yield {
|
|
3963
|
+
key,
|
|
3964
|
+
value
|
|
3965
|
+
};
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
/**
|
|
3969
|
+
* {@inheritDoc KvStore.cas}
|
|
3970
|
+
*/
|
|
3971
|
+
async cas(key, expectedValue, newValue, options) {
|
|
3972
|
+
while (true) {
|
|
3973
|
+
const entry = await this.#store.getWithMetadata(this.serializeKey(key), {
|
|
3974
|
+
consistency: "strong",
|
|
3975
|
+
type: "json"
|
|
3976
|
+
});
|
|
3977
|
+
if (!(0, es_toolkit.isEqual)(entry === null || this.isAbsent(entry.metadata) ? void 0 : entry.data, expectedValue)) return false;
|
|
3978
|
+
if (entry === null && newValue === void 0) return true;
|
|
3979
|
+
const serializedKey = this.serializeKey(key);
|
|
3980
|
+
if (newValue === void 0) {
|
|
3981
|
+
if ((await this.#store.setJSON(serializedKey, null, {
|
|
3982
|
+
onlyIfMatch: this.getEtag(entry),
|
|
3983
|
+
metadata: { tombstone: true }
|
|
3984
|
+
})).modified) return true;
|
|
3985
|
+
continue;
|
|
3986
|
+
}
|
|
3987
|
+
const metadata = { "expireIn": options?.ttl == null ? null : Temporal.Now.instant().epochMilliseconds + options.ttl.total("millisecond") };
|
|
3988
|
+
if ((entry === null ? await this.#store.setJSON(serializedKey, newValue, {
|
|
3989
|
+
onlyIfNew: true,
|
|
3990
|
+
metadata
|
|
3991
|
+
}) : await this.#store.setJSON(serializedKey, newValue, {
|
|
3992
|
+
onlyIfMatch: this.getEtag(entry),
|
|
3993
|
+
metadata
|
|
3994
|
+
})).modified) return true;
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
};
|
|
3998
|
+
//#endregion
|
|
3999
|
+
exports.NetlifyBlobsKvStore = NetlifyBlobsKvStore;
|
|
3881
4000
|
exports.NetlifyMessageQueue = NetlifyMessageQueue;
|
|
3882
4001
|
exports.NetlifyMessageQueueSendError = NetlifyMessageQueueSendError;
|
|
3883
4002
|
exports.createNetlifyQueueHandler = createNetlifyQueueHandler;
|
package/dist/mod.d.cts
CHANGED
|
@@ -1,9 +1,49 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { Federation, KvStore, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions } from "@fedify/fedify/federation";
|
|
2
|
+
import { Federation, KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions } from "@fedify/fedify/federation";
|
|
3
3
|
import { AsyncWorkloadEvent, CustomAsyncWorkloadEvent, asyncWorkloadFn } from "@netlify/async-workloads";
|
|
4
4
|
|
|
5
5
|
//#region src/types.d.ts
|
|
6
6
|
/**
|
|
7
|
+
* The subset of Netlify's Blobs `Store` used by this package.
|
|
8
|
+
*
|
|
9
|
+
* The real Netlify Blobs `Store` satisfies this interface. The narrower
|
|
10
|
+
* interface also keeps Netlify Blobs optional and makes it possible to supply
|
|
11
|
+
* a test double.
|
|
12
|
+
*
|
|
13
|
+
* @since 2.4.0
|
|
14
|
+
*/
|
|
15
|
+
interface NetlifyBlobsStore {
|
|
16
|
+
getWithMetadata(key: string, options: {
|
|
17
|
+
readonly consistency?: "eventual" | "strong";
|
|
18
|
+
readonly type: "json";
|
|
19
|
+
}): Promise<{
|
|
20
|
+
readonly data: unknown;
|
|
21
|
+
readonly etag?: string;
|
|
22
|
+
readonly metadata: Record<string, unknown>;
|
|
23
|
+
} | null>;
|
|
24
|
+
setJSON(key: string, data: unknown, options?: {
|
|
25
|
+
readonly metadata?: Record<string, unknown>;
|
|
26
|
+
} & ({
|
|
27
|
+
readonly onlyIfNew?: boolean;
|
|
28
|
+
readonly onlyIfMatch?: never;
|
|
29
|
+
} | {
|
|
30
|
+
readonly onlyIfMatch?: string;
|
|
31
|
+
readonly onlyIfNew?: never;
|
|
32
|
+
})): Promise<{
|
|
33
|
+
readonly etag?: string;
|
|
34
|
+
readonly modified: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
delete(key: string): Promise<void>;
|
|
37
|
+
list(options: {
|
|
38
|
+
readonly paginate: true;
|
|
39
|
+
readonly prefix?: string;
|
|
40
|
+
}): AsyncIterable<{
|
|
41
|
+
readonly blobs: readonly {
|
|
42
|
+
readonly key: string;
|
|
43
|
+
}[];
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
7
47
|
* The subset of Netlify's `AsyncWorkloadsClient` used by this package.
|
|
8
48
|
*
|
|
9
49
|
* The real `AsyncWorkloadsClient` satisfies this interface. The narrower
|
|
@@ -176,4 +216,44 @@ type WorkloadFunction = ReturnType<typeof asyncWorkloadFn<NetlifyQueueEvent>>;
|
|
|
176
216
|
*/
|
|
177
217
|
declare function createNetlifyQueueHandler<TContextData>(options: NetlifyQueueHandlerOptions<TContextData>): WorkloadFunction;
|
|
178
218
|
//#endregion
|
|
179
|
-
|
|
219
|
+
//#region src/kv.d.ts
|
|
220
|
+
/**
|
|
221
|
+
* A key-value store that uses Netlify Blobs.
|
|
222
|
+
*
|
|
223
|
+
* @since 2.4.0
|
|
224
|
+
*/
|
|
225
|
+
declare class NetlifyBlobsKvStore implements KvStore {
|
|
226
|
+
#private;
|
|
227
|
+
/**
|
|
228
|
+
* Creates a new Netlify Blobs-backed key-value store.
|
|
229
|
+
* @param store The Netlify Blobs store to use.
|
|
230
|
+
*/
|
|
231
|
+
constructor(store: NetlifyBlobsStore);
|
|
232
|
+
private serializeKey;
|
|
233
|
+
private deserializeKey;
|
|
234
|
+
private serializePrefix;
|
|
235
|
+
private isAbsent;
|
|
236
|
+
private getEtag;
|
|
237
|
+
/**
|
|
238
|
+
* {@inheritDoc KvStore.get}
|
|
239
|
+
*/
|
|
240
|
+
get<T = unknown>(key: KvKey): Promise<T | undefined>;
|
|
241
|
+
/**
|
|
242
|
+
* {@inheritDoc KvStore.set}
|
|
243
|
+
*/
|
|
244
|
+
set(key: KvKey, value: unknown, options?: KvStoreSetOptions | undefined): Promise<void>;
|
|
245
|
+
/**
|
|
246
|
+
* {@inheritDoc KvStore.delete}
|
|
247
|
+
*/
|
|
248
|
+
delete(key: KvKey): Promise<void>;
|
|
249
|
+
/**
|
|
250
|
+
* {@inheritDoc KvStore.list}
|
|
251
|
+
*/
|
|
252
|
+
list(prefix?: KvKey): AsyncIterable<KvStoreListEntry>;
|
|
253
|
+
/**
|
|
254
|
+
* {@inheritDoc KvStore.cas}
|
|
255
|
+
*/
|
|
256
|
+
cas(key: KvKey, expectedValue: unknown, newValue: unknown, options?: KvStoreSetOptions): Promise<boolean>;
|
|
257
|
+
}
|
|
258
|
+
//#endregion
|
|
259
|
+
export { type NetlifyAsyncWorkloadsClient, NetlifyBlobsKvStore, type NetlifyBlobsStore, NetlifyMessageQueue, type NetlifyMessageQueueOptions, NetlifyMessageQueueSendError, type NetlifyQueueEvent, type NetlifyQueueEventData, type NetlifyQueueHandlerOptions, createNetlifyQueueHandler };
|
package/dist/mod.d.ts
CHANGED
|
@@ -1,9 +1,49 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
2
|
import { AsyncWorkloadEvent, CustomAsyncWorkloadEvent, asyncWorkloadFn } from "@netlify/async-workloads";
|
|
3
|
-
import { Federation, KvStore, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions } from "@fedify/fedify/federation";
|
|
3
|
+
import { Federation, KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions, Message, MessageQueue, MessageQueueEnqueueOptions, MessageQueueListenOptions } from "@fedify/fedify/federation";
|
|
4
4
|
|
|
5
5
|
//#region src/types.d.ts
|
|
6
6
|
/**
|
|
7
|
+
* The subset of Netlify's Blobs `Store` used by this package.
|
|
8
|
+
*
|
|
9
|
+
* The real Netlify Blobs `Store` satisfies this interface. The narrower
|
|
10
|
+
* interface also keeps Netlify Blobs optional and makes it possible to supply
|
|
11
|
+
* a test double.
|
|
12
|
+
*
|
|
13
|
+
* @since 2.4.0
|
|
14
|
+
*/
|
|
15
|
+
interface NetlifyBlobsStore {
|
|
16
|
+
getWithMetadata(key: string, options: {
|
|
17
|
+
readonly consistency?: "eventual" | "strong";
|
|
18
|
+
readonly type: "json";
|
|
19
|
+
}): Promise<{
|
|
20
|
+
readonly data: unknown;
|
|
21
|
+
readonly etag?: string;
|
|
22
|
+
readonly metadata: Record<string, unknown>;
|
|
23
|
+
} | null>;
|
|
24
|
+
setJSON(key: string, data: unknown, options?: {
|
|
25
|
+
readonly metadata?: Record<string, unknown>;
|
|
26
|
+
} & ({
|
|
27
|
+
readonly onlyIfNew?: boolean;
|
|
28
|
+
readonly onlyIfMatch?: never;
|
|
29
|
+
} | {
|
|
30
|
+
readonly onlyIfMatch?: string;
|
|
31
|
+
readonly onlyIfNew?: never;
|
|
32
|
+
})): Promise<{
|
|
33
|
+
readonly etag?: string;
|
|
34
|
+
readonly modified: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
delete(key: string): Promise<void>;
|
|
37
|
+
list(options: {
|
|
38
|
+
readonly paginate: true;
|
|
39
|
+
readonly prefix?: string;
|
|
40
|
+
}): AsyncIterable<{
|
|
41
|
+
readonly blobs: readonly {
|
|
42
|
+
readonly key: string;
|
|
43
|
+
}[];
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
7
47
|
* The subset of Netlify's `AsyncWorkloadsClient` used by this package.
|
|
8
48
|
*
|
|
9
49
|
* The real `AsyncWorkloadsClient` satisfies this interface. The narrower
|
|
@@ -176,4 +216,44 @@ type WorkloadFunction = ReturnType<typeof asyncWorkloadFn<NetlifyQueueEvent>>;
|
|
|
176
216
|
*/
|
|
177
217
|
declare function createNetlifyQueueHandler<TContextData>(options: NetlifyQueueHandlerOptions<TContextData>): WorkloadFunction;
|
|
178
218
|
//#endregion
|
|
179
|
-
|
|
219
|
+
//#region src/kv.d.ts
|
|
220
|
+
/**
|
|
221
|
+
* A key-value store that uses Netlify Blobs.
|
|
222
|
+
*
|
|
223
|
+
* @since 2.4.0
|
|
224
|
+
*/
|
|
225
|
+
declare class NetlifyBlobsKvStore implements KvStore {
|
|
226
|
+
#private;
|
|
227
|
+
/**
|
|
228
|
+
* Creates a new Netlify Blobs-backed key-value store.
|
|
229
|
+
* @param store The Netlify Blobs store to use.
|
|
230
|
+
*/
|
|
231
|
+
constructor(store: NetlifyBlobsStore);
|
|
232
|
+
private serializeKey;
|
|
233
|
+
private deserializeKey;
|
|
234
|
+
private serializePrefix;
|
|
235
|
+
private isAbsent;
|
|
236
|
+
private getEtag;
|
|
237
|
+
/**
|
|
238
|
+
* {@inheritDoc KvStore.get}
|
|
239
|
+
*/
|
|
240
|
+
get<T = unknown>(key: KvKey): Promise<T | undefined>;
|
|
241
|
+
/**
|
|
242
|
+
* {@inheritDoc KvStore.set}
|
|
243
|
+
*/
|
|
244
|
+
set(key: KvKey, value: unknown, options?: KvStoreSetOptions | undefined): Promise<void>;
|
|
245
|
+
/**
|
|
246
|
+
* {@inheritDoc KvStore.delete}
|
|
247
|
+
*/
|
|
248
|
+
delete(key: KvKey): Promise<void>;
|
|
249
|
+
/**
|
|
250
|
+
* {@inheritDoc KvStore.list}
|
|
251
|
+
*/
|
|
252
|
+
list(prefix?: KvKey): AsyncIterable<KvStoreListEntry>;
|
|
253
|
+
/**
|
|
254
|
+
* {@inheritDoc KvStore.cas}
|
|
255
|
+
*/
|
|
256
|
+
cas(key: KvKey, expectedValue: unknown, newValue: unknown, options?: KvStoreSetOptions): Promise<boolean>;
|
|
257
|
+
}
|
|
258
|
+
//#endregion
|
|
259
|
+
export { type NetlifyAsyncWorkloadsClient, NetlifyBlobsKvStore, type NetlifyBlobsStore, NetlifyMessageQueue, type NetlifyMessageQueueOptions, NetlifyMessageQueueSendError, type NetlifyQueueEvent, type NetlifyQueueEventData, type NetlifyQueueHandlerOptions, createNetlifyQueueHandler };
|
package/dist/mod.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { ErrorDoNotRetry, asyncWorkloadFn } from "@netlify/async-workloads";
|
|
3
|
+
import { decodeBase64Url, encodeBase64Url } from "byte-encodings/base64url";
|
|
4
|
+
import { isEqual } from "es-toolkit";
|
|
3
5
|
//#region src/mq.ts
|
|
4
6
|
const defaultEventName = "fedify:queue";
|
|
5
7
|
const defaultOrderingRetryDelay = Temporal.Duration.from({ seconds: 5 });
|
|
@@ -222,7 +224,7 @@ var NetlifyMessageQueue = class {
|
|
|
222
224
|
};
|
|
223
225
|
//#endregion
|
|
224
226
|
//#region src/handler.ts
|
|
225
|
-
const messageTypes = new Set([
|
|
227
|
+
const messageTypes = /* @__PURE__ */ new Set([
|
|
226
228
|
"fanout",
|
|
227
229
|
"inbox",
|
|
228
230
|
"outbox",
|
|
@@ -318,4 +320,113 @@ function createNetlifyQueueHandler(options) {
|
|
|
318
320
|
return createNetlifyQueueHandlerWith(asyncWorkloadFn, options);
|
|
319
321
|
}
|
|
320
322
|
//#endregion
|
|
321
|
-
|
|
323
|
+
//#region src/kv.ts
|
|
324
|
+
const MAX_BLOB_KEY_BYTES = 600;
|
|
325
|
+
const BLOB_KEY_PREFIX = "fedify1.";
|
|
326
|
+
const BLOB_KEY_SEPARATOR = ".";
|
|
327
|
+
const textDecoder = new TextDecoder();
|
|
328
|
+
/**
|
|
329
|
+
* A key-value store that uses Netlify Blobs.
|
|
330
|
+
*
|
|
331
|
+
* @since 2.4.0
|
|
332
|
+
*/
|
|
333
|
+
var NetlifyBlobsKvStore = class {
|
|
334
|
+
#store;
|
|
335
|
+
/**
|
|
336
|
+
* Creates a new Netlify Blobs-backed key-value store.
|
|
337
|
+
* @param store The Netlify Blobs store to use.
|
|
338
|
+
*/
|
|
339
|
+
constructor(store) {
|
|
340
|
+
this.#store = store;
|
|
341
|
+
}
|
|
342
|
+
serializeKey(key) {
|
|
343
|
+
const serialized = BLOB_KEY_PREFIX + key.map((part) => encodeBase64Url(part)).join(BLOB_KEY_SEPARATOR) + BLOB_KEY_SEPARATOR;
|
|
344
|
+
if (new TextEncoder().encode(serialized).byteLength > MAX_BLOB_KEY_BYTES) throw new RangeError("The encoded key exceeds Netlify Blobs' 600-byte key limit.");
|
|
345
|
+
return serialized;
|
|
346
|
+
}
|
|
347
|
+
deserializeKey(key) {
|
|
348
|
+
return key.slice(8, -1).split(BLOB_KEY_SEPARATOR).map((part) => textDecoder.decode(decodeBase64Url(part)));
|
|
349
|
+
}
|
|
350
|
+
serializePrefix(prefix) {
|
|
351
|
+
return prefix == null ? BLOB_KEY_PREFIX : this.serializeKey(prefix);
|
|
352
|
+
}
|
|
353
|
+
isAbsent(metadata) {
|
|
354
|
+
if (metadata.tombstone === true) return true;
|
|
355
|
+
const expireIn = metadata.expireIn;
|
|
356
|
+
return typeof expireIn === "number" && expireIn <= Temporal.Now.instant().epochMilliseconds;
|
|
357
|
+
}
|
|
358
|
+
getEtag(entry) {
|
|
359
|
+
if (entry?.etag == null) throw new Error("Netlify Blobs did not return an ETag.");
|
|
360
|
+
return entry.etag;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* {@inheritDoc KvStore.get}
|
|
364
|
+
*/
|
|
365
|
+
async get(key) {
|
|
366
|
+
const result = await this.#store.getWithMetadata(this.serializeKey(key), { type: "json" });
|
|
367
|
+
if (result === null || this.isAbsent(result.metadata)) return void 0;
|
|
368
|
+
return result.data;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* {@inheritDoc KvStore.set}
|
|
372
|
+
*/
|
|
373
|
+
async set(key, value, options) {
|
|
374
|
+
const expireIn = options?.ttl == null ? null : Temporal.Now.instant().epochMilliseconds + options.ttl.total("millisecond");
|
|
375
|
+
await this.#store.setJSON(this.serializeKey(key), value, { metadata: { "expireIn": expireIn } });
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* {@inheritDoc KvStore.delete}
|
|
379
|
+
*/
|
|
380
|
+
delete(key) {
|
|
381
|
+
return this.#store.delete(this.serializeKey(key));
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* {@inheritDoc KvStore.list}
|
|
385
|
+
*/
|
|
386
|
+
async *list(prefix) {
|
|
387
|
+
const pages = this.#store.list({
|
|
388
|
+
paginate: true,
|
|
389
|
+
prefix: this.serializePrefix(prefix)
|
|
390
|
+
});
|
|
391
|
+
for await (const page of pages) for (const blob of page.blobs) {
|
|
392
|
+
const key = this.deserializeKey(blob.key);
|
|
393
|
+
const value = await this.get(key);
|
|
394
|
+
if (value === void 0) continue;
|
|
395
|
+
yield {
|
|
396
|
+
key,
|
|
397
|
+
value
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* {@inheritDoc KvStore.cas}
|
|
403
|
+
*/
|
|
404
|
+
async cas(key, expectedValue, newValue, options) {
|
|
405
|
+
while (true) {
|
|
406
|
+
const entry = await this.#store.getWithMetadata(this.serializeKey(key), {
|
|
407
|
+
consistency: "strong",
|
|
408
|
+
type: "json"
|
|
409
|
+
});
|
|
410
|
+
if (!isEqual(entry === null || this.isAbsent(entry.metadata) ? void 0 : entry.data, expectedValue)) return false;
|
|
411
|
+
if (entry === null && newValue === void 0) return true;
|
|
412
|
+
const serializedKey = this.serializeKey(key);
|
|
413
|
+
if (newValue === void 0) {
|
|
414
|
+
if ((await this.#store.setJSON(serializedKey, null, {
|
|
415
|
+
onlyIfMatch: this.getEtag(entry),
|
|
416
|
+
metadata: { tombstone: true }
|
|
417
|
+
})).modified) return true;
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
const metadata = { "expireIn": options?.ttl == null ? null : Temporal.Now.instant().epochMilliseconds + options.ttl.total("millisecond") };
|
|
421
|
+
if ((entry === null ? await this.#store.setJSON(serializedKey, newValue, {
|
|
422
|
+
onlyIfNew: true,
|
|
423
|
+
metadata
|
|
424
|
+
}) : await this.#store.setJSON(serializedKey, newValue, {
|
|
425
|
+
onlyIfMatch: this.getEtag(entry),
|
|
426
|
+
metadata
|
|
427
|
+
})).modified) return true;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
//#endregion
|
|
432
|
+
export { NetlifyBlobsKvStore, NetlifyMessageQueue, NetlifyMessageQueueSendError, createNetlifyQueueHandler };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/netlify",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0-dev.2271+49d937e2",
|
|
4
4
|
"description": "Integrate Fedify with Netlify Async Workloads",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Fedify",
|
|
@@ -47,22 +47,22 @@
|
|
|
47
47
|
"package.json"
|
|
48
48
|
],
|
|
49
49
|
"dependencies": {
|
|
50
|
+
"byte-encodings": "^1.0.11",
|
|
51
|
+
"es-toolkit": "1.46.1",
|
|
50
52
|
"temporal-polyfill": "^1.0.1"
|
|
51
53
|
},
|
|
52
54
|
"peerDependencies": {
|
|
53
55
|
"@netlify/async-workloads": "^0.0.106",
|
|
54
|
-
"@fedify/fedify": "^2.
|
|
56
|
+
"@fedify/fedify": "^2.5.0-dev.2271+49d937e2"
|
|
55
57
|
},
|
|
56
58
|
"devDependencies": {
|
|
57
59
|
"@js-temporal/polyfill": "^0.5.1",
|
|
58
|
-
"@netlify/database": "^1.1.0",
|
|
59
60
|
"@standard-schema/spec": "^1.1.0",
|
|
60
|
-
"@
|
|
61
|
-
"
|
|
62
|
-
"
|
|
61
|
+
"@netlify/blobs": "11.0.3",
|
|
62
|
+
"@types/node": "^24.12.2",
|
|
63
|
+
"netlify-cli": "^27.4.2",
|
|
63
64
|
"tsdown": "^0.22.0",
|
|
64
|
-
"typescript": "^6.0.0"
|
|
65
|
-
"@fedify/postgres": "^2.4.0-pr.934.40+3331d302"
|
|
65
|
+
"typescript": "^6.0.0"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build:self": "tsdown",
|