s3db.js 12.0.0 → 12.1.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/s3db.es.js CHANGED
@@ -1,25 +1,27 @@
1
1
  import crypto$1, { createHash } from 'crypto';
2
2
  import { customAlphabet, urlAlphabet } from 'nanoid';
3
3
  import EventEmitter from 'events';
4
- import { createServer, Agent } from 'http';
5
- import { Http2ServerRequest } from 'http2';
6
- import require$$3, { Readable, Transform, Writable } from 'stream';
4
+ import { Hono } from 'hono';
5
+ import { serve } from '@hono/node-server';
6
+ import { swaggerUI } from '@hono/swagger-ui';
7
7
  import { mkdir, copyFile, unlink, stat, access, readdir, writeFile, readFile, rm, watch } from 'fs/promises';
8
8
  import fs, { createReadStream, createWriteStream, realpathSync as realpathSync$1, readlinkSync, readdirSync, readdir as readdir$2, lstatSync, existsSync } from 'fs';
9
9
  import { pipeline } from 'stream/promises';
10
10
  import path$1, { join } from 'path';
11
+ import { Transform, Writable } from 'stream';
11
12
  import zlib from 'node:zlib';
12
13
  import os from 'os';
13
14
  import jsonStableStringify from 'json-stable-stringify';
14
15
  import os$1 from 'node:os';
15
16
  import { PromisePool } from '@supercharge/promise-pool';
16
17
  import { chunk, merge, isString, isEmpty, invert, uniq, cloneDeep, get, set, isObject, isFunction } from 'lodash-es';
18
+ import { Agent } from 'http';
17
19
  import { Agent as Agent$1 } from 'https';
18
20
  import { NodeHttpHandler } from '@smithy/node-http-handler';
19
21
  import { S3Client, PutObjectCommand, GetObjectCommand, HeadObjectCommand, CopyObjectCommand, DeleteObjectCommand, DeleteObjectsCommand, ListObjectsV2Command } from '@aws-sdk/client-s3';
20
22
  import { flatten, unflatten } from 'flat';
21
23
  import FastestValidator from 'fastest-validator';
22
- import { ReadableStream as ReadableStream$1 } from 'node:stream/web';
24
+ import { ReadableStream } from 'node:stream/web';
23
25
  import { fileURLToPath } from 'node:url';
24
26
  import { win32, posix } from 'node:path';
25
27
  import * as actualFS from 'node:fs';
@@ -27,24 +29,6 @@ import { realpath, readlink, readdir as readdir$1, lstat } from 'node:fs/promise
27
29
  import { EventEmitter as EventEmitter$1 } from 'node:events';
28
30
  import Stream from 'node:stream';
29
31
  import { StringDecoder } from 'node:string_decoder';
30
- import require$$0 from 'node:crypto';
31
- import require$$1 from 'child_process';
32
- import require$$5 from 'url';
33
-
34
- function _mergeNamespaces(n, m) {
35
- m.forEach(function (e) {
36
- e && typeof e !== 'string' && !Array.isArray(e) && Object.keys(e).forEach(function (k) {
37
- if (k !== 'default' && !(k in n)) {
38
- var d = Object.getOwnPropertyDescriptor(e, k);
39
- Object.defineProperty(n, k, d.get ? d : {
40
- enumerable: true,
41
- get: function () { return e[k]; }
42
- });
43
- }
44
- });
45
- });
46
- return Object.freeze(n);
47
- }
48
32
 
49
33
  const alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
50
34
  const base = alphabet.length;
@@ -1963,44 +1947,31 @@ class PluginStorage {
1963
1947
  * @returns {Promise<boolean>} True if extended, false if not found or no TTL
1964
1948
  */
1965
1949
  async touch(key, additionalSeconds) {
1966
- const [ok, err, response] = await tryFn(() => this.client.getObject(key));
1950
+ const [ok, err, response] = await tryFn(() => this.client.headObject(key));
1967
1951
  if (!ok) {
1968
1952
  return false;
1969
1953
  }
1970
1954
  const metadata = response.Metadata || {};
1971
1955
  const parsedMetadata = this._parseMetadataValues(metadata);
1972
- let data = parsedMetadata;
1973
- if (response.Body) {
1974
- const [ok2, err2, result] = await tryFn(async () => {
1975
- const bodyContent = await response.Body.transformToString();
1976
- if (bodyContent && bodyContent.trim()) {
1977
- const body = JSON.parse(bodyContent);
1978
- return { ...parsedMetadata, ...body };
1979
- }
1980
- return parsedMetadata;
1981
- });
1982
- if (!ok2) {
1983
- return false;
1984
- }
1985
- data = result;
1986
- }
1987
- const expiresAt = data._expiresat || data._expiresAt;
1956
+ const expiresAt = parsedMetadata._expiresat || parsedMetadata._expiresAt;
1988
1957
  if (!expiresAt) {
1989
1958
  return false;
1990
1959
  }
1991
- data._expiresAt = expiresAt + additionalSeconds * 1e3;
1992
- delete data._expiresat;
1993
- const { metadata: newMetadata, body: newBody } = this._applyBehavior(data, "body-overflow");
1994
- const putParams = {
1995
- key,
1996
- metadata: newMetadata,
1997
- contentType: "application/json"
1998
- };
1999
- if (newBody !== null) {
2000
- putParams.body = JSON.stringify(newBody);
2001
- }
2002
- const [putOk] = await tryFn(() => this.client.putObject(putParams));
2003
- return putOk;
1960
+ parsedMetadata._expiresAt = expiresAt + additionalSeconds * 1e3;
1961
+ delete parsedMetadata._expiresat;
1962
+ const encodedMetadata = {};
1963
+ for (const [metaKey, metaValue] of Object.entries(parsedMetadata)) {
1964
+ const { encoded } = metadataEncode(metaValue);
1965
+ encodedMetadata[metaKey] = encoded;
1966
+ }
1967
+ const [copyOk] = await tryFn(() => this.client.copyObject({
1968
+ from: key,
1969
+ to: key,
1970
+ metadata: encodedMetadata,
1971
+ metadataDirective: "REPLACE",
1972
+ contentType: response.ContentType || "application/json"
1973
+ }));
1974
+ return copyOk;
2004
1975
  }
2005
1976
  /**
2006
1977
  * Delete a single object
@@ -2135,12 +2106,41 @@ class PluginStorage {
2135
2106
  /**
2136
2107
  * Increment a counter value
2137
2108
  *
2109
+ * Optimization: Uses HEAD + COPY for existing counters to avoid body transfer.
2110
+ * Falls back to GET + PUT for non-existent counters or those with additional data.
2111
+ *
2138
2112
  * @param {string} key - S3 key
2139
2113
  * @param {number} amount - Amount to increment (default: 1)
2140
2114
  * @param {Object} options - Options (e.g., ttl)
2141
2115
  * @returns {Promise<number>} New value
2142
2116
  */
2143
2117
  async increment(key, amount = 1, options = {}) {
2118
+ const [headOk, headErr, headResponse] = await tryFn(() => this.client.headObject(key));
2119
+ if (headOk && headResponse.Metadata) {
2120
+ const metadata = headResponse.Metadata || {};
2121
+ const parsedMetadata = this._parseMetadataValues(metadata);
2122
+ const currentValue = parsedMetadata.value || 0;
2123
+ const newValue = currentValue + amount;
2124
+ parsedMetadata.value = newValue;
2125
+ if (options.ttl) {
2126
+ parsedMetadata._expiresAt = Date.now() + options.ttl * 1e3;
2127
+ }
2128
+ const encodedMetadata = {};
2129
+ for (const [metaKey, metaValue] of Object.entries(parsedMetadata)) {
2130
+ const { encoded } = metadataEncode(metaValue);
2131
+ encodedMetadata[metaKey] = encoded;
2132
+ }
2133
+ const [copyOk] = await tryFn(() => this.client.copyObject({
2134
+ from: key,
2135
+ to: key,
2136
+ metadata: encodedMetadata,
2137
+ metadataDirective: "REPLACE",
2138
+ contentType: headResponse.ContentType || "application/json"
2139
+ }));
2140
+ if (copyOk) {
2141
+ return newValue;
2142
+ }
2143
+ }
2144
2144
  const data = await this.get(key);
2145
2145
  const value = (data?.value || 0) + amount;
2146
2146
  await this.set(key, { value }, options);
@@ -2360,6 +2360,9 @@ class Plugin extends EventEmitter {
2360
2360
  * - Pode modificar argumentos/resultados.
2361
2361
  */
2362
2362
  addMiddleware(resource, methodName, middleware) {
2363
+ if (typeof resource[methodName] !== "function") {
2364
+ throw new Error(`Cannot add middleware to "${methodName}": method does not exist on resource "${resource.name || "unknown"}"`);
2365
+ }
2363
2366
  if (!resource._pluginMiddlewares) {
2364
2367
  resource._pluginMiddlewares = {};
2365
2368
  }
@@ -2451,2291 +2454,6 @@ const PluginObject = {
2451
2454
  }
2452
2455
  };
2453
2456
 
2454
- // src/compose.ts
2455
- var compose = (middleware, onError, onNotFound) => {
2456
- return (context, next) => {
2457
- let index = -1;
2458
- return dispatch(0);
2459
- async function dispatch(i) {
2460
- if (i <= index) {
2461
- throw new Error("next() called multiple times");
2462
- }
2463
- index = i;
2464
- let res;
2465
- let isError = false;
2466
- let handler;
2467
- if (middleware[i]) {
2468
- handler = middleware[i][0][0];
2469
- context.req.routeIndex = i;
2470
- } else {
2471
- handler = i === middleware.length && next || void 0;
2472
- }
2473
- if (handler) {
2474
- try {
2475
- res = await handler(context, () => dispatch(i + 1));
2476
- } catch (err) {
2477
- if (err instanceof Error && onError) {
2478
- context.error = err;
2479
- res = await onError(err, context);
2480
- isError = true;
2481
- } else {
2482
- throw err;
2483
- }
2484
- }
2485
- } else {
2486
- if (context.finalized === false && onNotFound) {
2487
- res = await onNotFound(context);
2488
- }
2489
- }
2490
- if (res && (context.finalized === false || isError)) {
2491
- context.res = res;
2492
- }
2493
- return context;
2494
- }
2495
- };
2496
- };
2497
-
2498
- // src/request/constants.ts
2499
- var GET_MATCH_RESULT = Symbol();
2500
-
2501
- // src/utils/body.ts
2502
- var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
2503
- const { all = false, dot = false } = options;
2504
- const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;
2505
- const contentType = headers.get("Content-Type");
2506
- if (contentType?.startsWith("multipart/form-data") || contentType?.startsWith("application/x-www-form-urlencoded")) {
2507
- return parseFormData(request, { all, dot });
2508
- }
2509
- return {};
2510
- };
2511
- async function parseFormData(request, options) {
2512
- const formData = await request.formData();
2513
- if (formData) {
2514
- return convertFormDataToBodyData(formData, options);
2515
- }
2516
- return {};
2517
- }
2518
- function convertFormDataToBodyData(formData, options) {
2519
- const form = /* @__PURE__ */ Object.create(null);
2520
- formData.forEach((value, key) => {
2521
- const shouldParseAllValues = options.all || key.endsWith("[]");
2522
- if (!shouldParseAllValues) {
2523
- form[key] = value;
2524
- } else {
2525
- handleParsingAllValues(form, key, value);
2526
- }
2527
- });
2528
- if (options.dot) {
2529
- Object.entries(form).forEach(([key, value]) => {
2530
- const shouldParseDotValues = key.includes(".");
2531
- if (shouldParseDotValues) {
2532
- handleParsingNestedValues(form, key, value);
2533
- delete form[key];
2534
- }
2535
- });
2536
- }
2537
- return form;
2538
- }
2539
- var handleParsingAllValues = (form, key, value) => {
2540
- if (form[key] !== void 0) {
2541
- if (Array.isArray(form[key])) {
2542
- form[key].push(value);
2543
- } else {
2544
- form[key] = [form[key], value];
2545
- }
2546
- } else {
2547
- if (!key.endsWith("[]")) {
2548
- form[key] = value;
2549
- } else {
2550
- form[key] = [value];
2551
- }
2552
- }
2553
- };
2554
- var handleParsingNestedValues = (form, key, value) => {
2555
- let nestedForm = form;
2556
- const keys = key.split(".");
2557
- keys.forEach((key2, index) => {
2558
- if (index === keys.length - 1) {
2559
- nestedForm[key2] = value;
2560
- } else {
2561
- if (!nestedForm[key2] || typeof nestedForm[key2] !== "object" || Array.isArray(nestedForm[key2]) || nestedForm[key2] instanceof File) {
2562
- nestedForm[key2] = /* @__PURE__ */ Object.create(null);
2563
- }
2564
- nestedForm = nestedForm[key2];
2565
- }
2566
- });
2567
- };
2568
-
2569
- // src/utils/url.ts
2570
- var splitPath = (path) => {
2571
- const paths = path.split("/");
2572
- if (paths[0] === "") {
2573
- paths.shift();
2574
- }
2575
- return paths;
2576
- };
2577
- var splitRoutingPath = (routePath) => {
2578
- const { groups, path } = extractGroupsFromPath(routePath);
2579
- const paths = splitPath(path);
2580
- return replaceGroupMarks(paths, groups);
2581
- };
2582
- var extractGroupsFromPath = (path) => {
2583
- const groups = [];
2584
- path = path.replace(/\{[^}]+\}/g, (match, index) => {
2585
- const mark = `@${index}`;
2586
- groups.push([mark, match]);
2587
- return mark;
2588
- });
2589
- return { groups, path };
2590
- };
2591
- var replaceGroupMarks = (paths, groups) => {
2592
- for (let i = groups.length - 1; i >= 0; i--) {
2593
- const [mark] = groups[i];
2594
- for (let j = paths.length - 1; j >= 0; j--) {
2595
- if (paths[j].includes(mark)) {
2596
- paths[j] = paths[j].replace(mark, groups[i][1]);
2597
- break;
2598
- }
2599
- }
2600
- }
2601
- return paths;
2602
- };
2603
- var patternCache = {};
2604
- var getPattern = (label, next) => {
2605
- if (label === "*") {
2606
- return "*";
2607
- }
2608
- const match = label.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
2609
- if (match) {
2610
- const cacheKey = `${label}#${next}`;
2611
- if (!patternCache[cacheKey]) {
2612
- if (match[2]) {
2613
- patternCache[cacheKey] = next && next[0] !== ":" && next[0] !== "*" ? [cacheKey, match[1], new RegExp(`^${match[2]}(?=/${next})`)] : [label, match[1], new RegExp(`^${match[2]}$`)];
2614
- } else {
2615
- patternCache[cacheKey] = [label, match[1], true];
2616
- }
2617
- }
2618
- return patternCache[cacheKey];
2619
- }
2620
- return null;
2621
- };
2622
- var tryDecode = (str, decoder) => {
2623
- try {
2624
- return decoder(str);
2625
- } catch {
2626
- return str.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match) => {
2627
- try {
2628
- return decoder(match);
2629
- } catch {
2630
- return match;
2631
- }
2632
- });
2633
- }
2634
- };
2635
- var tryDecodeURI = (str) => tryDecode(str, decodeURI);
2636
- var getPath = (request) => {
2637
- const url = request.url;
2638
- const start = url.indexOf("/", url.indexOf(":") + 4);
2639
- let i = start;
2640
- for (; i < url.length; i++) {
2641
- const charCode = url.charCodeAt(i);
2642
- if (charCode === 37) {
2643
- const queryIndex = url.indexOf("?", i);
2644
- const path = url.slice(start, queryIndex === -1 ? void 0 : queryIndex);
2645
- return tryDecodeURI(path.includes("%25") ? path.replace(/%25/g, "%2525") : path);
2646
- } else if (charCode === 63) {
2647
- break;
2648
- }
2649
- }
2650
- return url.slice(start, i);
2651
- };
2652
- var getPathNoStrict = (request) => {
2653
- const result = getPath(request);
2654
- return result.length > 1 && result.at(-1) === "/" ? result.slice(0, -1) : result;
2655
- };
2656
- var mergePath = (base, sub, ...rest) => {
2657
- if (rest.length) {
2658
- sub = mergePath(sub, ...rest);
2659
- }
2660
- return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`;
2661
- };
2662
- var checkOptionalParameter = (path) => {
2663
- if (path.charCodeAt(path.length - 1) !== 63 || !path.includes(":")) {
2664
- return null;
2665
- }
2666
- const segments = path.split("/");
2667
- const results = [];
2668
- let basePath = "";
2669
- segments.forEach((segment) => {
2670
- if (segment !== "" && !/\:/.test(segment)) {
2671
- basePath += "/" + segment;
2672
- } else if (/\:/.test(segment)) {
2673
- if (/\?/.test(segment)) {
2674
- if (results.length === 0 && basePath === "") {
2675
- results.push("/");
2676
- } else {
2677
- results.push(basePath);
2678
- }
2679
- const optionalSegment = segment.replace("?", "");
2680
- basePath += "/" + optionalSegment;
2681
- results.push(basePath);
2682
- } else {
2683
- basePath += "/" + segment;
2684
- }
2685
- }
2686
- });
2687
- return results.filter((v, i, a) => a.indexOf(v) === i);
2688
- };
2689
- var _decodeURI = (value) => {
2690
- if (!/[%+]/.test(value)) {
2691
- return value;
2692
- }
2693
- if (value.indexOf("+") !== -1) {
2694
- value = value.replace(/\+/g, " ");
2695
- }
2696
- return value.indexOf("%") !== -1 ? tryDecode(value, decodeURIComponent_) : value;
2697
- };
2698
- var _getQueryParam = (url, key, multiple) => {
2699
- let encoded;
2700
- if (!multiple && key && !/[%+]/.test(key)) {
2701
- let keyIndex2 = url.indexOf(`?${key}`, 8);
2702
- if (keyIndex2 === -1) {
2703
- keyIndex2 = url.indexOf(`&${key}`, 8);
2704
- }
2705
- while (keyIndex2 !== -1) {
2706
- const trailingKeyCode = url.charCodeAt(keyIndex2 + key.length + 1);
2707
- if (trailingKeyCode === 61) {
2708
- const valueIndex = keyIndex2 + key.length + 2;
2709
- const endIndex = url.indexOf("&", valueIndex);
2710
- return _decodeURI(url.slice(valueIndex, endIndex === -1 ? void 0 : endIndex));
2711
- } else if (trailingKeyCode == 38 || isNaN(trailingKeyCode)) {
2712
- return "";
2713
- }
2714
- keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1);
2715
- }
2716
- encoded = /[%+]/.test(url);
2717
- if (!encoded) {
2718
- return void 0;
2719
- }
2720
- }
2721
- const results = {};
2722
- encoded ??= /[%+]/.test(url);
2723
- let keyIndex = url.indexOf("?", 8);
2724
- while (keyIndex !== -1) {
2725
- const nextKeyIndex = url.indexOf("&", keyIndex + 1);
2726
- let valueIndex = url.indexOf("=", keyIndex);
2727
- if (valueIndex > nextKeyIndex && nextKeyIndex !== -1) {
2728
- valueIndex = -1;
2729
- }
2730
- let name = url.slice(
2731
- keyIndex + 1,
2732
- valueIndex === -1 ? nextKeyIndex === -1 ? void 0 : nextKeyIndex : valueIndex
2733
- );
2734
- if (encoded) {
2735
- name = _decodeURI(name);
2736
- }
2737
- keyIndex = nextKeyIndex;
2738
- if (name === "") {
2739
- continue;
2740
- }
2741
- let value;
2742
- if (valueIndex === -1) {
2743
- value = "";
2744
- } else {
2745
- value = url.slice(valueIndex + 1, nextKeyIndex === -1 ? void 0 : nextKeyIndex);
2746
- if (encoded) {
2747
- value = _decodeURI(value);
2748
- }
2749
- }
2750
- if (multiple) {
2751
- if (!(results[name] && Array.isArray(results[name]))) {
2752
- results[name] = [];
2753
- }
2754
- results[name].push(value);
2755
- } else {
2756
- results[name] ??= value;
2757
- }
2758
- }
2759
- return key ? results[key] : results;
2760
- };
2761
- var getQueryParam = _getQueryParam;
2762
- var getQueryParams = (url, key) => {
2763
- return _getQueryParam(url, key, true);
2764
- };
2765
- var decodeURIComponent_ = decodeURIComponent;
2766
-
2767
- // src/request.ts
2768
- var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
2769
- var HonoRequest = class {
2770
- raw;
2771
- #validatedData;
2772
- #matchResult;
2773
- routeIndex = 0;
2774
- path;
2775
- bodyCache = {};
2776
- constructor(request, path = "/", matchResult = [[]]) {
2777
- this.raw = request;
2778
- this.path = path;
2779
- this.#matchResult = matchResult;
2780
- this.#validatedData = {};
2781
- }
2782
- param(key) {
2783
- return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
2784
- }
2785
- #getDecodedParam(key) {
2786
- const paramKey = this.#matchResult[0][this.routeIndex][1][key];
2787
- const param = this.#getParamValue(paramKey);
2788
- return param && /\%/.test(param) ? tryDecodeURIComponent(param) : param;
2789
- }
2790
- #getAllDecodedParams() {
2791
- const decoded = {};
2792
- const keys = Object.keys(this.#matchResult[0][this.routeIndex][1]);
2793
- for (const key of keys) {
2794
- const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
2795
- if (value !== void 0) {
2796
- decoded[key] = /\%/.test(value) ? tryDecodeURIComponent(value) : value;
2797
- }
2798
- }
2799
- return decoded;
2800
- }
2801
- #getParamValue(paramKey) {
2802
- return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey;
2803
- }
2804
- query(key) {
2805
- return getQueryParam(this.url, key);
2806
- }
2807
- queries(key) {
2808
- return getQueryParams(this.url, key);
2809
- }
2810
- header(name) {
2811
- if (name) {
2812
- return this.raw.headers.get(name) ?? void 0;
2813
- }
2814
- const headerData = {};
2815
- this.raw.headers.forEach((value, key) => {
2816
- headerData[key] = value;
2817
- });
2818
- return headerData;
2819
- }
2820
- async parseBody(options) {
2821
- return this.bodyCache.parsedBody ??= await parseBody(this, options);
2822
- }
2823
- #cachedBody = (key) => {
2824
- const { bodyCache, raw } = this;
2825
- const cachedBody = bodyCache[key];
2826
- if (cachedBody) {
2827
- return cachedBody;
2828
- }
2829
- const anyCachedKey = Object.keys(bodyCache)[0];
2830
- if (anyCachedKey) {
2831
- return bodyCache[anyCachedKey].then((body) => {
2832
- if (anyCachedKey === "json") {
2833
- body = JSON.stringify(body);
2834
- }
2835
- return new Response(body)[key]();
2836
- });
2837
- }
2838
- return bodyCache[key] = raw[key]();
2839
- };
2840
- json() {
2841
- return this.#cachedBody("text").then((text) => JSON.parse(text));
2842
- }
2843
- text() {
2844
- return this.#cachedBody("text");
2845
- }
2846
- arrayBuffer() {
2847
- return this.#cachedBody("arrayBuffer");
2848
- }
2849
- blob() {
2850
- return this.#cachedBody("blob");
2851
- }
2852
- formData() {
2853
- return this.#cachedBody("formData");
2854
- }
2855
- addValidatedData(target, data) {
2856
- this.#validatedData[target] = data;
2857
- }
2858
- valid(target) {
2859
- return this.#validatedData[target];
2860
- }
2861
- get url() {
2862
- return this.raw.url;
2863
- }
2864
- get method() {
2865
- return this.raw.method;
2866
- }
2867
- get [GET_MATCH_RESULT]() {
2868
- return this.#matchResult;
2869
- }
2870
- get matchedRoutes() {
2871
- return this.#matchResult[0].map(([[, route]]) => route);
2872
- }
2873
- get routePath() {
2874
- return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path;
2875
- }
2876
- };
2877
-
2878
- // src/utils/html.ts
2879
- var HtmlEscapedCallbackPhase = {
2880
- Stringify: 1};
2881
- var raw = (value, callbacks) => {
2882
- const escapedString = new String(value);
2883
- escapedString.isEscaped = true;
2884
- escapedString.callbacks = callbacks;
2885
- return escapedString;
2886
- };
2887
- var escapeRe = /[&<>'"]/;
2888
- var stringBufferToString = async (buffer, callbacks) => {
2889
- let str = "";
2890
- callbacks ||= [];
2891
- const resolvedBuffer = await Promise.all(buffer);
2892
- for (let i = resolvedBuffer.length - 1; ; i--) {
2893
- str += resolvedBuffer[i];
2894
- i--;
2895
- if (i < 0) {
2896
- break;
2897
- }
2898
- let r = resolvedBuffer[i];
2899
- if (typeof r === "object") {
2900
- callbacks.push(...r.callbacks || []);
2901
- }
2902
- const isEscaped = r.isEscaped;
2903
- r = await (typeof r === "object" ? r.toString() : r);
2904
- if (typeof r === "object") {
2905
- callbacks.push(...r.callbacks || []);
2906
- }
2907
- if (r.isEscaped ?? isEscaped) {
2908
- str += r;
2909
- } else {
2910
- const buf = [str];
2911
- escapeToBuffer(r, buf);
2912
- str = buf[0];
2913
- }
2914
- }
2915
- return raw(str, callbacks);
2916
- };
2917
- var escapeToBuffer = (str, buffer) => {
2918
- const match = str.search(escapeRe);
2919
- if (match === -1) {
2920
- buffer[0] += str;
2921
- return;
2922
- }
2923
- let escape;
2924
- let index;
2925
- let lastIndex = 0;
2926
- for (index = match; index < str.length; index++) {
2927
- switch (str.charCodeAt(index)) {
2928
- case 34:
2929
- escape = "&quot;";
2930
- break;
2931
- case 39:
2932
- escape = "&#39;";
2933
- break;
2934
- case 38:
2935
- escape = "&amp;";
2936
- break;
2937
- case 60:
2938
- escape = "&lt;";
2939
- break;
2940
- case 62:
2941
- escape = "&gt;";
2942
- break;
2943
- default:
2944
- continue;
2945
- }
2946
- buffer[0] += str.substring(lastIndex, index) + escape;
2947
- lastIndex = index + 1;
2948
- }
2949
- buffer[0] += str.substring(lastIndex, index);
2950
- };
2951
- var resolveCallbackSync = (str) => {
2952
- const callbacks = str.callbacks;
2953
- if (!callbacks?.length) {
2954
- return str;
2955
- }
2956
- const buffer = [str];
2957
- const context = {};
2958
- callbacks.forEach((c) => c({ phase: HtmlEscapedCallbackPhase.Stringify, buffer, context }));
2959
- return buffer[0];
2960
- };
2961
- var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
2962
- if (typeof str === "object" && !(str instanceof String)) {
2963
- if (!(str instanceof Promise)) {
2964
- str = str.toString();
2965
- }
2966
- if (str instanceof Promise) {
2967
- str = await str;
2968
- }
2969
- }
2970
- const callbacks = str.callbacks;
2971
- if (!callbacks?.length) {
2972
- return Promise.resolve(str);
2973
- }
2974
- if (buffer) {
2975
- buffer[0] += str;
2976
- } else {
2977
- buffer = [str];
2978
- }
2979
- const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then(
2980
- (res) => Promise.all(
2981
- res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context, buffer))
2982
- ).then(() => buffer[0])
2983
- );
2984
- {
2985
- return resStr;
2986
- }
2987
- };
2988
-
2989
- // src/context.ts
2990
- var TEXT_PLAIN = "text/plain; charset=UTF-8";
2991
- var setDefaultContentType = (contentType, headers) => {
2992
- return {
2993
- "Content-Type": contentType,
2994
- ...headers
2995
- };
2996
- };
2997
- var Context = class {
2998
- #rawRequest;
2999
- #req;
3000
- env = {};
3001
- #var;
3002
- finalized = false;
3003
- error;
3004
- #status;
3005
- #executionCtx;
3006
- #res;
3007
- #layout;
3008
- #renderer;
3009
- #notFoundHandler;
3010
- #preparedHeaders;
3011
- #matchResult;
3012
- #path;
3013
- constructor(req, options) {
3014
- this.#rawRequest = req;
3015
- if (options) {
3016
- this.#executionCtx = options.executionCtx;
3017
- this.env = options.env;
3018
- this.#notFoundHandler = options.notFoundHandler;
3019
- this.#path = options.path;
3020
- this.#matchResult = options.matchResult;
3021
- }
3022
- }
3023
- get req() {
3024
- this.#req ??= new HonoRequest(this.#rawRequest, this.#path, this.#matchResult);
3025
- return this.#req;
3026
- }
3027
- get event() {
3028
- if (this.#executionCtx && "respondWith" in this.#executionCtx) {
3029
- return this.#executionCtx;
3030
- } else {
3031
- throw Error("This context has no FetchEvent");
3032
- }
3033
- }
3034
- get executionCtx() {
3035
- if (this.#executionCtx) {
3036
- return this.#executionCtx;
3037
- } else {
3038
- throw Error("This context has no ExecutionContext");
3039
- }
3040
- }
3041
- get res() {
3042
- return this.#res ||= new Response(null, {
3043
- headers: this.#preparedHeaders ??= new Headers()
3044
- });
3045
- }
3046
- set res(_res) {
3047
- if (this.#res && _res) {
3048
- _res = new Response(_res.body, _res);
3049
- for (const [k, v] of this.#res.headers.entries()) {
3050
- if (k === "content-type") {
3051
- continue;
3052
- }
3053
- if (k === "set-cookie") {
3054
- const cookies = this.#res.headers.getSetCookie();
3055
- _res.headers.delete("set-cookie");
3056
- for (const cookie of cookies) {
3057
- _res.headers.append("set-cookie", cookie);
3058
- }
3059
- } else {
3060
- _res.headers.set(k, v);
3061
- }
3062
- }
3063
- }
3064
- this.#res = _res;
3065
- this.finalized = true;
3066
- }
3067
- render = (...args) => {
3068
- this.#renderer ??= (content) => this.html(content);
3069
- return this.#renderer(...args);
3070
- };
3071
- setLayout = (layout) => this.#layout = layout;
3072
- getLayout = () => this.#layout;
3073
- setRenderer = (renderer) => {
3074
- this.#renderer = renderer;
3075
- };
3076
- header = (name, value, options) => {
3077
- if (this.finalized) {
3078
- this.#res = new Response(this.#res.body, this.#res);
3079
- }
3080
- const headers = this.#res ? this.#res.headers : this.#preparedHeaders ??= new Headers();
3081
- if (value === void 0) {
3082
- headers.delete(name);
3083
- } else if (options?.append) {
3084
- headers.append(name, value);
3085
- } else {
3086
- headers.set(name, value);
3087
- }
3088
- };
3089
- status = (status) => {
3090
- this.#status = status;
3091
- };
3092
- set = (key, value) => {
3093
- this.#var ??= /* @__PURE__ */ new Map();
3094
- this.#var.set(key, value);
3095
- };
3096
- get = (key) => {
3097
- return this.#var ? this.#var.get(key) : void 0;
3098
- };
3099
- get var() {
3100
- if (!this.#var) {
3101
- return {};
3102
- }
3103
- return Object.fromEntries(this.#var);
3104
- }
3105
- #newResponse(data, arg, headers) {
3106
- const responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders ?? new Headers();
3107
- if (typeof arg === "object" && "headers" in arg) {
3108
- const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers);
3109
- for (const [key, value] of argHeaders) {
3110
- if (key.toLowerCase() === "set-cookie") {
3111
- responseHeaders.append(key, value);
3112
- } else {
3113
- responseHeaders.set(key, value);
3114
- }
3115
- }
3116
- }
3117
- if (headers) {
3118
- for (const [k, v] of Object.entries(headers)) {
3119
- if (typeof v === "string") {
3120
- responseHeaders.set(k, v);
3121
- } else {
3122
- responseHeaders.delete(k);
3123
- for (const v2 of v) {
3124
- responseHeaders.append(k, v2);
3125
- }
3126
- }
3127
- }
3128
- }
3129
- const status = typeof arg === "number" ? arg : arg?.status ?? this.#status;
3130
- return new Response(data, { status, headers: responseHeaders });
3131
- }
3132
- newResponse = (...args) => this.#newResponse(...args);
3133
- body = (data, arg, headers) => this.#newResponse(data, arg, headers);
3134
- text = (text, arg, headers) => {
3135
- return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text) : this.#newResponse(
3136
- text,
3137
- arg,
3138
- setDefaultContentType(TEXT_PLAIN, headers)
3139
- );
3140
- };
3141
- json = (object, arg, headers) => {
3142
- return this.#newResponse(
3143
- JSON.stringify(object),
3144
- arg,
3145
- setDefaultContentType("application/json", headers)
3146
- );
3147
- };
3148
- html = (html, arg, headers) => {
3149
- const res = (html2) => this.#newResponse(html2, arg, setDefaultContentType("text/html; charset=UTF-8", headers));
3150
- return typeof html === "object" ? resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res) : res(html);
3151
- };
3152
- redirect = (location, status) => {
3153
- const locationString = String(location);
3154
- this.header(
3155
- "Location",
3156
- !/[^\x00-\xFF]/.test(locationString) ? locationString : encodeURI(locationString)
3157
- );
3158
- return this.newResponse(null, status ?? 302);
3159
- };
3160
- notFound = () => {
3161
- this.#notFoundHandler ??= () => new Response();
3162
- return this.#notFoundHandler(this);
3163
- };
3164
- };
3165
-
3166
- // src/router.ts
3167
- var METHOD_NAME_ALL = "ALL";
3168
- var METHOD_NAME_ALL_LOWERCASE = "all";
3169
- var METHODS = ["get", "post", "put", "delete", "options", "patch"];
3170
- var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built.";
3171
- var UnsupportedPathError = class extends Error {
3172
- };
3173
-
3174
- // src/utils/constants.ts
3175
- var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
3176
-
3177
- // src/hono-base.ts
3178
- var notFoundHandler = (c) => {
3179
- return c.text("404 Not Found", 404);
3180
- };
3181
- var errorHandler$1 = (err, c) => {
3182
- if ("getResponse" in err) {
3183
- const res = err.getResponse();
3184
- return c.newResponse(res.body, res);
3185
- }
3186
- console.error(err);
3187
- return c.text("Internal Server Error", 500);
3188
- };
3189
- var Hono$1 = class Hono {
3190
- get;
3191
- post;
3192
- put;
3193
- delete;
3194
- options;
3195
- patch;
3196
- all;
3197
- on;
3198
- use;
3199
- router;
3200
- getPath;
3201
- _basePath = "/";
3202
- #path = "/";
3203
- routes = [];
3204
- constructor(options = {}) {
3205
- const allMethods = [...METHODS, METHOD_NAME_ALL_LOWERCASE];
3206
- allMethods.forEach((method) => {
3207
- this[method] = (args1, ...args) => {
3208
- if (typeof args1 === "string") {
3209
- this.#path = args1;
3210
- } else {
3211
- this.#addRoute(method, this.#path, args1);
3212
- }
3213
- args.forEach((handler) => {
3214
- this.#addRoute(method, this.#path, handler);
3215
- });
3216
- return this;
3217
- };
3218
- });
3219
- this.on = (method, path, ...handlers) => {
3220
- for (const p of [path].flat()) {
3221
- this.#path = p;
3222
- for (const m of [method].flat()) {
3223
- handlers.map((handler) => {
3224
- this.#addRoute(m.toUpperCase(), this.#path, handler);
3225
- });
3226
- }
3227
- }
3228
- return this;
3229
- };
3230
- this.use = (arg1, ...handlers) => {
3231
- if (typeof arg1 === "string") {
3232
- this.#path = arg1;
3233
- } else {
3234
- this.#path = "*";
3235
- handlers.unshift(arg1);
3236
- }
3237
- handlers.forEach((handler) => {
3238
- this.#addRoute(METHOD_NAME_ALL, this.#path, handler);
3239
- });
3240
- return this;
3241
- };
3242
- const { strict, ...optionsWithoutStrict } = options;
3243
- Object.assign(this, optionsWithoutStrict);
3244
- this.getPath = strict ?? true ? options.getPath ?? getPath : getPathNoStrict;
3245
- }
3246
- #clone() {
3247
- const clone = new Hono$1({
3248
- router: this.router,
3249
- getPath: this.getPath
3250
- });
3251
- clone.errorHandler = this.errorHandler;
3252
- clone.#notFoundHandler = this.#notFoundHandler;
3253
- clone.routes = this.routes;
3254
- return clone;
3255
- }
3256
- #notFoundHandler = notFoundHandler;
3257
- errorHandler = errorHandler$1;
3258
- route(path, app) {
3259
- const subApp = this.basePath(path);
3260
- app.routes.map((r) => {
3261
- let handler;
3262
- if (app.errorHandler === errorHandler$1) {
3263
- handler = r.handler;
3264
- } else {
3265
- handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res;
3266
- handler[COMPOSED_HANDLER] = r.handler;
3267
- }
3268
- subApp.#addRoute(r.method, r.path, handler);
3269
- });
3270
- return this;
3271
- }
3272
- basePath(path) {
3273
- const subApp = this.#clone();
3274
- subApp._basePath = mergePath(this._basePath, path);
3275
- return subApp;
3276
- }
3277
- onError = (handler) => {
3278
- this.errorHandler = handler;
3279
- return this;
3280
- };
3281
- notFound = (handler) => {
3282
- this.#notFoundHandler = handler;
3283
- return this;
3284
- };
3285
- mount(path, applicationHandler, options) {
3286
- let replaceRequest;
3287
- let optionHandler;
3288
- if (options) {
3289
- if (typeof options === "function") {
3290
- optionHandler = options;
3291
- } else {
3292
- optionHandler = options.optionHandler;
3293
- if (options.replaceRequest === false) {
3294
- replaceRequest = (request) => request;
3295
- } else {
3296
- replaceRequest = options.replaceRequest;
3297
- }
3298
- }
3299
- }
3300
- const getOptions = optionHandler ? (c) => {
3301
- const options2 = optionHandler(c);
3302
- return Array.isArray(options2) ? options2 : [options2];
3303
- } : (c) => {
3304
- let executionContext = void 0;
3305
- try {
3306
- executionContext = c.executionCtx;
3307
- } catch {
3308
- }
3309
- return [c.env, executionContext];
3310
- };
3311
- replaceRequest ||= (() => {
3312
- const mergedPath = mergePath(this._basePath, path);
3313
- const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
3314
- return (request) => {
3315
- const url = new URL(request.url);
3316
- url.pathname = url.pathname.slice(pathPrefixLength) || "/";
3317
- return new Request(url, request);
3318
- };
3319
- })();
3320
- const handler = async (c, next) => {
3321
- const res = await applicationHandler(replaceRequest(c.req.raw), ...getOptions(c));
3322
- if (res) {
3323
- return res;
3324
- }
3325
- await next();
3326
- };
3327
- this.#addRoute(METHOD_NAME_ALL, mergePath(path, "*"), handler);
3328
- return this;
3329
- }
3330
- #addRoute(method, path, handler) {
3331
- method = method.toUpperCase();
3332
- path = mergePath(this._basePath, path);
3333
- const r = { basePath: this._basePath, path, method, handler };
3334
- this.router.add(method, path, [handler, r]);
3335
- this.routes.push(r);
3336
- }
3337
- #handleError(err, c) {
3338
- if (err instanceof Error) {
3339
- return this.errorHandler(err, c);
3340
- }
3341
- throw err;
3342
- }
3343
- #dispatch(request, executionCtx, env, method) {
3344
- if (method === "HEAD") {
3345
- return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))();
3346
- }
3347
- const path = this.getPath(request, { env });
3348
- const matchResult = this.router.match(method, path);
3349
- const c = new Context(request, {
3350
- path,
3351
- matchResult,
3352
- env,
3353
- executionCtx,
3354
- notFoundHandler: this.#notFoundHandler
3355
- });
3356
- if (matchResult[0].length === 1) {
3357
- let res;
3358
- try {
3359
- res = matchResult[0][0][0][0](c, async () => {
3360
- c.res = await this.#notFoundHandler(c);
3361
- });
3362
- } catch (err) {
3363
- return this.#handleError(err, c);
3364
- }
3365
- return res instanceof Promise ? res.then(
3366
- (resolved) => resolved || (c.finalized ? c.res : this.#notFoundHandler(c))
3367
- ).catch((err) => this.#handleError(err, c)) : res ?? this.#notFoundHandler(c);
3368
- }
3369
- const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler);
3370
- return (async () => {
3371
- try {
3372
- const context = await composed(c);
3373
- if (!context.finalized) {
3374
- throw new Error(
3375
- "Context is not finalized. Did you forget to return a Response object or `await next()`?"
3376
- );
3377
- }
3378
- return context.res;
3379
- } catch (err) {
3380
- return this.#handleError(err, c);
3381
- }
3382
- })();
3383
- }
3384
- fetch = (request, ...rest) => {
3385
- return this.#dispatch(request, rest[1], rest[0], request.method);
3386
- };
3387
- request = (input, requestInit, Env, executionCtx) => {
3388
- if (input instanceof Request) {
3389
- return this.fetch(requestInit ? new Request(input, requestInit) : input, Env, executionCtx);
3390
- }
3391
- input = input.toString();
3392
- return this.fetch(
3393
- new Request(
3394
- /^https?:\/\//.test(input) ? input : `http://localhost${mergePath("/", input)}`,
3395
- requestInit
3396
- ),
3397
- Env,
3398
- executionCtx
3399
- );
3400
- };
3401
- fire = () => {
3402
- addEventListener("fetch", (event) => {
3403
- event.respondWith(this.#dispatch(event.request, event, void 0, event.request.method));
3404
- });
3405
- };
3406
- };
3407
-
3408
- // src/router/reg-exp-router/matcher.ts
3409
- var emptyParam = [];
3410
- function match$1(method, path) {
3411
- const matchers = this.buildAllMatchers();
3412
- const match2 = (method2, path2) => {
3413
- const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
3414
- const staticMatch = matcher[2][path2];
3415
- if (staticMatch) {
3416
- return staticMatch;
3417
- }
3418
- const match3 = path2.match(matcher[0]);
3419
- if (!match3) {
3420
- return [[], emptyParam];
3421
- }
3422
- const index = match3.indexOf("", 1);
3423
- return [matcher[1][index], match3];
3424
- };
3425
- this.match = match2;
3426
- return match2(method, path);
3427
- }
3428
-
3429
- // src/router/reg-exp-router/node.ts
3430
- var LABEL_REG_EXP_STR = "[^/]+";
3431
- var ONLY_WILDCARD_REG_EXP_STR = ".*";
3432
- var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
3433
- var PATH_ERROR = Symbol();
3434
- var regExpMetaChars = new Set(".\\+*[^]$()");
3435
- function compareKey(a, b) {
3436
- if (a.length === 1) {
3437
- return b.length === 1 ? a < b ? -1 : 1 : -1;
3438
- }
3439
- if (b.length === 1) {
3440
- return 1;
3441
- }
3442
- if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
3443
- return 1;
3444
- } else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
3445
- return -1;
3446
- }
3447
- if (a === LABEL_REG_EXP_STR) {
3448
- return 1;
3449
- } else if (b === LABEL_REG_EXP_STR) {
3450
- return -1;
3451
- }
3452
- return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;
3453
- }
3454
- var Node$1 = class Node {
3455
- #index;
3456
- #varIndex;
3457
- #children = /* @__PURE__ */ Object.create(null);
3458
- insert(tokens, index, paramMap, context, pathErrorCheckOnly) {
3459
- if (tokens.length === 0) {
3460
- if (this.#index !== void 0) {
3461
- throw PATH_ERROR;
3462
- }
3463
- if (pathErrorCheckOnly) {
3464
- return;
3465
- }
3466
- this.#index = index;
3467
- return;
3468
- }
3469
- const [token, ...restTokens] = tokens;
3470
- const pattern = token === "*" ? restTokens.length === 0 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
3471
- let node;
3472
- if (pattern) {
3473
- const name = pattern[1];
3474
- let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
3475
- if (name && pattern[2]) {
3476
- if (regexpStr === ".*") {
3477
- throw PATH_ERROR;
3478
- }
3479
- regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
3480
- if (/\((?!\?:)/.test(regexpStr)) {
3481
- throw PATH_ERROR;
3482
- }
3483
- }
3484
- node = this.#children[regexpStr];
3485
- if (!node) {
3486
- if (Object.keys(this.#children).some(
3487
- (k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
3488
- )) {
3489
- throw PATH_ERROR;
3490
- }
3491
- if (pathErrorCheckOnly) {
3492
- return;
3493
- }
3494
- node = this.#children[regexpStr] = new Node$1();
3495
- if (name !== "") {
3496
- node.#varIndex = context.varIndex++;
3497
- }
3498
- }
3499
- if (!pathErrorCheckOnly && name !== "") {
3500
- paramMap.push([name, node.#varIndex]);
3501
- }
3502
- } else {
3503
- node = this.#children[token];
3504
- if (!node) {
3505
- if (Object.keys(this.#children).some(
3506
- (k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
3507
- )) {
3508
- throw PATH_ERROR;
3509
- }
3510
- if (pathErrorCheckOnly) {
3511
- return;
3512
- }
3513
- node = this.#children[token] = new Node$1();
3514
- }
3515
- }
3516
- node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);
3517
- }
3518
- buildRegExpStr() {
3519
- const childKeys = Object.keys(this.#children).sort(compareKey);
3520
- const strList = childKeys.map((k) => {
3521
- const c = this.#children[k];
3522
- return (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + c.buildRegExpStr();
3523
- });
3524
- if (typeof this.#index === "number") {
3525
- strList.unshift(`#${this.#index}`);
3526
- }
3527
- if (strList.length === 0) {
3528
- return "";
3529
- }
3530
- if (strList.length === 1) {
3531
- return strList[0];
3532
- }
3533
- return "(?:" + strList.join("|") + ")";
3534
- }
3535
- };
3536
-
3537
- // src/router/reg-exp-router/trie.ts
3538
- var Trie = class {
3539
- #context = { varIndex: 0 };
3540
- #root = new Node$1();
3541
- insert(path, index, pathErrorCheckOnly) {
3542
- const paramAssoc = [];
3543
- const groups = [];
3544
- for (let i = 0; ; ) {
3545
- let replaced = false;
3546
- path = path.replace(/\{[^}]+\}/g, (m) => {
3547
- const mark = `@\\${i}`;
3548
- groups[i] = [mark, m];
3549
- i++;
3550
- replaced = true;
3551
- return mark;
3552
- });
3553
- if (!replaced) {
3554
- break;
3555
- }
3556
- }
3557
- const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
3558
- for (let i = groups.length - 1; i >= 0; i--) {
3559
- const [mark] = groups[i];
3560
- for (let j = tokens.length - 1; j >= 0; j--) {
3561
- if (tokens[j].indexOf(mark) !== -1) {
3562
- tokens[j] = tokens[j].replace(mark, groups[i][1]);
3563
- break;
3564
- }
3565
- }
3566
- }
3567
- this.#root.insert(tokens, index, paramAssoc, this.#context, pathErrorCheckOnly);
3568
- return paramAssoc;
3569
- }
3570
- buildRegExp() {
3571
- let regexp = this.#root.buildRegExpStr();
3572
- if (regexp === "") {
3573
- return [/^$/, [], []];
3574
- }
3575
- let captureIndex = 0;
3576
- const indexReplacementMap = [];
3577
- const paramReplacementMap = [];
3578
- regexp = regexp.replace(/#(\d+)|@(\d+)|\.\*\$/g, (_, handlerIndex, paramIndex) => {
3579
- if (handlerIndex !== void 0) {
3580
- indexReplacementMap[++captureIndex] = Number(handlerIndex);
3581
- return "$()";
3582
- }
3583
- if (paramIndex !== void 0) {
3584
- paramReplacementMap[Number(paramIndex)] = ++captureIndex;
3585
- return "";
3586
- }
3587
- return "";
3588
- });
3589
- return [new RegExp(`^${regexp}`), indexReplacementMap, paramReplacementMap];
3590
- }
3591
- };
3592
-
3593
- // src/router/reg-exp-router/router.ts
3594
- var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
3595
- var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
3596
- function buildWildcardRegExp(path) {
3597
- return wildcardRegExpCache[path] ??= new RegExp(
3598
- path === "*" ? "" : `^${path.replace(
3599
- /\/\*$|([.\\+*[^\]$()])/g,
3600
- (_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)"
3601
- )}$`
3602
- );
3603
- }
3604
- function clearWildcardRegExpCache() {
3605
- wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
3606
- }
3607
- function buildMatcherFromPreprocessedRoutes(routes) {
3608
- const trie = new Trie();
3609
- const handlerData = [];
3610
- if (routes.length === 0) {
3611
- return nullMatcher;
3612
- }
3613
- const routesWithStaticPathFlag = routes.map(
3614
- (route) => [!/\*|\/:/.test(route[0]), ...route]
3615
- ).sort(
3616
- ([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length
3617
- );
3618
- const staticMap = /* @__PURE__ */ Object.create(null);
3619
- for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {
3620
- const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];
3621
- if (pathErrorCheckOnly) {
3622
- staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
3623
- } else {
3624
- j++;
3625
- }
3626
- let paramAssoc;
3627
- try {
3628
- paramAssoc = trie.insert(path, j, pathErrorCheckOnly);
3629
- } catch (e) {
3630
- throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
3631
- }
3632
- if (pathErrorCheckOnly) {
3633
- continue;
3634
- }
3635
- handlerData[j] = handlers.map(([h, paramCount]) => {
3636
- const paramIndexMap = /* @__PURE__ */ Object.create(null);
3637
- paramCount -= 1;
3638
- for (; paramCount >= 0; paramCount--) {
3639
- const [key, value] = paramAssoc[paramCount];
3640
- paramIndexMap[key] = value;
3641
- }
3642
- return [h, paramIndexMap];
3643
- });
3644
- }
3645
- const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
3646
- for (let i = 0, len = handlerData.length; i < len; i++) {
3647
- for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
3648
- const map = handlerData[i][j]?.[1];
3649
- if (!map) {
3650
- continue;
3651
- }
3652
- const keys = Object.keys(map);
3653
- for (let k = 0, len3 = keys.length; k < len3; k++) {
3654
- map[keys[k]] = paramReplacementMap[map[keys[k]]];
3655
- }
3656
- }
3657
- }
3658
- const handlerMap = [];
3659
- for (const i in indexReplacementMap) {
3660
- handlerMap[i] = handlerData[indexReplacementMap[i]];
3661
- }
3662
- return [regexp, handlerMap, staticMap];
3663
- }
3664
- function findMiddleware(middleware, path) {
3665
- if (!middleware) {
3666
- return void 0;
3667
- }
3668
- for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
3669
- if (buildWildcardRegExp(k).test(path)) {
3670
- return [...middleware[k]];
3671
- }
3672
- }
3673
- return void 0;
3674
- }
3675
- var RegExpRouter = class {
3676
- name = "RegExpRouter";
3677
- #middleware;
3678
- #routes;
3679
- constructor() {
3680
- this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
3681
- this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
3682
- }
3683
- add(method, path, handler) {
3684
- const middleware = this.#middleware;
3685
- const routes = this.#routes;
3686
- if (!middleware || !routes) {
3687
- throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
3688
- }
3689
- if (!middleware[method]) {
3690
- [middleware, routes].forEach((handlerMap) => {
3691
- handlerMap[method] = /* @__PURE__ */ Object.create(null);
3692
- Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {
3693
- handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];
3694
- });
3695
- });
3696
- }
3697
- if (path === "/*") {
3698
- path = "*";
3699
- }
3700
- const paramCount = (path.match(/\/:/g) || []).length;
3701
- if (/\*$/.test(path)) {
3702
- const re = buildWildcardRegExp(path);
3703
- if (method === METHOD_NAME_ALL) {
3704
- Object.keys(middleware).forEach((m) => {
3705
- middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
3706
- });
3707
- } else {
3708
- middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
3709
- }
3710
- Object.keys(middleware).forEach((m) => {
3711
- if (method === METHOD_NAME_ALL || method === m) {
3712
- Object.keys(middleware[m]).forEach((p) => {
3713
- re.test(p) && middleware[m][p].push([handler, paramCount]);
3714
- });
3715
- }
3716
- });
3717
- Object.keys(routes).forEach((m) => {
3718
- if (method === METHOD_NAME_ALL || method === m) {
3719
- Object.keys(routes[m]).forEach(
3720
- (p) => re.test(p) && routes[m][p].push([handler, paramCount])
3721
- );
3722
- }
3723
- });
3724
- return;
3725
- }
3726
- const paths = checkOptionalParameter(path) || [path];
3727
- for (let i = 0, len = paths.length; i < len; i++) {
3728
- const path2 = paths[i];
3729
- Object.keys(routes).forEach((m) => {
3730
- if (method === METHOD_NAME_ALL || method === m) {
3731
- routes[m][path2] ||= [
3732
- ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
3733
- ];
3734
- routes[m][path2].push([handler, paramCount - len + i + 1]);
3735
- }
3736
- });
3737
- }
3738
- }
3739
- match = match$1;
3740
- buildAllMatchers() {
3741
- const matchers = /* @__PURE__ */ Object.create(null);
3742
- Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => {
3743
- matchers[method] ||= this.#buildMatcher(method);
3744
- });
3745
- this.#middleware = this.#routes = void 0;
3746
- clearWildcardRegExpCache();
3747
- return matchers;
3748
- }
3749
- #buildMatcher(method) {
3750
- const routes = [];
3751
- let hasOwnRoute = method === METHOD_NAME_ALL;
3752
- [this.#middleware, this.#routes].forEach((r) => {
3753
- const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : [];
3754
- if (ownRoute.length !== 0) {
3755
- hasOwnRoute ||= true;
3756
- routes.push(...ownRoute);
3757
- } else if (method !== METHOD_NAME_ALL) {
3758
- routes.push(
3759
- ...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]])
3760
- );
3761
- }
3762
- });
3763
- if (!hasOwnRoute) {
3764
- return null;
3765
- } else {
3766
- return buildMatcherFromPreprocessedRoutes(routes);
3767
- }
3768
- }
3769
- };
3770
-
3771
- // src/router/smart-router/router.ts
3772
- var SmartRouter = class {
3773
- name = "SmartRouter";
3774
- #routers = [];
3775
- #routes = [];
3776
- constructor(init) {
3777
- this.#routers = init.routers;
3778
- }
3779
- add(method, path, handler) {
3780
- if (!this.#routes) {
3781
- throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
3782
- }
3783
- this.#routes.push([method, path, handler]);
3784
- }
3785
- match(method, path) {
3786
- if (!this.#routes) {
3787
- throw new Error("Fatal error");
3788
- }
3789
- const routers = this.#routers;
3790
- const routes = this.#routes;
3791
- const len = routers.length;
3792
- let i = 0;
3793
- let res;
3794
- for (; i < len; i++) {
3795
- const router = routers[i];
3796
- try {
3797
- for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) {
3798
- router.add(...routes[i2]);
3799
- }
3800
- res = router.match(method, path);
3801
- } catch (e) {
3802
- if (e instanceof UnsupportedPathError) {
3803
- continue;
3804
- }
3805
- throw e;
3806
- }
3807
- this.match = router.match.bind(router);
3808
- this.#routers = [router];
3809
- this.#routes = void 0;
3810
- break;
3811
- }
3812
- if (i === len) {
3813
- throw new Error("Fatal error");
3814
- }
3815
- this.name = `SmartRouter + ${this.activeRouter.name}`;
3816
- return res;
3817
- }
3818
- get activeRouter() {
3819
- if (this.#routes || this.#routers.length !== 1) {
3820
- throw new Error("No active router has been determined yet.");
3821
- }
3822
- return this.#routers[0];
3823
- }
3824
- };
3825
-
3826
- // src/router/trie-router/node.ts
3827
- var emptyParams = /* @__PURE__ */ Object.create(null);
3828
- var Node = class {
3829
- #methods;
3830
- #children;
3831
- #patterns;
3832
- #order = 0;
3833
- #params = emptyParams;
3834
- constructor(method, handler, children) {
3835
- this.#children = children || /* @__PURE__ */ Object.create(null);
3836
- this.#methods = [];
3837
- if (method && handler) {
3838
- const m = /* @__PURE__ */ Object.create(null);
3839
- m[method] = { handler, possibleKeys: [], score: 0 };
3840
- this.#methods = [m];
3841
- }
3842
- this.#patterns = [];
3843
- }
3844
- insert(method, path, handler) {
3845
- this.#order = ++this.#order;
3846
- let curNode = this;
3847
- const parts = splitRoutingPath(path);
3848
- const possibleKeys = [];
3849
- for (let i = 0, len = parts.length; i < len; i++) {
3850
- const p = parts[i];
3851
- const nextP = parts[i + 1];
3852
- const pattern = getPattern(p, nextP);
3853
- const key = Array.isArray(pattern) ? pattern[0] : p;
3854
- if (key in curNode.#children) {
3855
- curNode = curNode.#children[key];
3856
- if (pattern) {
3857
- possibleKeys.push(pattern[1]);
3858
- }
3859
- continue;
3860
- }
3861
- curNode.#children[key] = new Node();
3862
- if (pattern) {
3863
- curNode.#patterns.push(pattern);
3864
- possibleKeys.push(pattern[1]);
3865
- }
3866
- curNode = curNode.#children[key];
3867
- }
3868
- curNode.#methods.push({
3869
- [method]: {
3870
- handler,
3871
- possibleKeys: possibleKeys.filter((v, i, a) => a.indexOf(v) === i),
3872
- score: this.#order
3873
- }
3874
- });
3875
- return curNode;
3876
- }
3877
- #getHandlerSets(node, method, nodeParams, params) {
3878
- const handlerSets = [];
3879
- for (let i = 0, len = node.#methods.length; i < len; i++) {
3880
- const m = node.#methods[i];
3881
- const handlerSet = m[method] || m[METHOD_NAME_ALL];
3882
- const processedSet = {};
3883
- if (handlerSet !== void 0) {
3884
- handlerSet.params = /* @__PURE__ */ Object.create(null);
3885
- handlerSets.push(handlerSet);
3886
- if (nodeParams !== emptyParams || params && params !== emptyParams) {
3887
- for (let i2 = 0, len2 = handlerSet.possibleKeys.length; i2 < len2; i2++) {
3888
- const key = handlerSet.possibleKeys[i2];
3889
- const processed = processedSet[handlerSet.score];
3890
- handlerSet.params[key] = params?.[key] && !processed ? params[key] : nodeParams[key] ?? params?.[key];
3891
- processedSet[handlerSet.score] = true;
3892
- }
3893
- }
3894
- }
3895
- }
3896
- return handlerSets;
3897
- }
3898
- search(method, path) {
3899
- const handlerSets = [];
3900
- this.#params = emptyParams;
3901
- const curNode = this;
3902
- let curNodes = [curNode];
3903
- const parts = splitPath(path);
3904
- const curNodesQueue = [];
3905
- for (let i = 0, len = parts.length; i < len; i++) {
3906
- const part = parts[i];
3907
- const isLast = i === len - 1;
3908
- const tempNodes = [];
3909
- for (let j = 0, len2 = curNodes.length; j < len2; j++) {
3910
- const node = curNodes[j];
3911
- const nextNode = node.#children[part];
3912
- if (nextNode) {
3913
- nextNode.#params = node.#params;
3914
- if (isLast) {
3915
- if (nextNode.#children["*"]) {
3916
- handlerSets.push(
3917
- ...this.#getHandlerSets(nextNode.#children["*"], method, node.#params)
3918
- );
3919
- }
3920
- handlerSets.push(...this.#getHandlerSets(nextNode, method, node.#params));
3921
- } else {
3922
- tempNodes.push(nextNode);
3923
- }
3924
- }
3925
- for (let k = 0, len3 = node.#patterns.length; k < len3; k++) {
3926
- const pattern = node.#patterns[k];
3927
- const params = node.#params === emptyParams ? {} : { ...node.#params };
3928
- if (pattern === "*") {
3929
- const astNode = node.#children["*"];
3930
- if (astNode) {
3931
- handlerSets.push(...this.#getHandlerSets(astNode, method, node.#params));
3932
- astNode.#params = params;
3933
- tempNodes.push(astNode);
3934
- }
3935
- continue;
3936
- }
3937
- const [key, name, matcher] = pattern;
3938
- if (!part && !(matcher instanceof RegExp)) {
3939
- continue;
3940
- }
3941
- const child = node.#children[key];
3942
- const restPathString = parts.slice(i).join("/");
3943
- if (matcher instanceof RegExp) {
3944
- const m = matcher.exec(restPathString);
3945
- if (m) {
3946
- params[name] = m[0];
3947
- handlerSets.push(...this.#getHandlerSets(child, method, node.#params, params));
3948
- if (Object.keys(child.#children).length) {
3949
- child.#params = params;
3950
- const componentCount = m[0].match(/\//)?.length ?? 0;
3951
- const targetCurNodes = curNodesQueue[componentCount] ||= [];
3952
- targetCurNodes.push(child);
3953
- }
3954
- continue;
3955
- }
3956
- }
3957
- if (matcher === true || matcher.test(part)) {
3958
- params[name] = part;
3959
- if (isLast) {
3960
- handlerSets.push(...this.#getHandlerSets(child, method, params, node.#params));
3961
- if (child.#children["*"]) {
3962
- handlerSets.push(
3963
- ...this.#getHandlerSets(child.#children["*"], method, params, node.#params)
3964
- );
3965
- }
3966
- } else {
3967
- child.#params = params;
3968
- tempNodes.push(child);
3969
- }
3970
- }
3971
- }
3972
- }
3973
- curNodes = tempNodes.concat(curNodesQueue.shift() ?? []);
3974
- }
3975
- if (handlerSets.length > 1) {
3976
- handlerSets.sort((a, b) => {
3977
- return a.score - b.score;
3978
- });
3979
- }
3980
- return [handlerSets.map(({ handler, params }) => [handler, params])];
3981
- }
3982
- };
3983
-
3984
- // src/router/trie-router/router.ts
3985
- var TrieRouter = class {
3986
- name = "TrieRouter";
3987
- #node;
3988
- constructor() {
3989
- this.#node = new Node();
3990
- }
3991
- add(method, path, handler) {
3992
- const results = checkOptionalParameter(path);
3993
- if (results) {
3994
- for (let i = 0, len = results.length; i < len; i++) {
3995
- this.#node.insert(method, results[i], handler);
3996
- }
3997
- return;
3998
- }
3999
- this.#node.insert(method, path, handler);
4000
- }
4001
- match(method, path) {
4002
- return this.#node.search(method, path);
4003
- }
4004
- };
4005
-
4006
- // src/hono.ts
4007
- var Hono = class extends Hono$1 {
4008
- constructor(options = {}) {
4009
- super(options);
4010
- this.router = options.router ?? new SmartRouter({
4011
- routers: [new RegExpRouter(), new TrieRouter()]
4012
- });
4013
- }
4014
- };
4015
-
4016
- // src/server.ts
4017
- var RequestError = class extends Error {
4018
- constructor(message, options) {
4019
- super(message, options);
4020
- this.name = "RequestError";
4021
- }
4022
- };
4023
- var toRequestError = (e) => {
4024
- if (e instanceof RequestError) {
4025
- return e;
4026
- }
4027
- return new RequestError(e.message, { cause: e });
4028
- };
4029
- var GlobalRequest = global.Request;
4030
- var Request$1 = class Request extends GlobalRequest {
4031
- constructor(input, options) {
4032
- if (typeof input === "object" && getRequestCache in input) {
4033
- input = input[getRequestCache]();
4034
- }
4035
- if (typeof options?.body?.getReader !== "undefined") {
4036
- options.duplex ??= "half";
4037
- }
4038
- super(input, options);
4039
- }
4040
- };
4041
- var newHeadersFromIncoming = (incoming) => {
4042
- const headerRecord = [];
4043
- const rawHeaders = incoming.rawHeaders;
4044
- for (let i = 0; i < rawHeaders.length; i += 2) {
4045
- const { [i]: key, [i + 1]: value } = rawHeaders;
4046
- if (key.charCodeAt(0) !== /*:*/
4047
- 58) {
4048
- headerRecord.push([key, value]);
4049
- }
4050
- }
4051
- return new Headers(headerRecord);
4052
- };
4053
- var wrapBodyStream = Symbol("wrapBodyStream");
4054
- var newRequestFromIncoming = (method, url, headers, incoming, abortController) => {
4055
- const init = {
4056
- method,
4057
- headers,
4058
- signal: abortController.signal
4059
- };
4060
- if (method === "TRACE") {
4061
- init.method = "GET";
4062
- const req = new Request$1(url, init);
4063
- Object.defineProperty(req, "method", {
4064
- get() {
4065
- return "TRACE";
4066
- }
4067
- });
4068
- return req;
4069
- }
4070
- if (!(method === "GET" || method === "HEAD")) {
4071
- if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) {
4072
- init.body = new ReadableStream({
4073
- start(controller) {
4074
- controller.enqueue(incoming.rawBody);
4075
- controller.close();
4076
- }
4077
- });
4078
- } else if (incoming[wrapBodyStream]) {
4079
- let reader;
4080
- init.body = new ReadableStream({
4081
- async pull(controller) {
4082
- try {
4083
- reader ||= Readable.toWeb(incoming).getReader();
4084
- const { done, value } = await reader.read();
4085
- if (done) {
4086
- controller.close();
4087
- } else {
4088
- controller.enqueue(value);
4089
- }
4090
- } catch (error) {
4091
- controller.error(error);
4092
- }
4093
- }
4094
- });
4095
- } else {
4096
- init.body = Readable.toWeb(incoming);
4097
- }
4098
- }
4099
- return new Request$1(url, init);
4100
- };
4101
- var getRequestCache = Symbol("getRequestCache");
4102
- var requestCache = Symbol("requestCache");
4103
- var incomingKey = Symbol("incomingKey");
4104
- var urlKey = Symbol("urlKey");
4105
- var headersKey = Symbol("headersKey");
4106
- var abortControllerKey = Symbol("abortControllerKey");
4107
- var getAbortController = Symbol("getAbortController");
4108
- var requestPrototype = {
4109
- get method() {
4110
- return this[incomingKey].method || "GET";
4111
- },
4112
- get url() {
4113
- return this[urlKey];
4114
- },
4115
- get headers() {
4116
- return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]);
4117
- },
4118
- [getAbortController]() {
4119
- this[getRequestCache]();
4120
- return this[abortControllerKey];
4121
- },
4122
- [getRequestCache]() {
4123
- this[abortControllerKey] ||= new AbortController();
4124
- return this[requestCache] ||= newRequestFromIncoming(
4125
- this.method,
4126
- this[urlKey],
4127
- this.headers,
4128
- this[incomingKey],
4129
- this[abortControllerKey]
4130
- );
4131
- }
4132
- };
4133
- [
4134
- "body",
4135
- "bodyUsed",
4136
- "cache",
4137
- "credentials",
4138
- "destination",
4139
- "integrity",
4140
- "mode",
4141
- "redirect",
4142
- "referrer",
4143
- "referrerPolicy",
4144
- "signal",
4145
- "keepalive"
4146
- ].forEach((k) => {
4147
- Object.defineProperty(requestPrototype, k, {
4148
- get() {
4149
- return this[getRequestCache]()[k];
4150
- }
4151
- });
4152
- });
4153
- ["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => {
4154
- Object.defineProperty(requestPrototype, k, {
4155
- value: function() {
4156
- return this[getRequestCache]()[k]();
4157
- }
4158
- });
4159
- });
4160
- Object.setPrototypeOf(requestPrototype, Request$1.prototype);
4161
- var newRequest = (incoming, defaultHostname) => {
4162
- const req = Object.create(requestPrototype);
4163
- req[incomingKey] = incoming;
4164
- const incomingUrl = incoming.url || "";
4165
- if (incomingUrl[0] !== "/" && // short-circuit for performance. most requests are relative URL.
4166
- (incomingUrl.startsWith("http://") || incomingUrl.startsWith("https://"))) {
4167
- if (incoming instanceof Http2ServerRequest) {
4168
- throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
4169
- }
4170
- try {
4171
- const url2 = new URL(incomingUrl);
4172
- req[urlKey] = url2.href;
4173
- } catch (e) {
4174
- throw new RequestError("Invalid absolute URL", { cause: e });
4175
- }
4176
- return req;
4177
- }
4178
- const host = (incoming instanceof Http2ServerRequest ? incoming.authority : incoming.headers.host) || defaultHostname;
4179
- if (!host) {
4180
- throw new RequestError("Missing host header");
4181
- }
4182
- let scheme;
4183
- if (incoming instanceof Http2ServerRequest) {
4184
- scheme = incoming.scheme;
4185
- if (!(scheme === "http" || scheme === "https")) {
4186
- throw new RequestError("Unsupported scheme");
4187
- }
4188
- } else {
4189
- scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
4190
- }
4191
- const url = new URL(`${scheme}://${host}${incomingUrl}`);
4192
- if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
4193
- throw new RequestError("Invalid host header");
4194
- }
4195
- req[urlKey] = url.href;
4196
- return req;
4197
- };
4198
-
4199
- // src/response.ts
4200
- var responseCache = Symbol("responseCache");
4201
- var getResponseCache = Symbol("getResponseCache");
4202
- var cacheKey = Symbol("cache");
4203
- var GlobalResponse = global.Response;
4204
- var Response2 = class _Response {
4205
- #body;
4206
- #init;
4207
- [getResponseCache]() {
4208
- delete this[cacheKey];
4209
- return this[responseCache] ||= new GlobalResponse(this.#body, this.#init);
4210
- }
4211
- constructor(body, init) {
4212
- let headers;
4213
- this.#body = body;
4214
- if (init instanceof _Response) {
4215
- const cachedGlobalResponse = init[responseCache];
4216
- if (cachedGlobalResponse) {
4217
- this.#init = cachedGlobalResponse;
4218
- this[getResponseCache]();
4219
- return;
4220
- } else {
4221
- this.#init = init.#init;
4222
- headers = new Headers(init.#init.headers);
4223
- }
4224
- } else {
4225
- this.#init = init;
4226
- }
4227
- if (typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) {
4228
- headers ||= init?.headers || { "content-type": "text/plain; charset=UTF-8" };
4229
- this[cacheKey] = [init?.status || 200, body, headers];
4230
- }
4231
- }
4232
- get headers() {
4233
- const cache = this[cacheKey];
4234
- if (cache) {
4235
- if (!(cache[2] instanceof Headers)) {
4236
- cache[2] = new Headers(cache[2]);
4237
- }
4238
- return cache[2];
4239
- }
4240
- return this[getResponseCache]().headers;
4241
- }
4242
- get status() {
4243
- return this[cacheKey]?.[0] ?? this[getResponseCache]().status;
4244
- }
4245
- get ok() {
4246
- const status = this.status;
4247
- return status >= 200 && status < 300;
4248
- }
4249
- };
4250
- ["body", "bodyUsed", "redirected", "statusText", "trailers", "type", "url"].forEach((k) => {
4251
- Object.defineProperty(Response2.prototype, k, {
4252
- get() {
4253
- return this[getResponseCache]()[k];
4254
- }
4255
- });
4256
- });
4257
- ["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => {
4258
- Object.defineProperty(Response2.prototype, k, {
4259
- value: function() {
4260
- return this[getResponseCache]()[k]();
4261
- }
4262
- });
4263
- });
4264
- Object.setPrototypeOf(Response2, GlobalResponse);
4265
- Object.setPrototypeOf(Response2.prototype, GlobalResponse.prototype);
4266
-
4267
- // src/utils.ts
4268
- async function readWithoutBlocking(readPromise) {
4269
- return Promise.race([readPromise, Promise.resolve().then(() => Promise.resolve(void 0))]);
4270
- }
4271
- function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromise) {
4272
- const cancel = (error) => {
4273
- reader.cancel(error).catch(() => {
4274
- });
4275
- };
4276
- writable.on("close", cancel);
4277
- writable.on("error", cancel);
4278
- (currentReadPromise ?? reader.read()).then(flow, handleStreamError);
4279
- return reader.closed.finally(() => {
4280
- writable.off("close", cancel);
4281
- writable.off("error", cancel);
4282
- });
4283
- function handleStreamError(error) {
4284
- if (error) {
4285
- writable.destroy(error);
4286
- }
4287
- }
4288
- function onDrain() {
4289
- reader.read().then(flow, handleStreamError);
4290
- }
4291
- function flow({ done, value }) {
4292
- try {
4293
- if (done) {
4294
- writable.end();
4295
- } else if (!writable.write(value)) {
4296
- writable.once("drain", onDrain);
4297
- } else {
4298
- return reader.read().then(flow, handleStreamError);
4299
- }
4300
- } catch (e) {
4301
- handleStreamError(e);
4302
- }
4303
- }
4304
- }
4305
- function writeFromReadableStream(stream, writable) {
4306
- if (stream.locked) {
4307
- throw new TypeError("ReadableStream is locked.");
4308
- } else if (writable.destroyed) {
4309
- return;
4310
- }
4311
- return writeFromReadableStreamDefaultReader(stream.getReader(), writable);
4312
- }
4313
- var buildOutgoingHttpHeaders = (headers) => {
4314
- const res = {};
4315
- if (!(headers instanceof Headers)) {
4316
- headers = new Headers(headers ?? void 0);
4317
- }
4318
- const cookies = [];
4319
- for (const [k, v] of headers) {
4320
- if (k === "set-cookie") {
4321
- cookies.push(v);
4322
- } else {
4323
- res[k] = v;
4324
- }
4325
- }
4326
- if (cookies.length > 0) {
4327
- res["set-cookie"] = cookies;
4328
- }
4329
- res["content-type"] ??= "text/plain; charset=UTF-8";
4330
- return res;
4331
- };
4332
-
4333
- // src/utils/response/constants.ts
4334
- var X_ALREADY_SENT = "x-hono-already-sent";
4335
- var webFetch = global.fetch;
4336
- if (typeof global.crypto === "undefined") {
4337
- global.crypto = crypto$1;
4338
- }
4339
- global.fetch = (info, init) => {
4340
- init = {
4341
- // Disable compression handling so people can return the result of a fetch
4342
- // directly in the loader without messing with the Content-Encoding header.
4343
- compress: false,
4344
- ...init
4345
- };
4346
- return webFetch(info, init);
4347
- };
4348
-
4349
- // src/listener.ts
4350
- var outgoingEnded = Symbol("outgoingEnded");
4351
- var handleRequestError = () => new Response(null, {
4352
- status: 400
4353
- });
4354
- var handleFetchError = (e) => new Response(null, {
4355
- status: e instanceof Error && (e.name === "TimeoutError" || e.constructor.name === "TimeoutError") ? 504 : 500
4356
- });
4357
- var handleResponseError = (e, outgoing) => {
4358
- const err = e instanceof Error ? e : new Error("unknown error", { cause: e });
4359
- if (err.code === "ERR_STREAM_PREMATURE_CLOSE") {
4360
- console.info("The user aborted a request.");
4361
- } else {
4362
- console.error(e);
4363
- if (!outgoing.headersSent) {
4364
- outgoing.writeHead(500, { "Content-Type": "text/plain" });
4365
- }
4366
- outgoing.end(`Error: ${err.message}`);
4367
- outgoing.destroy(err);
4368
- }
4369
- };
4370
- var flushHeaders = (outgoing) => {
4371
- if ("flushHeaders" in outgoing && outgoing.writable) {
4372
- outgoing.flushHeaders();
4373
- }
4374
- };
4375
- var responseViaCache = async (res, outgoing) => {
4376
- let [status, body, header] = res[cacheKey];
4377
- if (header instanceof Headers) {
4378
- header = buildOutgoingHttpHeaders(header);
4379
- }
4380
- if (typeof body === "string") {
4381
- header["Content-Length"] = Buffer.byteLength(body);
4382
- } else if (body instanceof Uint8Array) {
4383
- header["Content-Length"] = body.byteLength;
4384
- } else if (body instanceof Blob) {
4385
- header["Content-Length"] = body.size;
4386
- }
4387
- outgoing.writeHead(status, header);
4388
- if (typeof body === "string" || body instanceof Uint8Array) {
4389
- outgoing.end(body);
4390
- } else if (body instanceof Blob) {
4391
- outgoing.end(new Uint8Array(await body.arrayBuffer()));
4392
- } else {
4393
- flushHeaders(outgoing);
4394
- await writeFromReadableStream(body, outgoing)?.catch(
4395
- (e) => handleResponseError(e, outgoing)
4396
- );
4397
- }
4398
- outgoing[outgoingEnded]?.();
4399
- };
4400
- var isPromise = (res) => typeof res.then === "function";
4401
- var responseViaResponseObject = async (res, outgoing, options = {}) => {
4402
- if (isPromise(res)) {
4403
- if (options.errorHandler) {
4404
- try {
4405
- res = await res;
4406
- } catch (err) {
4407
- const errRes = await options.errorHandler(err);
4408
- if (!errRes) {
4409
- return;
4410
- }
4411
- res = errRes;
4412
- }
4413
- } else {
4414
- res = await res.catch(handleFetchError);
4415
- }
4416
- }
4417
- if (cacheKey in res) {
4418
- return responseViaCache(res, outgoing);
4419
- }
4420
- const resHeaderRecord = buildOutgoingHttpHeaders(res.headers);
4421
- if (res.body) {
4422
- const reader = res.body.getReader();
4423
- const values = [];
4424
- let done = false;
4425
- let currentReadPromise = void 0;
4426
- if (resHeaderRecord["transfer-encoding"] !== "chunked") {
4427
- let maxReadCount = 2;
4428
- for (let i = 0; i < maxReadCount; i++) {
4429
- currentReadPromise ||= reader.read();
4430
- const chunk = await readWithoutBlocking(currentReadPromise).catch((e) => {
4431
- console.error(e);
4432
- done = true;
4433
- });
4434
- if (!chunk) {
4435
- if (i === 1) {
4436
- await new Promise((resolve) => setTimeout(resolve));
4437
- maxReadCount = 3;
4438
- continue;
4439
- }
4440
- break;
4441
- }
4442
- currentReadPromise = void 0;
4443
- if (chunk.value) {
4444
- values.push(chunk.value);
4445
- }
4446
- if (chunk.done) {
4447
- done = true;
4448
- break;
4449
- }
4450
- }
4451
- if (done && !("content-length" in resHeaderRecord)) {
4452
- resHeaderRecord["content-length"] = values.reduce((acc, value) => acc + value.length, 0);
4453
- }
4454
- }
4455
- outgoing.writeHead(res.status, resHeaderRecord);
4456
- values.forEach((value) => {
4457
- outgoing.write(value);
4458
- });
4459
- if (done) {
4460
- outgoing.end();
4461
- } else {
4462
- if (values.length === 0) {
4463
- flushHeaders(outgoing);
4464
- }
4465
- await writeFromReadableStreamDefaultReader(reader, outgoing, currentReadPromise);
4466
- }
4467
- } else if (resHeaderRecord[X_ALREADY_SENT]) ; else {
4468
- outgoing.writeHead(res.status, resHeaderRecord);
4469
- outgoing.end();
4470
- }
4471
- outgoing[outgoingEnded]?.();
4472
- };
4473
- var getRequestListener = (fetchCallback, options = {}) => {
4474
- const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
4475
- if (options.overrideGlobalObjects !== false && global.Request !== Request$1) {
4476
- Object.defineProperty(global, "Request", {
4477
- value: Request$1
4478
- });
4479
- Object.defineProperty(global, "Response", {
4480
- value: Response2
4481
- });
4482
- }
4483
- return async (incoming, outgoing) => {
4484
- let res, req;
4485
- try {
4486
- req = newRequest(incoming, options.hostname);
4487
- let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
4488
- if (!incomingEnded) {
4489
- ;
4490
- incoming[wrapBodyStream] = true;
4491
- incoming.on("end", () => {
4492
- incomingEnded = true;
4493
- });
4494
- if (incoming instanceof Http2ServerRequest) {
4495
- ;
4496
- outgoing[outgoingEnded] = () => {
4497
- if (!incomingEnded) {
4498
- setTimeout(() => {
4499
- if (!incomingEnded) {
4500
- setTimeout(() => {
4501
- incoming.destroy();
4502
- outgoing.destroy();
4503
- });
4504
- }
4505
- });
4506
- }
4507
- };
4508
- }
4509
- }
4510
- outgoing.on("close", () => {
4511
- const abortController = req[abortControllerKey];
4512
- if (abortController) {
4513
- if (incoming.errored) {
4514
- req[abortControllerKey].abort(incoming.errored.toString());
4515
- } else if (!outgoing.writableFinished) {
4516
- req[abortControllerKey].abort("Client connection prematurely closed.");
4517
- }
4518
- }
4519
- if (!incomingEnded) {
4520
- setTimeout(() => {
4521
- if (!incomingEnded) {
4522
- setTimeout(() => {
4523
- incoming.destroy();
4524
- });
4525
- }
4526
- });
4527
- }
4528
- });
4529
- res = fetchCallback(req, { incoming, outgoing });
4530
- if (cacheKey in res) {
4531
- return responseViaCache(res, outgoing);
4532
- }
4533
- } catch (e) {
4534
- if (!res) {
4535
- if (options.errorHandler) {
4536
- res = await options.errorHandler(req ? e : toRequestError(e));
4537
- if (!res) {
4538
- return;
4539
- }
4540
- } else if (!req) {
4541
- res = handleRequestError();
4542
- } else {
4543
- res = handleFetchError(e);
4544
- }
4545
- } else {
4546
- return handleResponseError(e, outgoing);
4547
- }
4548
- }
4549
- try {
4550
- return await responseViaResponseObject(res, outgoing, options);
4551
- } catch (e) {
4552
- return handleResponseError(e, outgoing);
4553
- }
4554
- };
4555
- };
4556
-
4557
- // src/server.ts
4558
- var createAdaptorServer = (options) => {
4559
- const fetchCallback = options.fetch;
4560
- const requestListener = getRequestListener(fetchCallback, {
4561
- hostname: options.hostname,
4562
- overrideGlobalObjects: options.overrideGlobalObjects,
4563
- autoCleanupIncoming: options.autoCleanupIncoming
4564
- });
4565
- const createServer$1 = options.createServer || createServer;
4566
- const server = createServer$1(options.serverOptions || {}, requestListener);
4567
- return server;
4568
- };
4569
- var serve = (options, listeningListener) => {
4570
- const server = createAdaptorServer(options);
4571
- server.listen(options?.port ?? 3e3, options.hostname, () => {
4572
- const serverInfo = server.address();
4573
- listeningListener && listeningListener(serverInfo);
4574
- });
4575
- return server;
4576
- };
4577
-
4578
- // src/helper/html/index.ts
4579
- var html = (strings, ...values) => {
4580
- const buffer = [""];
4581
- for (let i = 0, len = strings.length - 1; i < len; i++) {
4582
- buffer[0] += strings[i];
4583
- const children = Array.isArray(values[i]) ? values[i].flat(Infinity) : [values[i]];
4584
- for (let i2 = 0, len2 = children.length; i2 < len2; i2++) {
4585
- const child = children[i2];
4586
- if (typeof child === "string") {
4587
- escapeToBuffer(child, buffer);
4588
- } else if (typeof child === "number") {
4589
- buffer[0] += child;
4590
- } else if (typeof child === "boolean" || child === null || child === void 0) {
4591
- continue;
4592
- } else if (typeof child === "object" && child.isEscaped) {
4593
- if (child.callbacks) {
4594
- buffer.unshift("", child);
4595
- } else {
4596
- const tmp = child.toString();
4597
- if (tmp instanceof Promise) {
4598
- buffer.unshift("", tmp);
4599
- } else {
4600
- buffer[0] += tmp;
4601
- }
4602
- }
4603
- } else if (child instanceof Promise) {
4604
- buffer.unshift("", child);
4605
- } else {
4606
- escapeToBuffer(child.toString(), buffer);
4607
- }
4608
- }
4609
- }
4610
- buffer[0] += strings.at(-1);
4611
- return buffer.length === 1 ? "callbacks" in buffer ? raw(resolveCallbackSync(raw(buffer[0], buffer.callbacks))) : raw(buffer[0]) : stringBufferToString(buffer, buffer.callbacks);
4612
- };
4613
-
4614
- // src/index.ts
4615
-
4616
- // src/swagger/renderer.ts
4617
- var RENDER_TYPE = {
4618
- STRING_ARRAY: "string_array",
4619
- STRING: "string",
4620
- JSON_STRING: "json_string",
4621
- RAW: "raw"
4622
- };
4623
- var RENDER_TYPE_MAP = {
4624
- configUrl: RENDER_TYPE.STRING,
4625
- deepLinking: RENDER_TYPE.RAW,
4626
- presets: RENDER_TYPE.STRING_ARRAY,
4627
- plugins: RENDER_TYPE.STRING_ARRAY,
4628
- spec: RENDER_TYPE.JSON_STRING,
4629
- url: RENDER_TYPE.STRING,
4630
- urls: RENDER_TYPE.JSON_STRING,
4631
- layout: RENDER_TYPE.STRING,
4632
- docExpansion: RENDER_TYPE.STRING,
4633
- maxDisplayedTags: RENDER_TYPE.RAW,
4634
- operationsSorter: RENDER_TYPE.RAW,
4635
- requestInterceptor: RENDER_TYPE.RAW,
4636
- responseInterceptor: RENDER_TYPE.RAW,
4637
- persistAuthorization: RENDER_TYPE.RAW,
4638
- defaultModelsExpandDepth: RENDER_TYPE.RAW,
4639
- defaultModelExpandDepth: RENDER_TYPE.RAW,
4640
- defaultModelRendering: RENDER_TYPE.STRING,
4641
- displayRequestDuration: RENDER_TYPE.RAW,
4642
- filter: RENDER_TYPE.RAW,
4643
- showExtensions: RENDER_TYPE.RAW,
4644
- showCommonExtensions: RENDER_TYPE.RAW,
4645
- queryConfigEnabled: RENDER_TYPE.RAW,
4646
- displayOperationId: RENDER_TYPE.RAW,
4647
- tagsSorter: RENDER_TYPE.RAW,
4648
- onComplete: RENDER_TYPE.RAW,
4649
- syntaxHighlight: RENDER_TYPE.JSON_STRING,
4650
- tryItOutEnabled: RENDER_TYPE.RAW,
4651
- requestSnippetsEnabled: RENDER_TYPE.RAW,
4652
- requestSnippets: RENDER_TYPE.JSON_STRING,
4653
- oauth2RedirectUrl: RENDER_TYPE.STRING,
4654
- showMutabledRequest: RENDER_TYPE.RAW,
4655
- request: RENDER_TYPE.JSON_STRING,
4656
- supportedSubmitMethods: RENDER_TYPE.JSON_STRING,
4657
- validatorUrl: RENDER_TYPE.STRING,
4658
- withCredentials: RENDER_TYPE.RAW,
4659
- modelPropertyMacro: RENDER_TYPE.RAW,
4660
- parameterMacro: RENDER_TYPE.RAW
4661
- };
4662
- var renderSwaggerUIOptions = (options) => {
4663
- const optionsStrings = Object.entries(options).map(([k, v]) => {
4664
- const key = k;
4665
- if (!RENDER_TYPE_MAP[key] || v === void 0) {
4666
- return "";
4667
- }
4668
- switch (RENDER_TYPE_MAP[key]) {
4669
- case RENDER_TYPE.STRING:
4670
- return `${key}: '${v}'`;
4671
- case RENDER_TYPE.STRING_ARRAY:
4672
- if (!Array.isArray(v)) {
4673
- return "";
4674
- }
4675
- return `${key}: [${v.map((ve) => `${ve}`).join(",")}]`;
4676
- case RENDER_TYPE.JSON_STRING:
4677
- return `${key}: ${JSON.stringify(v)}`;
4678
- case RENDER_TYPE.RAW:
4679
- return `${key}: ${v}`;
4680
- default:
4681
- return "";
4682
- }
4683
- }).filter((item) => item !== "").join(",");
4684
- return optionsStrings;
4685
- };
4686
-
4687
- // src/swagger/resource.ts
4688
- var remoteAssets = ({ version }) => {
4689
- const url = `https://cdn.jsdelivr.net/npm/swagger-ui-dist${version !== void 0 ? `@${version}` : ""}`;
4690
- return {
4691
- css: [`${url}/swagger-ui.css`],
4692
- js: [`${url}/swagger-ui-bundle.js`]
4693
- };
4694
- };
4695
-
4696
- // src/index.ts
4697
- var SwaggerUI = (options) => {
4698
- const asset = remoteAssets({ version: options?.version });
4699
- delete options.version;
4700
- if (options.manuallySwaggerUIHtml) {
4701
- return options.manuallySwaggerUIHtml(asset);
4702
- }
4703
- const optionsStrings = renderSwaggerUIOptions(options);
4704
- return `
4705
- <div>
4706
- <div id="swagger-ui"></div>
4707
- ${asset.css.map((url) => html`<link rel="stylesheet" href="${url}" />`)}
4708
- ${asset.js.map((url) => html`<script src="${url}" crossorigin="anonymous"></script>`)}
4709
- <script>
4710
- window.onload = () => {
4711
- window.ui = SwaggerUIBundle({
4712
- dom_id: '#swagger-ui',${optionsStrings},
4713
- })
4714
- }
4715
- </script>
4716
- </div>
4717
- `;
4718
- };
4719
- var middleware = (options) => async (c) => {
4720
- const title = options?.title ?? "SwaggerUI";
4721
- return c.html(
4722
- /* html */
4723
- `
4724
- <html lang="en">
4725
- <head>
4726
- <meta charset="utf-8" />
4727
- <meta name="viewport" content="width=device-width, initial-scale=1" />
4728
- <meta name="description" content="SwaggerUI" />
4729
- <title>${title}</title>
4730
- </head>
4731
- <body>
4732
- ${SwaggerUI(options)}
4733
- </body>
4734
- </html>
4735
- `
4736
- );
4737
- };
4738
-
4739
2457
  function success(data, options = {}) {
4740
2458
  const { status = 200, meta = {} } = options;
4741
2459
  return {
@@ -6300,7 +4018,7 @@ class ApiServer {
6300
4018
  return c.json(this.openAPISpec);
6301
4019
  });
6302
4020
  if (this.options.docsUI === "swagger") {
6303
- this.app.get("/docs", middleware({
4021
+ this.app.get("/docs", swaggerUI({
6304
4022
  url: "/openapi.json"
6305
4023
  }));
6306
4024
  } else {
@@ -15619,6 +13337,9 @@ class RelationPlugin extends Plugin {
15619
13337
  this.batchSize = config.batchSize || 100;
15620
13338
  this.preventN1 = config.preventN1 !== void 0 ? config.preventN1 : true;
15621
13339
  this.verbose = config.verbose || false;
13340
+ this.fallbackLimit = config.fallbackLimit !== void 0 ? config.fallbackLimit : null;
13341
+ this.cascadeBatchSize = config.cascadeBatchSize || 10;
13342
+ this.cascadeTransactions = config.cascadeTransactions !== void 0 ? config.cascadeTransactions : false;
15622
13343
  this._loaderCache = /* @__PURE__ */ new Map();
15623
13344
  this._partitionCache = /* @__PURE__ */ new Map();
15624
13345
  this.stats = {
@@ -15627,7 +13348,8 @@ class RelationPlugin extends Plugin {
15627
13348
  batchLoads: 0,
15628
13349
  cascadeOperations: 0,
15629
13350
  partitionCacheHits: 0,
15630
- deduplicatedQueries: 0
13351
+ deduplicatedQueries: 0,
13352
+ fallbackLimitWarnings: 0
15631
13353
  };
15632
13354
  }
15633
13355
  /**
@@ -15892,13 +13614,7 @@ class RelationPlugin extends Plugin {
15892
13614
  localKeys
15893
13615
  );
15894
13616
  } else {
15895
- if (this.verbose) {
15896
- console.log(
15897
- `[RelationPlugin] No partition found for ${relatedResource.name}.${config.foreignKey}, using full scan`
15898
- );
15899
- }
15900
- const allRelated = await relatedResource.list({ limit: 1e4 });
15901
- relatedRecords = allRelated.filter((r) => localKeys.includes(r[config.foreignKey]));
13617
+ relatedRecords = await this._fallbackLoad(relatedResource, config.foreignKey, localKeys);
15902
13618
  }
15903
13619
  const relatedMap = /* @__PURE__ */ new Map();
15904
13620
  relatedRecords.forEach((related) => {
@@ -15937,13 +13653,7 @@ class RelationPlugin extends Plugin {
15937
13653
  localKeys
15938
13654
  );
15939
13655
  } else {
15940
- if (this.verbose) {
15941
- console.log(
15942
- `[RelationPlugin] No partition found for ${relatedResource.name}.${config.foreignKey}, using full scan`
15943
- );
15944
- }
15945
- const allRelated = await relatedResource.list({ limit: 1e4 });
15946
- relatedRecords = allRelated.filter((r) => localKeys.includes(r[config.foreignKey]));
13656
+ relatedRecords = await this._fallbackLoad(relatedResource, config.foreignKey, localKeys);
15947
13657
  }
15948
13658
  const relatedMap = /* @__PURE__ */ new Map();
15949
13659
  relatedRecords.forEach((related) => {
@@ -15989,13 +13699,7 @@ class RelationPlugin extends Plugin {
15989
13699
  foreignKeys
15990
13700
  );
15991
13701
  } else {
15992
- if (this.verbose) {
15993
- console.log(
15994
- `[RelationPlugin] No partition found for ${relatedResource.name}.${config.localKey}, using full scan`
15995
- );
15996
- }
15997
- const allRelated = await relatedResource.list({ limit: 1e4 });
15998
- return allRelated.filter((r) => foreignKeys.includes(r[config.localKey]));
13702
+ return await this._fallbackLoad(relatedResource, config.localKey, foreignKeys);
15999
13703
  }
16000
13704
  });
16001
13705
  if (!ok) {
@@ -16052,13 +13756,7 @@ class RelationPlugin extends Plugin {
16052
13756
  localKeys
16053
13757
  );
16054
13758
  } else {
16055
- if (this.verbose) {
16056
- console.log(
16057
- `[RelationPlugin] No partition found for ${junctionResource.name}.${config.foreignKey}, using full scan`
16058
- );
16059
- }
16060
- const allJunction = await junctionResource.list({ limit: 1e4 });
16061
- junctionRecords = allJunction.filter((j) => localKeys.includes(j[config.foreignKey]));
13759
+ junctionRecords = await this._fallbackLoad(junctionResource, config.foreignKey, localKeys);
16062
13760
  }
16063
13761
  if (junctionRecords.length === 0) {
16064
13762
  records.forEach((r) => r[relationName] = []);
@@ -16075,13 +13773,7 @@ class RelationPlugin extends Plugin {
16075
13773
  otherKeys
16076
13774
  );
16077
13775
  } else {
16078
- if (this.verbose) {
16079
- console.log(
16080
- `[RelationPlugin] No partition found for ${relatedResource.name}.${config.localKey}, using full scan`
16081
- );
16082
- }
16083
- const allRelated = await relatedResource.list({ limit: 1e4 });
16084
- relatedRecords = allRelated.filter((r) => otherKeys.includes(r[config.localKey]));
13776
+ relatedRecords = await this._fallbackLoad(relatedResource, config.localKey, otherKeys);
16085
13777
  }
16086
13778
  const relatedMap = /* @__PURE__ */ new Map();
16087
13779
  relatedRecords.forEach((related) => {
@@ -16105,6 +13797,47 @@ class RelationPlugin extends Plugin {
16105
13797
  }
16106
13798
  return records;
16107
13799
  }
13800
+ /**
13801
+ * Batch process operations with controlled parallelism
13802
+ * @private
13803
+ */
13804
+ async _batchProcess(items, operation, batchSize = null) {
13805
+ if (items.length === 0) return [];
13806
+ const actualBatchSize = batchSize || this.cascadeBatchSize;
13807
+ const results = [];
13808
+ for (let i = 0; i < items.length; i += actualBatchSize) {
13809
+ const chunk = items.slice(i, i + actualBatchSize);
13810
+ const chunkPromises = chunk.map((item) => operation(item));
13811
+ const chunkResults = await Promise.all(chunkPromises);
13812
+ results.push(...chunkResults);
13813
+ }
13814
+ return results;
13815
+ }
13816
+ /**
13817
+ * Load records using fallback (full scan) when no partition is available
13818
+ * Issues warnings when limit is reached to prevent silent data loss
13819
+ * @private
13820
+ */
13821
+ async _fallbackLoad(resource, fieldName, filterValues) {
13822
+ const options = this.fallbackLimit !== null ? { limit: this.fallbackLimit } : {};
13823
+ if (this.verbose) {
13824
+ console.log(
13825
+ `[RelationPlugin] No partition found for ${resource.name}.${fieldName}, using full scan` + (this.fallbackLimit ? ` (limited to ${this.fallbackLimit} records)` : " (no limit)")
13826
+ );
13827
+ }
13828
+ const allRecords = await resource.list(options);
13829
+ const filteredRecords = allRecords.filter((r) => filterValues.includes(r[fieldName]));
13830
+ if (this.fallbackLimit && allRecords.length >= this.fallbackLimit) {
13831
+ this.stats.fallbackLimitWarnings++;
13832
+ console.warn(
13833
+ `[RelationPlugin] WARNING: Fallback query for ${resource.name}.${fieldName} hit the limit of ${this.fallbackLimit} records. Some related records may be missing! Consider:
13834
+ 1. Adding a partition on field "${fieldName}" for better performance
13835
+ 2. Increasing fallbackLimit in plugin config (or set to null for no limit)
13836
+ Partition example: partitions: { by${fieldName.charAt(0).toUpperCase() + fieldName.slice(1)}: { fields: { ${fieldName}: 'string' } } }`
13837
+ );
13838
+ }
13839
+ return filteredRecords;
13840
+ }
16108
13841
  /**
16109
13842
  * Find partition by field name (for efficient relation loading)
16110
13843
  * Uses cache to avoid repeated lookups
@@ -16180,6 +13913,7 @@ class RelationPlugin extends Plugin {
16180
13913
  /**
16181
13914
  * Cascade delete operation
16182
13915
  * Uses partitions when available for efficient cascade
13916
+ * Supports transaction/rollback when enabled
16183
13917
  * @private
16184
13918
  */
16185
13919
  async _cascadeDelete(record, resource, relationName, config) {
@@ -16191,6 +13925,8 @@ class RelationPlugin extends Plugin {
16191
13925
  relation: relationName
16192
13926
  });
16193
13927
  }
13928
+ const deletedRecords = [];
13929
+ config.type === "belongsToMany" ? this.database.resource(config.through) : null;
16194
13930
  try {
16195
13931
  if (config.type === "hasMany") {
16196
13932
  let relatedRecords;
@@ -16210,12 +13946,15 @@ class RelationPlugin extends Plugin {
16210
13946
  [config.foreignKey]: record[config.localKey]
16211
13947
  });
16212
13948
  }
16213
- for (const related of relatedRecords) {
16214
- await relatedResource.delete(related.id);
13949
+ if (this.cascadeTransactions) {
13950
+ deletedRecords.push(...relatedRecords.map((r) => ({ type: "delete", resource: relatedResource, record: r })));
16215
13951
  }
13952
+ await this._batchProcess(relatedRecords, async (related) => {
13953
+ return await relatedResource.delete(related.id);
13954
+ });
16216
13955
  if (this.verbose) {
16217
13956
  console.log(
16218
- `[RelationPlugin] Cascade deleted ${relatedRecords.length} ${config.resource} for ${resource.name}:${record.id}`
13957
+ `[RelationPlugin] Cascade deleted ${relatedRecords.length} ${config.resource} for ${resource.name}:${record.id} (batched in ${Math.ceil(relatedRecords.length / this.cascadeBatchSize)} chunks)`
16219
13958
  );
16220
13959
  }
16221
13960
  } else if (config.type === "hasOne") {
@@ -16232,15 +13971,18 @@ class RelationPlugin extends Plugin {
16232
13971
  });
16233
13972
  }
16234
13973
  if (relatedRecords.length > 0) {
13974
+ if (this.cascadeTransactions) {
13975
+ deletedRecords.push({ type: "delete", resource: relatedResource, record: relatedRecords[0] });
13976
+ }
16235
13977
  await relatedResource.delete(relatedRecords[0].id);
16236
13978
  }
16237
13979
  } else if (config.type === "belongsToMany") {
16238
- const junctionResource = this.database.resource(config.through);
16239
- if (junctionResource) {
13980
+ const junctionResource2 = this.database.resource(config.through);
13981
+ if (junctionResource2) {
16240
13982
  let junctionRecords;
16241
- const partitionName = this._findPartitionByField(junctionResource, config.foreignKey);
13983
+ const partitionName = this._findPartitionByField(junctionResource2, config.foreignKey);
16242
13984
  if (partitionName) {
16243
- junctionRecords = await junctionResource.list({
13985
+ junctionRecords = await junctionResource2.list({
16244
13986
  partition: partitionName,
16245
13987
  partitionValues: { [config.foreignKey]: record[config.localKey] }
16246
13988
  });
@@ -16250,21 +13992,45 @@ class RelationPlugin extends Plugin {
16250
13992
  );
16251
13993
  }
16252
13994
  } else {
16253
- junctionRecords = await junctionResource.query({
13995
+ junctionRecords = await junctionResource2.query({
16254
13996
  [config.foreignKey]: record[config.localKey]
16255
13997
  });
16256
13998
  }
16257
- for (const junction of junctionRecords) {
16258
- await junctionResource.delete(junction.id);
13999
+ if (this.cascadeTransactions) {
14000
+ deletedRecords.push(...junctionRecords.map((j) => ({ type: "delete", resource: junctionResource2, record: j })));
16259
14001
  }
14002
+ await this._batchProcess(junctionRecords, async (junction) => {
14003
+ return await junctionResource2.delete(junction.id);
14004
+ });
16260
14005
  if (this.verbose) {
16261
14006
  console.log(
16262
- `[RelationPlugin] Cascade deleted ${junctionRecords.length} junction records from ${config.through}`
14007
+ `[RelationPlugin] Cascade deleted ${junctionRecords.length} junction records from ${config.through} (batched in ${Math.ceil(junctionRecords.length / this.cascadeBatchSize)} chunks)`
16263
14008
  );
16264
14009
  }
16265
14010
  }
16266
14011
  }
16267
14012
  } catch (error) {
14013
+ if (this.cascadeTransactions && deletedRecords.length > 0) {
14014
+ console.error(
14015
+ `[RelationPlugin] Cascade delete failed, attempting rollback of ${deletedRecords.length} records...`
14016
+ );
14017
+ const rollbackErrors = [];
14018
+ for (const { resource: res, record: rec } of deletedRecords.reverse()) {
14019
+ try {
14020
+ await res.insert(rec);
14021
+ } catch (rollbackError) {
14022
+ rollbackErrors.push({ record: rec.id, error: rollbackError.message });
14023
+ }
14024
+ }
14025
+ if (rollbackErrors.length > 0) {
14026
+ console.error(
14027
+ `[RelationPlugin] Rollback partially failed for ${rollbackErrors.length} records:`,
14028
+ rollbackErrors
14029
+ );
14030
+ } else if (this.verbose) {
14031
+ console.log(`[RelationPlugin] Rollback successful, restored ${deletedRecords.length} records`);
14032
+ }
14033
+ }
16268
14034
  throw new CascadeError("delete", resource.name, record.id, error, {
16269
14035
  relation: relationName,
16270
14036
  relatedResource: config.resource
@@ -16274,6 +14040,7 @@ class RelationPlugin extends Plugin {
16274
14040
  /**
16275
14041
  * Cascade update operation (update foreign keys when local key changes)
16276
14042
  * Uses partitions when available for efficient cascade
14043
+ * Supports transaction/rollback when enabled
16277
14044
  * @private
16278
14045
  */
16279
14046
  async _cascadeUpdate(record, changes, resource, relationName, config) {
@@ -16282,6 +14049,7 @@ class RelationPlugin extends Plugin {
16282
14049
  if (!relatedResource) {
16283
14050
  return;
16284
14051
  }
14052
+ const updatedRecords = [];
16285
14053
  try {
16286
14054
  const oldLocalKeyValue = record[config.localKey];
16287
14055
  const newLocalKeyValue = changes[config.localKey];
@@ -16305,17 +14073,48 @@ class RelationPlugin extends Plugin {
16305
14073
  [config.foreignKey]: oldLocalKeyValue
16306
14074
  });
16307
14075
  }
16308
- for (const related of relatedRecords) {
16309
- await relatedResource.update(related.id, {
14076
+ if (this.cascadeTransactions) {
14077
+ updatedRecords.push(...relatedRecords.map((r) => ({
14078
+ type: "update",
14079
+ resource: relatedResource,
14080
+ id: r.id,
14081
+ oldValue: r[config.foreignKey],
14082
+ newValue: newLocalKeyValue,
14083
+ field: config.foreignKey
14084
+ })));
14085
+ }
14086
+ await this._batchProcess(relatedRecords, async (related) => {
14087
+ return await relatedResource.update(related.id, {
16310
14088
  [config.foreignKey]: newLocalKeyValue
16311
14089
  }, { skipCascade: true });
16312
- }
14090
+ });
16313
14091
  if (this.verbose) {
16314
14092
  console.log(
16315
- `[RelationPlugin] Cascade updated ${relatedRecords.length} ${config.resource} records`
14093
+ `[RelationPlugin] Cascade updated ${relatedRecords.length} ${config.resource} records (batched in ${Math.ceil(relatedRecords.length / this.cascadeBatchSize)} chunks)`
16316
14094
  );
16317
14095
  }
16318
14096
  } catch (error) {
14097
+ if (this.cascadeTransactions && updatedRecords.length > 0) {
14098
+ console.error(
14099
+ `[RelationPlugin] Cascade update failed, attempting rollback of ${updatedRecords.length} records...`
14100
+ );
14101
+ const rollbackErrors = [];
14102
+ for (const { resource: res, id, field, oldValue } of updatedRecords.reverse()) {
14103
+ try {
14104
+ await res.update(id, { [field]: oldValue }, { skipCascade: true });
14105
+ } catch (rollbackError) {
14106
+ rollbackErrors.push({ id, error: rollbackError.message });
14107
+ }
14108
+ }
14109
+ if (rollbackErrors.length > 0) {
14110
+ console.error(
14111
+ `[RelationPlugin] Rollback partially failed for ${rollbackErrors.length} records:`,
14112
+ rollbackErrors
14113
+ );
14114
+ } else if (this.verbose) {
14115
+ console.log(`[RelationPlugin] Rollback successful, restored ${updatedRecords.length} records`);
14116
+ }
14117
+ }
16319
14118
  throw new CascadeError("update", resource.name, record.id, error, {
16320
14119
  relation: relationName,
16321
14120
  relatedResource: config.resource
@@ -19140,7 +16939,17 @@ class Client extends EventEmitter {
19140
16939
  Bucket: this.config.bucket,
19141
16940
  Key: keyPrefix ? path$1.join(keyPrefix, key) : key
19142
16941
  };
19143
- const [ok, err, response] = await tryFn(() => this.sendCommand(new HeadObjectCommand(options)));
16942
+ const [ok, err, response] = await tryFn(async () => {
16943
+ const res = await this.sendCommand(new HeadObjectCommand(options));
16944
+ if (res.Metadata) {
16945
+ const decodedMetadata = {};
16946
+ for (const [key2, value] of Object.entries(res.Metadata)) {
16947
+ decodedMetadata[key2] = metadataDecode(value);
16948
+ }
16949
+ res.Metadata = decodedMetadata;
16950
+ }
16951
+ return res;
16952
+ });
19144
16953
  this.emit("headObject", err || response, { key });
19145
16954
  if (!ok) {
19146
16955
  throw mapAwsError(err, {
@@ -19152,14 +16961,29 @@ class Client extends EventEmitter {
19152
16961
  }
19153
16962
  return response;
19154
16963
  }
19155
- async copyObject({ from, to }) {
16964
+ async copyObject({ from, to, metadata, metadataDirective, contentType }) {
16965
+ const keyPrefix = typeof this.config.keyPrefix === "string" ? this.config.keyPrefix : "";
19156
16966
  const options = {
19157
16967
  Bucket: this.config.bucket,
19158
- Key: this.config.keyPrefix ? path$1.join(this.config.keyPrefix, to) : to,
19159
- CopySource: path$1.join(this.config.bucket, this.config.keyPrefix ? path$1.join(this.config.keyPrefix, from) : from)
16968
+ Key: keyPrefix ? path$1.join(keyPrefix, to) : to,
16969
+ CopySource: path$1.join(this.config.bucket, keyPrefix ? path$1.join(keyPrefix, from) : from)
19160
16970
  };
16971
+ if (metadataDirective) {
16972
+ options.MetadataDirective = metadataDirective;
16973
+ }
16974
+ if (metadata && typeof metadata === "object") {
16975
+ const encodedMetadata = {};
16976
+ for (const [key, value] of Object.entries(metadata)) {
16977
+ const { encoded } = metadataEncode(value);
16978
+ encodedMetadata[key] = encoded;
16979
+ }
16980
+ options.Metadata = encodedMetadata;
16981
+ }
16982
+ if (contentType) {
16983
+ options.ContentType = contentType;
16984
+ }
19161
16985
  const [ok, err, response] = await tryFn(() => this.sendCommand(new CopyObjectCommand(options)));
19162
- this.emit("copyObject", err || response, { from, to });
16986
+ this.emit("copyObject", err || response, { from, to, metadataDirective });
19163
16987
  if (!ok) {
19164
16988
  throw mapAwsError(err, {
19165
16989
  bucket: this.config.bucket,
@@ -20740,7 +18564,7 @@ class ResourceIdsReader extends EventEmitter {
20740
18564
  super();
20741
18565
  this.resource = resource;
20742
18566
  this.client = resource.client;
20743
- this.stream = new ReadableStream$1({
18567
+ this.stream = new ReadableStream({
20744
18568
  highWaterMark: this.client.parallelism * 3,
20745
18569
  start: this._start.bind(this),
20746
18570
  pull: this._pull.bind(this),
@@ -22362,6 +20186,235 @@ ${errorDetails}`,
22362
20186
  return finalResult;
22363
20187
  }
22364
20188
  }
20189
+ /**
20190
+ * Patch resource (partial update optimized for metadata-only behaviors)
20191
+ *
20192
+ * This method provides an optimized update path for resources using metadata-only behaviors
20193
+ * (enforce-limits, truncate-data). It uses HeadObject + CopyObject for atomic updates without
20194
+ * body transfer, eliminating race conditions and reducing latency by ~50%.
20195
+ *
20196
+ * For behaviors that store data in body (body-overflow, body-only), it automatically falls
20197
+ * back to the standard update() method.
20198
+ *
20199
+ * @param {string} id - Resource ID
20200
+ * @param {Object} fields - Fields to update (partial data)
20201
+ * @param {Object} options - Update options
20202
+ * @param {string} options.partition - Partition name (if using partitions)
20203
+ * @param {Object} options.partitionValues - Partition values (if using partitions)
20204
+ * @returns {Promise<Object>} Updated resource data
20205
+ *
20206
+ * @example
20207
+ * // Fast atomic update (enforce-limits behavior)
20208
+ * await resource.patch('user-123', { status: 'active', loginCount: 42 });
20209
+ *
20210
+ * @example
20211
+ * // With partitions
20212
+ * await resource.patch('order-456', { status: 'shipped' }, {
20213
+ * partition: 'byRegion',
20214
+ * partitionValues: { region: 'US' }
20215
+ * });
20216
+ */
20217
+ async patch(id, fields, options = {}) {
20218
+ if (isEmpty(id)) {
20219
+ throw new Error("id cannot be empty");
20220
+ }
20221
+ if (!fields || typeof fields !== "object") {
20222
+ throw new Error("fields must be a non-empty object");
20223
+ }
20224
+ const behavior = this.behavior;
20225
+ const hasNestedFields = Object.keys(fields).some((key) => key.includes("."));
20226
+ if ((behavior === "enforce-limits" || behavior === "truncate-data") && !hasNestedFields) {
20227
+ return await this._patchViaCopyObject(id, fields, options);
20228
+ }
20229
+ return await this.update(id, fields, options);
20230
+ }
20231
+ /**
20232
+ * Internal helper: Optimized patch using HeadObject + CopyObject
20233
+ * Only works for metadata-only behaviors (enforce-limits, truncate-data)
20234
+ * Only for simple field updates (no nested fields with dot notation)
20235
+ * @private
20236
+ */
20237
+ async _patchViaCopyObject(id, fields, options = {}) {
20238
+ const { partition, partitionValues } = options;
20239
+ const key = this.getResourceKey(id);
20240
+ const headResponse = await this.client.headObject(key);
20241
+ const currentMetadata = headResponse.Metadata || {};
20242
+ let currentData = await this.schema.unmapper(currentMetadata);
20243
+ if (!currentData.id) {
20244
+ currentData.id = id;
20245
+ }
20246
+ const fieldsClone = cloneDeep(fields);
20247
+ let mergedData = cloneDeep(currentData);
20248
+ for (const [key2, value] of Object.entries(fieldsClone)) {
20249
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
20250
+ mergedData[key2] = merge({}, mergedData[key2], value);
20251
+ } else {
20252
+ mergedData[key2] = cloneDeep(value);
20253
+ }
20254
+ }
20255
+ if (this.config.timestamps) {
20256
+ mergedData.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
20257
+ }
20258
+ const validationResult = await this.schema.validate(mergedData);
20259
+ if (validationResult !== true) {
20260
+ throw new ValidationError("Validation failed during patch", validationResult);
20261
+ }
20262
+ const newMetadata = await this.schema.mapper(mergedData);
20263
+ newMetadata._v = String(this.version);
20264
+ await this.client.copyObject({
20265
+ from: key,
20266
+ to: key,
20267
+ metadataDirective: "REPLACE",
20268
+ metadata: newMetadata
20269
+ });
20270
+ if (this.config.partitions && Object.keys(this.config.partitions).length > 0) {
20271
+ const oldData = { ...currentData, id };
20272
+ const newData = { ...mergedData, id };
20273
+ if (this.config.asyncPartitions) {
20274
+ setImmediate(() => {
20275
+ this.handlePartitionReferenceUpdates(oldData, newData).catch((err) => {
20276
+ this.emit("partitionIndexError", {
20277
+ operation: "patch",
20278
+ id,
20279
+ error: err
20280
+ });
20281
+ });
20282
+ });
20283
+ } else {
20284
+ await this.handlePartitionReferenceUpdates(oldData, newData);
20285
+ }
20286
+ }
20287
+ return mergedData;
20288
+ }
20289
+ /**
20290
+ * Replace resource (full object replacement without GET)
20291
+ *
20292
+ * This method performs a direct PUT operation without fetching the current object.
20293
+ * Use this when you already have the complete object and want to replace it entirely,
20294
+ * saving 1 S3 request (GET).
20295
+ *
20296
+ * ⚠️ Warning: You must provide ALL required fields. Missing fields will NOT be preserved
20297
+ * from the current object. This method does not merge with existing data.
20298
+ *
20299
+ * @param {string} id - Resource ID
20300
+ * @param {Object} fullData - Complete object data (all required fields)
20301
+ * @param {Object} options - Update options
20302
+ * @param {string} options.partition - Partition name (if using partitions)
20303
+ * @param {Object} options.partitionValues - Partition values (if using partitions)
20304
+ * @returns {Promise<Object>} Replaced resource data
20305
+ *
20306
+ * @example
20307
+ * // Replace entire object (must include ALL required fields)
20308
+ * await resource.replace('user-123', {
20309
+ * name: 'John Doe',
20310
+ * email: 'john@example.com',
20311
+ * status: 'active',
20312
+ * loginCount: 42
20313
+ * });
20314
+ *
20315
+ * @example
20316
+ * // With partitions
20317
+ * await resource.replace('order-456', fullOrderData, {
20318
+ * partition: 'byRegion',
20319
+ * partitionValues: { region: 'US' }
20320
+ * });
20321
+ */
20322
+ async replace(id, fullData, options = {}) {
20323
+ if (isEmpty(id)) {
20324
+ throw new Error("id cannot be empty");
20325
+ }
20326
+ if (!fullData || typeof fullData !== "object") {
20327
+ throw new Error("fullData must be a non-empty object");
20328
+ }
20329
+ const { partition, partitionValues } = options;
20330
+ const dataClone = cloneDeep(fullData);
20331
+ const attributesWithDefaults = this.applyDefaults(dataClone);
20332
+ if (this.config.timestamps) {
20333
+ if (!attributesWithDefaults.createdAt) {
20334
+ attributesWithDefaults.createdAt = (/* @__PURE__ */ new Date()).toISOString();
20335
+ }
20336
+ attributesWithDefaults.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
20337
+ }
20338
+ const completeData = { id, ...attributesWithDefaults };
20339
+ const {
20340
+ errors,
20341
+ isValid,
20342
+ data: validated
20343
+ } = await this.validate(completeData);
20344
+ if (!isValid) {
20345
+ const errorMsg = errors && errors.length && errors[0].message ? errors[0].message : "Replace failed";
20346
+ throw new InvalidResourceItem({
20347
+ bucket: this.client.config.bucket,
20348
+ resourceName: this.name,
20349
+ attributes: completeData,
20350
+ validation: errors,
20351
+ message: errorMsg
20352
+ });
20353
+ }
20354
+ const { id: validatedId, ...validatedAttributes } = validated;
20355
+ const mappedMetadata = await this.schema.mapper(validatedAttributes);
20356
+ mappedMetadata._v = String(this.version);
20357
+ const behaviorImpl = getBehavior(this.behavior);
20358
+ const { mappedData: finalMetadata, body } = await behaviorImpl.handleInsert({
20359
+ resource: this,
20360
+ data: validatedAttributes,
20361
+ mappedData: mappedMetadata,
20362
+ originalData: completeData
20363
+ });
20364
+ const key = this.getResourceKey(id);
20365
+ let contentType = void 0;
20366
+ if (body && body !== "") {
20367
+ const [okParse] = await tryFn(() => Promise.resolve(JSON.parse(body)));
20368
+ if (okParse) contentType = "application/json";
20369
+ }
20370
+ if (this.behavior === "body-only" && (!body || body === "")) {
20371
+ throw new Error(`[Resource.replace] Attempt to save object without body! Data: id=${id}, resource=${this.name}`);
20372
+ }
20373
+ const [okPut, errPut] = await tryFn(() => this.client.putObject({
20374
+ key,
20375
+ body,
20376
+ contentType,
20377
+ metadata: finalMetadata
20378
+ }));
20379
+ if (!okPut) {
20380
+ const msg = errPut && errPut.message ? errPut.message : "";
20381
+ if (msg.includes("metadata headers exceed") || msg.includes("Replace failed")) {
20382
+ const totalSize = calculateTotalSize(finalMetadata);
20383
+ const effectiveLimit = calculateEffectiveLimit({
20384
+ s3Limit: 2047,
20385
+ systemConfig: {
20386
+ version: this.version,
20387
+ timestamps: this.config.timestamps,
20388
+ id
20389
+ }
20390
+ });
20391
+ const excess = totalSize - effectiveLimit;
20392
+ errPut.totalSize = totalSize;
20393
+ errPut.limit = 2047;
20394
+ errPut.effectiveLimit = effectiveLimit;
20395
+ errPut.excess = excess;
20396
+ throw new ResourceError("metadata headers exceed", { resourceName: this.name, operation: "replace", id, totalSize, effectiveLimit, excess, suggestion: "Reduce metadata size or number of fields." });
20397
+ }
20398
+ throw errPut;
20399
+ }
20400
+ const replacedObject = { id, ...validatedAttributes };
20401
+ if (this.config.partitions && Object.keys(this.config.partitions).length > 0) {
20402
+ if (this.config.asyncPartitions) {
20403
+ setImmediate(() => {
20404
+ this.handlePartitionReferenceUpdates({}, replacedObject).catch((err) => {
20405
+ this.emit("partitionIndexError", {
20406
+ operation: "replace",
20407
+ id,
20408
+ error: err
20409
+ });
20410
+ });
20411
+ });
20412
+ } else {
20413
+ await this.handlePartitionReferenceUpdates({}, replacedObject);
20414
+ }
20415
+ }
20416
+ return replacedObject;
20417
+ }
22365
20418
  /**
22366
20419
  * Update with conditional check (If-Match ETag)
22367
20420
  * @param {string} id - Resource ID
@@ -23710,29 +21763,6 @@ ${errorDetails}`,
23710
21763
  }
23711
21764
  return filtered;
23712
21765
  }
23713
- async replace(id, attributes) {
23714
- await this.delete(id);
23715
- await new Promise((r) => setTimeout(r, 100));
23716
- const maxWait = 5e3;
23717
- const interval = 50;
23718
- const start = Date.now();
23719
- while (Date.now() - start < maxWait) {
23720
- const exists = await this.exists(id);
23721
- if (!exists) {
23722
- break;
23723
- }
23724
- await new Promise((r) => setTimeout(r, interval));
23725
- }
23726
- const [ok, err, result] = await tryFn(() => this.insert({ ...attributes, id }));
23727
- if (!ok) {
23728
- if (err && err.message && err.message.includes("already exists")) {
23729
- const updateResult = await this.update(id, attributes);
23730
- return updateResult;
23731
- }
23732
- throw err;
23733
- }
23734
- return result;
23735
- }
23736
21766
  // --- MIDDLEWARE SYSTEM ---
23737
21767
  _initMiddleware() {
23738
21768
  this._middlewares = /* @__PURE__ */ new Map();
@@ -23934,7 +21964,7 @@ class Database extends EventEmitter {
23934
21964
  this.id = idGenerator(7);
23935
21965
  this.version = "1";
23936
21966
  this.s3dbVersion = (() => {
23937
- const [ok, err, version] = tryFn(() => true ? "12.0.0" : "latest");
21967
+ const [ok, err, version] = tryFn(() => true ? "12.1.0" : "latest");
23938
21968
  return ok ? version : "latest";
23939
21969
  })();
23940
21970
  this.resources = {};
@@ -26804,7 +24834,7 @@ class ReplicatorPlugin extends Plugin {
26804
24834
  async updateReplicatorLog(logId, updates) {
26805
24835
  if (!this.replicatorLog) return;
26806
24836
  const [ok, err] = await tryFn(async () => {
26807
- await this.replicatorLog.update(logId, {
24837
+ await this.replicatorLog.patch(logId, {
26808
24838
  ...updates,
26809
24839
  lastAttempt: (/* @__PURE__ */ new Date()).toISOString()
26810
24840
  });
@@ -38075,7 +36105,7 @@ class TfStatePlugin extends Plugin {
38075
36105
  console.log(`[TfStatePlugin] Setting up cron monitoring: ${this.monitorCron}`);
38076
36106
  }
38077
36107
  await requirePluginDependency("tfstate-plugin");
38078
- const [ok, err, cronModule] = await tryFn(() => Promise.resolve().then(function () { return nodeCron$1; }));
36108
+ const [ok, err, cronModule] = await tryFn(() => import('node-cron'));
38079
36109
  if (!ok) {
38080
36110
  throw new TfStateError(`Failed to import node-cron: ${err.message}`);
38081
36111
  }
@@ -39772,1415 +37802,6 @@ var prometheusFormatter = /*#__PURE__*/Object.freeze({
39772
37802
  formatPrometheusMetrics: formatPrometheusMetrics
39773
37803
  });
39774
37804
 
39775
- function getDefaultExportFromCjs (x) {
39776
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
39777
- }
39778
-
39779
- var nodeCron$2 = {};
39780
-
39781
- var inlineScheduledTask = {};
39782
-
39783
- var runner = {};
39784
-
39785
- var createId = {};
39786
-
39787
- var hasRequiredCreateId;
39788
-
39789
- function requireCreateId () {
39790
- if (hasRequiredCreateId) return createId;
39791
- hasRequiredCreateId = 1;
39792
- var __importDefault = (createId && createId.__importDefault) || function (mod) {
39793
- return (mod && mod.__esModule) ? mod : { "default": mod };
39794
- };
39795
- Object.defineProperty(createId, "__esModule", { value: true });
39796
- createId.createID = createID;
39797
- const node_crypto_1 = __importDefault(require$$0);
39798
- function createID(prefix = '', length = 16) {
39799
- const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
39800
- const values = node_crypto_1.default.randomBytes(length);
39801
- const id = Array.from(values, v => charset[v % charset.length]).join('');
39802
- return prefix ? `${prefix}-${id}` : id;
39803
- }
39804
-
39805
- return createId;
39806
- }
39807
-
39808
- var logger = {};
39809
-
39810
- var hasRequiredLogger;
39811
-
39812
- function requireLogger () {
39813
- if (hasRequiredLogger) return logger;
39814
- hasRequiredLogger = 1;
39815
- Object.defineProperty(logger, "__esModule", { value: true });
39816
- const levelColors = {
39817
- INFO: '\x1b[36m',
39818
- WARN: '\x1b[33m',
39819
- ERROR: '\x1b[31m',
39820
- DEBUG: '\x1b[35m',
39821
- };
39822
- const GREEN = '\x1b[32m';
39823
- const RESET = '\x1b[0m';
39824
- function log(level, message, extra) {
39825
- const timestamp = new Date().toISOString();
39826
- const color = levelColors[level] ?? '';
39827
- const prefix = `[${timestamp}] [PID: ${process.pid}] ${GREEN}[NODE-CRON]${GREEN} ${color}[${level}]${RESET}`;
39828
- const output = `${prefix} ${message}`;
39829
- switch (level) {
39830
- case 'ERROR':
39831
- console.error(output, extra ?? '');
39832
- break;
39833
- case 'DEBUG':
39834
- console.debug(output, extra ?? '');
39835
- break;
39836
- case 'WARN':
39837
- console.warn(output);
39838
- break;
39839
- case 'INFO':
39840
- default:
39841
- console.info(output);
39842
- break;
39843
- }
39844
- }
39845
- const logger$1 = {
39846
- info(message) {
39847
- log('INFO', message);
39848
- },
39849
- warn(message) {
39850
- log('WARN', message);
39851
- },
39852
- error(message, err) {
39853
- if (message instanceof Error) {
39854
- log('ERROR', message.message, message);
39855
- }
39856
- else {
39857
- log('ERROR', message, err);
39858
- }
39859
- },
39860
- debug(message, err) {
39861
- if (message instanceof Error) {
39862
- log('DEBUG', message.message, message);
39863
- }
39864
- else {
39865
- log('DEBUG', message, err);
39866
- }
39867
- },
39868
- };
39869
- logger.default = logger$1;
39870
-
39871
- return logger;
39872
- }
39873
-
39874
- var trackedPromise = {};
39875
-
39876
- var hasRequiredTrackedPromise;
39877
-
39878
- function requireTrackedPromise () {
39879
- if (hasRequiredTrackedPromise) return trackedPromise;
39880
- hasRequiredTrackedPromise = 1;
39881
- Object.defineProperty(trackedPromise, "__esModule", { value: true });
39882
- trackedPromise.TrackedPromise = void 0;
39883
- class TrackedPromise {
39884
- promise;
39885
- error;
39886
- state;
39887
- value;
39888
- constructor(executor) {
39889
- this.state = 'pending';
39890
- this.promise = new Promise((resolve, reject) => {
39891
- executor((value) => {
39892
- this.state = 'fulfilled';
39893
- this.value = value;
39894
- resolve(value);
39895
- }, (error) => {
39896
- this.state = 'rejected';
39897
- this.error = error;
39898
- reject(error);
39899
- });
39900
- });
39901
- }
39902
- getPromise() {
39903
- return this.promise;
39904
- }
39905
- getState() {
39906
- return this.state;
39907
- }
39908
- isPending() {
39909
- return this.state === 'pending';
39910
- }
39911
- isFulfilled() {
39912
- return this.state === 'fulfilled';
39913
- }
39914
- isRejected() {
39915
- return this.state === 'rejected';
39916
- }
39917
- getValue() {
39918
- return this.value;
39919
- }
39920
- getError() {
39921
- return this.error;
39922
- }
39923
- then(onfulfilled, onrejected) {
39924
- return this.promise.then(onfulfilled, onrejected);
39925
- }
39926
- catch(onrejected) {
39927
- return this.promise.catch(onrejected);
39928
- }
39929
- finally(onfinally) {
39930
- return this.promise.finally(onfinally);
39931
- }
39932
- }
39933
- trackedPromise.TrackedPromise = TrackedPromise;
39934
-
39935
- return trackedPromise;
39936
- }
39937
-
39938
- var hasRequiredRunner;
39939
-
39940
- function requireRunner () {
39941
- if (hasRequiredRunner) return runner;
39942
- hasRequiredRunner = 1;
39943
- var __importDefault = (runner && runner.__importDefault) || function (mod) {
39944
- return (mod && mod.__esModule) ? mod : { "default": mod };
39945
- };
39946
- Object.defineProperty(runner, "__esModule", { value: true });
39947
- runner.Runner = void 0;
39948
- const create_id_1 = requireCreateId();
39949
- const logger_1 = __importDefault(requireLogger());
39950
- const tracked_promise_1 = requireTrackedPromise();
39951
- function emptyOnFn() { }
39952
- function emptyHookFn() { return true; }
39953
- function defaultOnError(date, error) {
39954
- logger_1.default.error('Task failed with error!', error);
39955
- }
39956
- class Runner {
39957
- timeMatcher;
39958
- onMatch;
39959
- noOverlap;
39960
- maxExecutions;
39961
- maxRandomDelay;
39962
- runCount;
39963
- running;
39964
- heartBeatTimeout;
39965
- onMissedExecution;
39966
- onOverlap;
39967
- onError;
39968
- beforeRun;
39969
- onFinished;
39970
- onMaxExecutions;
39971
- constructor(timeMatcher, onMatch, options) {
39972
- this.timeMatcher = timeMatcher;
39973
- this.onMatch = onMatch;
39974
- this.noOverlap = options == undefined || options.noOverlap === undefined ? false : options.noOverlap;
39975
- this.maxExecutions = options?.maxExecutions;
39976
- this.maxRandomDelay = options?.maxRandomDelay || 0;
39977
- this.onMissedExecution = options?.onMissedExecution || emptyOnFn;
39978
- this.onOverlap = options?.onOverlap || emptyOnFn;
39979
- this.onError = options?.onError || defaultOnError;
39980
- this.onFinished = options?.onFinished || emptyHookFn;
39981
- this.beforeRun = options?.beforeRun || emptyHookFn;
39982
- this.onMaxExecutions = options?.onMaxExecutions || emptyOnFn;
39983
- this.runCount = 0;
39984
- this.running = false;
39985
- }
39986
- start() {
39987
- this.running = true;
39988
- let lastExecution;
39989
- let expectedNextExecution;
39990
- const scheduleNextHeartBeat = (currentDate) => {
39991
- if (this.running) {
39992
- clearTimeout(this.heartBeatTimeout);
39993
- this.heartBeatTimeout = setTimeout(heartBeat, getDelay(this.timeMatcher, currentDate));
39994
- }
39995
- };
39996
- const runTask = (date) => {
39997
- return new Promise(async (resolve) => {
39998
- const execution = {
39999
- id: (0, create_id_1.createID)('exec'),
40000
- reason: 'scheduled'
40001
- };
40002
- const shouldExecute = await this.beforeRun(date, execution);
40003
- const randomDelay = Math.floor(Math.random() * this.maxRandomDelay);
40004
- if (shouldExecute) {
40005
- setTimeout(async () => {
40006
- try {
40007
- this.runCount++;
40008
- execution.startedAt = new Date();
40009
- const result = await this.onMatch(date, execution);
40010
- execution.finishedAt = new Date();
40011
- execution.result = result;
40012
- this.onFinished(date, execution);
40013
- if (this.maxExecutions && this.runCount >= this.maxExecutions) {
40014
- this.onMaxExecutions(date);
40015
- this.stop();
40016
- }
40017
- }
40018
- catch (error) {
40019
- execution.finishedAt = new Date();
40020
- execution.error = error;
40021
- this.onError(date, error, execution);
40022
- }
40023
- resolve(true);
40024
- }, randomDelay);
40025
- }
40026
- });
40027
- };
40028
- const checkAndRun = (date) => {
40029
- return new tracked_promise_1.TrackedPromise(async (resolve, reject) => {
40030
- try {
40031
- if (this.timeMatcher.match(date)) {
40032
- await runTask(date);
40033
- }
40034
- resolve(true);
40035
- }
40036
- catch (err) {
40037
- reject(err);
40038
- }
40039
- });
40040
- };
40041
- const heartBeat = async () => {
40042
- const currentDate = nowWithoutMs();
40043
- if (expectedNextExecution && expectedNextExecution.getTime() < currentDate.getTime()) {
40044
- while (expectedNextExecution.getTime() < currentDate.getTime()) {
40045
- logger_1.default.warn(`missed execution at ${expectedNextExecution}! Possible blocking IO or high CPU user at the same process used by node-cron.`);
40046
- expectedNextExecution = this.timeMatcher.getNextMatch(expectedNextExecution);
40047
- runAsync(this.onMissedExecution, expectedNextExecution, defaultOnError);
40048
- }
40049
- }
40050
- if (lastExecution && lastExecution.getState() === 'pending') {
40051
- runAsync(this.onOverlap, currentDate, defaultOnError);
40052
- if (this.noOverlap) {
40053
- logger_1.default.warn('task still running, new execution blocked by overlap prevention!');
40054
- expectedNextExecution = this.timeMatcher.getNextMatch(currentDate);
40055
- scheduleNextHeartBeat(currentDate);
40056
- return;
40057
- }
40058
- }
40059
- lastExecution = checkAndRun(currentDate);
40060
- expectedNextExecution = this.timeMatcher.getNextMatch(currentDate);
40061
- scheduleNextHeartBeat(currentDate);
40062
- };
40063
- this.heartBeatTimeout = setTimeout(() => {
40064
- heartBeat();
40065
- }, getDelay(this.timeMatcher, nowWithoutMs()));
40066
- }
40067
- nextRun() {
40068
- return this.timeMatcher.getNextMatch(new Date());
40069
- }
40070
- stop() {
40071
- this.running = false;
40072
- if (this.heartBeatTimeout) {
40073
- clearTimeout(this.heartBeatTimeout);
40074
- this.heartBeatTimeout = undefined;
40075
- }
40076
- }
40077
- isStarted() {
40078
- return !!this.heartBeatTimeout && this.running;
40079
- }
40080
- isStopped() {
40081
- return !this.isStarted();
40082
- }
40083
- async execute() {
40084
- const date = new Date();
40085
- const execution = {
40086
- id: (0, create_id_1.createID)('exec'),
40087
- reason: 'invoked'
40088
- };
40089
- try {
40090
- const shouldExecute = await this.beforeRun(date, execution);
40091
- if (shouldExecute) {
40092
- this.runCount++;
40093
- execution.startedAt = new Date();
40094
- const result = await this.onMatch(date, execution);
40095
- execution.finishedAt = new Date();
40096
- execution.result = result;
40097
- this.onFinished(date, execution);
40098
- }
40099
- }
40100
- catch (error) {
40101
- execution.finishedAt = new Date();
40102
- execution.error = error;
40103
- this.onError(date, error, execution);
40104
- }
40105
- }
40106
- }
40107
- runner.Runner = Runner;
40108
- async function runAsync(fn, date, onError) {
40109
- try {
40110
- await fn(date);
40111
- }
40112
- catch (error) {
40113
- onError(date, error);
40114
- }
40115
- }
40116
- function getDelay(timeMatcher, currentDate) {
40117
- const maxDelay = 86400000;
40118
- const nextRun = timeMatcher.getNextMatch(currentDate);
40119
- const now = new Date();
40120
- const delay = nextRun.getTime() - now.getTime();
40121
- if (delay > maxDelay) {
40122
- return maxDelay;
40123
- }
40124
- return Math.max(0, delay);
40125
- }
40126
- function nowWithoutMs() {
40127
- const date = new Date();
40128
- date.setMilliseconds(0);
40129
- return date;
40130
- }
40131
-
40132
- return runner;
40133
- }
40134
-
40135
- var timeMatcher = {};
40136
-
40137
- var convertion = {};
40138
-
40139
- var monthNamesConversion = {};
40140
-
40141
- var hasRequiredMonthNamesConversion;
40142
-
40143
- function requireMonthNamesConversion () {
40144
- if (hasRequiredMonthNamesConversion) return monthNamesConversion;
40145
- hasRequiredMonthNamesConversion = 1;
40146
- Object.defineProperty(monthNamesConversion, "__esModule", { value: true });
40147
- monthNamesConversion.default = (() => {
40148
- const months = ['january', 'february', 'march', 'april', 'may', 'june', 'july',
40149
- 'august', 'september', 'october', 'november', 'december'];
40150
- const shortMonths = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug',
40151
- 'sep', 'oct', 'nov', 'dec'];
40152
- function convertMonthName(expression, items) {
40153
- for (let i = 0; i < items.length; i++) {
40154
- expression = expression.replace(new RegExp(items[i], 'gi'), i + 1);
40155
- }
40156
- return expression;
40157
- }
40158
- function interprete(monthExpression) {
40159
- monthExpression = convertMonthName(monthExpression, months);
40160
- monthExpression = convertMonthName(monthExpression, shortMonths);
40161
- return monthExpression;
40162
- }
40163
- return interprete;
40164
- })();
40165
-
40166
- return monthNamesConversion;
40167
- }
40168
-
40169
- var weekDayNamesConversion = {};
40170
-
40171
- var hasRequiredWeekDayNamesConversion;
40172
-
40173
- function requireWeekDayNamesConversion () {
40174
- if (hasRequiredWeekDayNamesConversion) return weekDayNamesConversion;
40175
- hasRequiredWeekDayNamesConversion = 1;
40176
- Object.defineProperty(weekDayNamesConversion, "__esModule", { value: true });
40177
- weekDayNamesConversion.default = (() => {
40178
- const weekDays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday',
40179
- 'friday', 'saturday'];
40180
- const shortWeekDays = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
40181
- function convertWeekDayName(expression, items) {
40182
- for (let i = 0; i < items.length; i++) {
40183
- expression = expression.replace(new RegExp(items[i], 'gi'), i);
40184
- }
40185
- return expression;
40186
- }
40187
- function convertWeekDays(expression) {
40188
- expression = expression.replace('7', '0');
40189
- expression = convertWeekDayName(expression, weekDays);
40190
- return convertWeekDayName(expression, shortWeekDays);
40191
- }
40192
- return convertWeekDays;
40193
- })();
40194
-
40195
- return weekDayNamesConversion;
40196
- }
40197
-
40198
- var asteriskToRangeConversion = {};
40199
-
40200
- var hasRequiredAsteriskToRangeConversion;
40201
-
40202
- function requireAsteriskToRangeConversion () {
40203
- if (hasRequiredAsteriskToRangeConversion) return asteriskToRangeConversion;
40204
- hasRequiredAsteriskToRangeConversion = 1;
40205
- Object.defineProperty(asteriskToRangeConversion, "__esModule", { value: true });
40206
- asteriskToRangeConversion.default = (() => {
40207
- function convertAsterisk(expression, replecement) {
40208
- if (expression.indexOf('*') !== -1) {
40209
- return expression.replace('*', replecement);
40210
- }
40211
- return expression;
40212
- }
40213
- function convertAsterisksToRanges(expressions) {
40214
- expressions[0] = convertAsterisk(expressions[0], '0-59');
40215
- expressions[1] = convertAsterisk(expressions[1], '0-59');
40216
- expressions[2] = convertAsterisk(expressions[2], '0-23');
40217
- expressions[3] = convertAsterisk(expressions[3], '1-31');
40218
- expressions[4] = convertAsterisk(expressions[4], '1-12');
40219
- expressions[5] = convertAsterisk(expressions[5], '0-6');
40220
- return expressions;
40221
- }
40222
- return convertAsterisksToRanges;
40223
- })();
40224
-
40225
- return asteriskToRangeConversion;
40226
- }
40227
-
40228
- var rangeConversion = {};
40229
-
40230
- var hasRequiredRangeConversion;
40231
-
40232
- function requireRangeConversion () {
40233
- if (hasRequiredRangeConversion) return rangeConversion;
40234
- hasRequiredRangeConversion = 1;
40235
- Object.defineProperty(rangeConversion, "__esModule", { value: true });
40236
- rangeConversion.default = (() => {
40237
- function replaceWithRange(expression, text, init, end, stepTxt) {
40238
- const step = parseInt(stepTxt);
40239
- const numbers = [];
40240
- let last = parseInt(end);
40241
- let first = parseInt(init);
40242
- if (first > last) {
40243
- last = parseInt(init);
40244
- first = parseInt(end);
40245
- }
40246
- for (let i = first; i <= last; i += step) {
40247
- numbers.push(i);
40248
- }
40249
- return expression.replace(new RegExp(text, 'i'), numbers.join());
40250
- }
40251
- function convertRange(expression) {
40252
- const rangeRegEx = /(\d+)-(\d+)(\/(\d+)|)/;
40253
- let match = rangeRegEx.exec(expression);
40254
- while (match !== null && match.length > 0) {
40255
- expression = replaceWithRange(expression, match[0], match[1], match[2], match[4] || '1');
40256
- match = rangeRegEx.exec(expression);
40257
- }
40258
- return expression;
40259
- }
40260
- function convertAllRanges(expressions) {
40261
- for (let i = 0; i < expressions.length; i++) {
40262
- expressions[i] = convertRange(expressions[i]);
40263
- }
40264
- return expressions;
40265
- }
40266
- return convertAllRanges;
40267
- })();
40268
-
40269
- return rangeConversion;
40270
- }
40271
-
40272
- var hasRequiredConvertion;
40273
-
40274
- function requireConvertion () {
40275
- if (hasRequiredConvertion) return convertion;
40276
- hasRequiredConvertion = 1;
40277
- var __importDefault = (convertion && convertion.__importDefault) || function (mod) {
40278
- return (mod && mod.__esModule) ? mod : { "default": mod };
40279
- };
40280
- Object.defineProperty(convertion, "__esModule", { value: true });
40281
- const month_names_conversion_1 = __importDefault(requireMonthNamesConversion());
40282
- const week_day_names_conversion_1 = __importDefault(requireWeekDayNamesConversion());
40283
- const asterisk_to_range_conversion_1 = __importDefault(requireAsteriskToRangeConversion());
40284
- const range_conversion_1 = __importDefault(requireRangeConversion());
40285
- convertion.default = (() => {
40286
- function appendSeccondExpression(expressions) {
40287
- if (expressions.length === 5) {
40288
- return ['0'].concat(expressions);
40289
- }
40290
- return expressions;
40291
- }
40292
- function removeSpaces(str) {
40293
- return str.replace(/\s{2,}/g, ' ').trim();
40294
- }
40295
- function normalizeIntegers(expressions) {
40296
- for (let i = 0; i < expressions.length; i++) {
40297
- const numbers = expressions[i].split(',');
40298
- for (let j = 0; j < numbers.length; j++) {
40299
- numbers[j] = parseInt(numbers[j]);
40300
- }
40301
- expressions[i] = numbers;
40302
- }
40303
- return expressions;
40304
- }
40305
- function interprete(expression) {
40306
- let expressions = removeSpaces(`${expression}`).split(' ');
40307
- expressions = appendSeccondExpression(expressions);
40308
- expressions[4] = (0, month_names_conversion_1.default)(expressions[4]);
40309
- expressions[5] = (0, week_day_names_conversion_1.default)(expressions[5]);
40310
- expressions = (0, asterisk_to_range_conversion_1.default)(expressions);
40311
- expressions = (0, range_conversion_1.default)(expressions);
40312
- expressions = normalizeIntegers(expressions);
40313
- return expressions;
40314
- }
40315
- return interprete;
40316
- })();
40317
-
40318
- return convertion;
40319
- }
40320
-
40321
- var localizedTime = {};
40322
-
40323
- var hasRequiredLocalizedTime;
40324
-
40325
- function requireLocalizedTime () {
40326
- if (hasRequiredLocalizedTime) return localizedTime;
40327
- hasRequiredLocalizedTime = 1;
40328
- Object.defineProperty(localizedTime, "__esModule", { value: true });
40329
- localizedTime.LocalizedTime = void 0;
40330
- class LocalizedTime {
40331
- timestamp;
40332
- parts;
40333
- timezone;
40334
- constructor(date, timezone) {
40335
- this.timestamp = date.getTime();
40336
- this.timezone = timezone;
40337
- this.parts = buildDateParts(date, timezone);
40338
- }
40339
- toDate() {
40340
- return new Date(this.timestamp);
40341
- }
40342
- toISO() {
40343
- const gmt = this.parts.gmt.replace(/^GMT/, '');
40344
- const offset = gmt ? gmt : 'Z';
40345
- const pad = (n) => String(n).padStart(2, '0');
40346
- return `${this.parts.year}-${pad(this.parts.month)}-${pad(this.parts.day)}`
40347
- + `T${pad(this.parts.hour)}:${pad(this.parts.minute)}:${pad(this.parts.second)}`
40348
- + `.${String(this.parts.milisecond).padStart(3, '0')}`
40349
- + offset;
40350
- }
40351
- getParts() {
40352
- return this.parts;
40353
- }
40354
- set(field, value) {
40355
- this.parts[field] = value;
40356
- const newDate = new Date(this.toISO());
40357
- this.timestamp = newDate.getTime();
40358
- this.parts = buildDateParts(newDate, this.timezone);
40359
- }
40360
- }
40361
- localizedTime.LocalizedTime = LocalizedTime;
40362
- function buildDateParts(date, timezone) {
40363
- const dftOptions = {
40364
- year: 'numeric',
40365
- month: '2-digit',
40366
- day: '2-digit',
40367
- hour: '2-digit',
40368
- minute: '2-digit',
40369
- second: '2-digit',
40370
- weekday: 'short',
40371
- hour12: false
40372
- };
40373
- if (timezone) {
40374
- dftOptions.timeZone = timezone;
40375
- }
40376
- const dateFormat = new Intl.DateTimeFormat('en-US', dftOptions);
40377
- const parts = dateFormat.formatToParts(date).filter(part => {
40378
- return part.type !== 'literal';
40379
- }).reduce((acc, part) => {
40380
- acc[part.type] = part.value;
40381
- return acc;
40382
- }, {});
40383
- return {
40384
- day: parseInt(parts.day),
40385
- month: parseInt(parts.month),
40386
- year: parseInt(parts.year),
40387
- hour: parts.hour === '24' ? 0 : parseInt(parts.hour),
40388
- minute: parseInt(parts.minute),
40389
- second: parseInt(parts.second),
40390
- milisecond: date.getMilliseconds(),
40391
- weekday: parts.weekday,
40392
- gmt: getTimezoneGMT(date, timezone)
40393
- };
40394
- }
40395
- function getTimezoneGMT(date, timezone) {
40396
- const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
40397
- const tzDate = new Date(date.toLocaleString('en-US', { timeZone: timezone }));
40398
- let offsetInMinutes = (utcDate.getTime() - tzDate.getTime()) / 60000;
40399
- const sign = offsetInMinutes <= 0 ? '+' : '-';
40400
- offsetInMinutes = Math.abs(offsetInMinutes);
40401
- if (offsetInMinutes === 0)
40402
- return 'Z';
40403
- const hours = Math.floor(offsetInMinutes / 60).toString().padStart(2, '0');
40404
- const minutes = Math.floor(offsetInMinutes % 60).toString().padStart(2, '0');
40405
- return `GMT${sign}${hours}:${minutes}`;
40406
- }
40407
-
40408
- return localizedTime;
40409
- }
40410
-
40411
- var matcherWalker = {};
40412
-
40413
- var hasRequiredMatcherWalker;
40414
-
40415
- function requireMatcherWalker () {
40416
- if (hasRequiredMatcherWalker) return matcherWalker;
40417
- hasRequiredMatcherWalker = 1;
40418
- var __importDefault = (matcherWalker && matcherWalker.__importDefault) || function (mod) {
40419
- return (mod && mod.__esModule) ? mod : { "default": mod };
40420
- };
40421
- Object.defineProperty(matcherWalker, "__esModule", { value: true });
40422
- matcherWalker.MatcherWalker = void 0;
40423
- const convertion_1 = __importDefault(requireConvertion());
40424
- const localized_time_1 = requireLocalizedTime();
40425
- const time_matcher_1 = requireTimeMatcher();
40426
- const week_day_names_conversion_1 = __importDefault(requireWeekDayNamesConversion());
40427
- class MatcherWalker {
40428
- cronExpression;
40429
- baseDate;
40430
- pattern;
40431
- expressions;
40432
- timeMatcher;
40433
- timezone;
40434
- constructor(cronExpression, baseDate, timezone) {
40435
- this.cronExpression = cronExpression;
40436
- this.baseDate = baseDate;
40437
- this.timeMatcher = new time_matcher_1.TimeMatcher(cronExpression, timezone);
40438
- this.timezone = timezone;
40439
- this.expressions = (0, convertion_1.default)(cronExpression);
40440
- }
40441
- isMatching() {
40442
- return this.timeMatcher.match(this.baseDate);
40443
- }
40444
- matchNext() {
40445
- const findNextDateIgnoringWeekday = () => {
40446
- const baseDate = new Date(this.baseDate.getTime());
40447
- baseDate.setMilliseconds(0);
40448
- const localTime = new localized_time_1.LocalizedTime(baseDate, this.timezone);
40449
- const dateParts = localTime.getParts();
40450
- const date = new localized_time_1.LocalizedTime(localTime.toDate(), this.timezone);
40451
- const seconds = this.expressions[0];
40452
- const nextSecond = availableValue(seconds, dateParts.second);
40453
- if (nextSecond) {
40454
- date.set('second', nextSecond);
40455
- if (this.timeMatcher.match(date.toDate())) {
40456
- return date;
40457
- }
40458
- }
40459
- date.set('second', seconds[0]);
40460
- const minutes = this.expressions[1];
40461
- const nextMinute = availableValue(minutes, dateParts.minute);
40462
- if (nextMinute) {
40463
- date.set('minute', nextMinute);
40464
- if (this.timeMatcher.match(date.toDate())) {
40465
- return date;
40466
- }
40467
- }
40468
- date.set('minute', minutes[0]);
40469
- const hours = this.expressions[2];
40470
- const nextHour = availableValue(hours, dateParts.hour);
40471
- if (nextHour) {
40472
- date.set('hour', nextHour);
40473
- if (this.timeMatcher.match(date.toDate())) {
40474
- return date;
40475
- }
40476
- }
40477
- date.set('hour', hours[0]);
40478
- const days = this.expressions[3];
40479
- const nextDay = availableValue(days, dateParts.day);
40480
- if (nextDay) {
40481
- date.set('day', nextDay);
40482
- if (this.timeMatcher.match(date.toDate())) {
40483
- return date;
40484
- }
40485
- }
40486
- date.set('day', days[0]);
40487
- const months = this.expressions[4];
40488
- const nextMonth = availableValue(months, dateParts.month);
40489
- if (nextMonth) {
40490
- date.set('month', nextMonth);
40491
- if (this.timeMatcher.match(date.toDate())) {
40492
- return date;
40493
- }
40494
- }
40495
- date.set('year', date.getParts().year + 1);
40496
- date.set('month', months[0]);
40497
- return date;
40498
- };
40499
- const date = findNextDateIgnoringWeekday();
40500
- const weekdays = this.expressions[5];
40501
- let currentWeekday = parseInt((0, week_day_names_conversion_1.default)(date.getParts().weekday));
40502
- while (!(weekdays.indexOf(currentWeekday) > -1)) {
40503
- date.set('year', date.getParts().year + 1);
40504
- currentWeekday = parseInt((0, week_day_names_conversion_1.default)(date.getParts().weekday));
40505
- }
40506
- return date;
40507
- }
40508
- }
40509
- matcherWalker.MatcherWalker = MatcherWalker;
40510
- function availableValue(values, currentValue) {
40511
- const availableValues = values.sort((a, b) => a - b).filter(s => s > currentValue);
40512
- if (availableValues.length > 0)
40513
- return availableValues[0];
40514
- return false;
40515
- }
40516
-
40517
- return matcherWalker;
40518
- }
40519
-
40520
- var hasRequiredTimeMatcher;
40521
-
40522
- function requireTimeMatcher () {
40523
- if (hasRequiredTimeMatcher) return timeMatcher;
40524
- hasRequiredTimeMatcher = 1;
40525
- var __importDefault = (timeMatcher && timeMatcher.__importDefault) || function (mod) {
40526
- return (mod && mod.__esModule) ? mod : { "default": mod };
40527
- };
40528
- Object.defineProperty(timeMatcher, "__esModule", { value: true });
40529
- timeMatcher.TimeMatcher = void 0;
40530
- const index_1 = __importDefault(requireConvertion());
40531
- const week_day_names_conversion_1 = __importDefault(requireWeekDayNamesConversion());
40532
- const localized_time_1 = requireLocalizedTime();
40533
- const matcher_walker_1 = requireMatcherWalker();
40534
- function matchValue(allowedValues, value) {
40535
- return allowedValues.indexOf(value) !== -1;
40536
- }
40537
- class TimeMatcher {
40538
- timezone;
40539
- pattern;
40540
- expressions;
40541
- constructor(pattern, timezone) {
40542
- this.timezone = timezone;
40543
- this.pattern = pattern;
40544
- this.expressions = (0, index_1.default)(pattern);
40545
- }
40546
- match(date) {
40547
- const localizedTime = new localized_time_1.LocalizedTime(date, this.timezone);
40548
- const parts = localizedTime.getParts();
40549
- const runOnSecond = matchValue(this.expressions[0], parts.second);
40550
- const runOnMinute = matchValue(this.expressions[1], parts.minute);
40551
- const runOnHour = matchValue(this.expressions[2], parts.hour);
40552
- const runOnDay = matchValue(this.expressions[3], parts.day);
40553
- const runOnMonth = matchValue(this.expressions[4], parts.month);
40554
- const runOnWeekDay = matchValue(this.expressions[5], parseInt((0, week_day_names_conversion_1.default)(parts.weekday)));
40555
- return runOnSecond && runOnMinute && runOnHour && runOnDay && runOnMonth && runOnWeekDay;
40556
- }
40557
- getNextMatch(date) {
40558
- const walker = new matcher_walker_1.MatcherWalker(this.pattern, date, this.timezone);
40559
- const next = walker.matchNext();
40560
- return next.toDate();
40561
- }
40562
- }
40563
- timeMatcher.TimeMatcher = TimeMatcher;
40564
-
40565
- return timeMatcher;
40566
- }
40567
-
40568
- var stateMachine = {};
40569
-
40570
- var hasRequiredStateMachine;
40571
-
40572
- function requireStateMachine () {
40573
- if (hasRequiredStateMachine) return stateMachine;
40574
- hasRequiredStateMachine = 1;
40575
- Object.defineProperty(stateMachine, "__esModule", { value: true });
40576
- stateMachine.StateMachine = void 0;
40577
- const allowedTransitions = {
40578
- 'stopped': ['stopped', 'idle', 'destroyed'],
40579
- 'idle': ['idle', 'running', 'stopped', 'destroyed'],
40580
- 'running': ['running', 'idle', 'stopped', 'destroyed'],
40581
- 'destroyed': ['destroyed']
40582
- };
40583
- class StateMachine {
40584
- state;
40585
- constructor(initial = 'stopped') {
40586
- this.state = initial;
40587
- }
40588
- changeState(state) {
40589
- if (allowedTransitions[this.state].includes(state)) {
40590
- this.state = state;
40591
- }
40592
- else {
40593
- throw new Error(`invalid transition from ${this.state} to ${state}`);
40594
- }
40595
- }
40596
- }
40597
- stateMachine.StateMachine = StateMachine;
40598
-
40599
- return stateMachine;
40600
- }
40601
-
40602
- var hasRequiredInlineScheduledTask;
40603
-
40604
- function requireInlineScheduledTask () {
40605
- if (hasRequiredInlineScheduledTask) return inlineScheduledTask;
40606
- hasRequiredInlineScheduledTask = 1;
40607
- var __importDefault = (inlineScheduledTask && inlineScheduledTask.__importDefault) || function (mod) {
40608
- return (mod && mod.__esModule) ? mod : { "default": mod };
40609
- };
40610
- Object.defineProperty(inlineScheduledTask, "__esModule", { value: true });
40611
- inlineScheduledTask.InlineScheduledTask = void 0;
40612
- const events_1 = __importDefault(EventEmitter);
40613
- const runner_1 = requireRunner();
40614
- const time_matcher_1 = requireTimeMatcher();
40615
- const create_id_1 = requireCreateId();
40616
- const state_machine_1 = requireStateMachine();
40617
- const logger_1 = __importDefault(requireLogger());
40618
- const localized_time_1 = requireLocalizedTime();
40619
- class TaskEmitter extends events_1.default {
40620
- }
40621
- class InlineScheduledTask {
40622
- emitter;
40623
- cronExpression;
40624
- timeMatcher;
40625
- runner;
40626
- id;
40627
- name;
40628
- stateMachine;
40629
- timezone;
40630
- constructor(cronExpression, taskFn, options) {
40631
- this.emitter = new TaskEmitter();
40632
- this.cronExpression = cronExpression;
40633
- this.id = (0, create_id_1.createID)('task', 12);
40634
- this.name = options?.name || this.id;
40635
- this.timezone = options?.timezone;
40636
- this.timeMatcher = new time_matcher_1.TimeMatcher(cronExpression, options?.timezone);
40637
- this.stateMachine = new state_machine_1.StateMachine();
40638
- const runnerOptions = {
40639
- timezone: options?.timezone,
40640
- noOverlap: options?.noOverlap,
40641
- maxExecutions: options?.maxExecutions,
40642
- maxRandomDelay: options?.maxRandomDelay,
40643
- beforeRun: (date, execution) => {
40644
- if (execution.reason === 'scheduled') {
40645
- this.changeState('running');
40646
- }
40647
- this.emitter.emit('execution:started', this.createContext(date, execution));
40648
- return true;
40649
- },
40650
- onFinished: (date, execution) => {
40651
- if (execution.reason === 'scheduled') {
40652
- this.changeState('idle');
40653
- }
40654
- this.emitter.emit('execution:finished', this.createContext(date, execution));
40655
- return true;
40656
- },
40657
- onError: (date, error, execution) => {
40658
- logger_1.default.error(error);
40659
- this.emitter.emit('execution:failed', this.createContext(date, execution));
40660
- this.changeState('idle');
40661
- },
40662
- onOverlap: (date) => {
40663
- this.emitter.emit('execution:overlap', this.createContext(date));
40664
- },
40665
- onMissedExecution: (date) => {
40666
- this.emitter.emit('execution:missed', this.createContext(date));
40667
- },
40668
- onMaxExecutions: (date) => {
40669
- this.emitter.emit('execution:maxReached', this.createContext(date));
40670
- this.destroy();
40671
- }
40672
- };
40673
- this.runner = new runner_1.Runner(this.timeMatcher, (date, execution) => {
40674
- return taskFn(this.createContext(date, execution));
40675
- }, runnerOptions);
40676
- }
40677
- getNextRun() {
40678
- if (this.stateMachine.state !== 'stopped') {
40679
- return this.runner.nextRun();
40680
- }
40681
- return null;
40682
- }
40683
- changeState(state) {
40684
- if (this.runner.isStarted()) {
40685
- this.stateMachine.changeState(state);
40686
- }
40687
- }
40688
- start() {
40689
- if (this.runner.isStopped()) {
40690
- this.runner.start();
40691
- this.stateMachine.changeState('idle');
40692
- this.emitter.emit('task:started', this.createContext(new Date()));
40693
- }
40694
- }
40695
- stop() {
40696
- if (this.runner.isStarted()) {
40697
- this.runner.stop();
40698
- this.stateMachine.changeState('stopped');
40699
- this.emitter.emit('task:stopped', this.createContext(new Date()));
40700
- }
40701
- }
40702
- getStatus() {
40703
- return this.stateMachine.state;
40704
- }
40705
- destroy() {
40706
- if (this.stateMachine.state === 'destroyed')
40707
- return;
40708
- this.stop();
40709
- this.stateMachine.changeState('destroyed');
40710
- this.emitter.emit('task:destroyed', this.createContext(new Date()));
40711
- }
40712
- execute() {
40713
- return new Promise((resolve, reject) => {
40714
- const onFail = (context) => {
40715
- this.off('execution:finished', onFail);
40716
- reject(context.execution?.error);
40717
- };
40718
- const onFinished = (context) => {
40719
- this.off('execution:failed', onFail);
40720
- resolve(context.execution?.result);
40721
- };
40722
- this.once('execution:finished', onFinished);
40723
- this.once('execution:failed', onFail);
40724
- this.runner.execute();
40725
- });
40726
- }
40727
- on(event, fun) {
40728
- this.emitter.on(event, fun);
40729
- }
40730
- off(event, fun) {
40731
- this.emitter.off(event, fun);
40732
- }
40733
- once(event, fun) {
40734
- this.emitter.once(event, fun);
40735
- }
40736
- createContext(executionDate, execution) {
40737
- const localTime = new localized_time_1.LocalizedTime(executionDate, this.timezone);
40738
- const ctx = {
40739
- date: localTime.toDate(),
40740
- dateLocalIso: localTime.toISO(),
40741
- triggeredAt: new Date(),
40742
- task: this,
40743
- execution: execution
40744
- };
40745
- return ctx;
40746
- }
40747
- }
40748
- inlineScheduledTask.InlineScheduledTask = InlineScheduledTask;
40749
-
40750
- return inlineScheduledTask;
40751
- }
40752
-
40753
- var taskRegistry = {};
40754
-
40755
- var hasRequiredTaskRegistry;
40756
-
40757
- function requireTaskRegistry () {
40758
- if (hasRequiredTaskRegistry) return taskRegistry;
40759
- hasRequiredTaskRegistry = 1;
40760
- Object.defineProperty(taskRegistry, "__esModule", { value: true });
40761
- taskRegistry.TaskRegistry = void 0;
40762
- const tasks = new Map();
40763
- class TaskRegistry {
40764
- add(task) {
40765
- if (this.has(task.id)) {
40766
- throw Error(`task ${task.id} already registred!`);
40767
- }
40768
- tasks.set(task.id, task);
40769
- task.on('task:destroyed', () => {
40770
- this.remove(task);
40771
- });
40772
- }
40773
- get(taskId) {
40774
- return tasks.get(taskId);
40775
- }
40776
- remove(task) {
40777
- if (this.has(task.id)) {
40778
- task?.destroy();
40779
- tasks.delete(task.id);
40780
- }
40781
- }
40782
- all() {
40783
- return tasks;
40784
- }
40785
- has(taskId) {
40786
- return tasks.has(taskId);
40787
- }
40788
- killAll() {
40789
- tasks.forEach(id => this.remove(id));
40790
- }
40791
- }
40792
- taskRegistry.TaskRegistry = TaskRegistry;
40793
-
40794
- return taskRegistry;
40795
- }
40796
-
40797
- var patternValidation = {};
40798
-
40799
- var hasRequiredPatternValidation;
40800
-
40801
- function requirePatternValidation () {
40802
- if (hasRequiredPatternValidation) return patternValidation;
40803
- hasRequiredPatternValidation = 1;
40804
- var __importDefault = (patternValidation && patternValidation.__importDefault) || function (mod) {
40805
- return (mod && mod.__esModule) ? mod : { "default": mod };
40806
- };
40807
- Object.defineProperty(patternValidation, "__esModule", { value: true });
40808
- const index_1 = __importDefault(requireConvertion());
40809
- const validationRegex = /^(?:\d+|\*|\*\/\d+)$/;
40810
- function isValidExpression(expression, min, max) {
40811
- const options = expression;
40812
- for (const option of options) {
40813
- const optionAsInt = parseInt(option, 10);
40814
- if ((!Number.isNaN(optionAsInt) &&
40815
- (optionAsInt < min || optionAsInt > max)) ||
40816
- !validationRegex.test(option))
40817
- return false;
40818
- }
40819
- return true;
40820
- }
40821
- function isInvalidSecond(expression) {
40822
- return !isValidExpression(expression, 0, 59);
40823
- }
40824
- function isInvalidMinute(expression) {
40825
- return !isValidExpression(expression, 0, 59);
40826
- }
40827
- function isInvalidHour(expression) {
40828
- return !isValidExpression(expression, 0, 23);
40829
- }
40830
- function isInvalidDayOfMonth(expression) {
40831
- return !isValidExpression(expression, 1, 31);
40832
- }
40833
- function isInvalidMonth(expression) {
40834
- return !isValidExpression(expression, 1, 12);
40835
- }
40836
- function isInvalidWeekDay(expression) {
40837
- return !isValidExpression(expression, 0, 7);
40838
- }
40839
- function validateFields(patterns, executablePatterns) {
40840
- if (isInvalidSecond(executablePatterns[0]))
40841
- throw new Error(`${patterns[0]} is a invalid expression for second`);
40842
- if (isInvalidMinute(executablePatterns[1]))
40843
- throw new Error(`${patterns[1]} is a invalid expression for minute`);
40844
- if (isInvalidHour(executablePatterns[2]))
40845
- throw new Error(`${patterns[2]} is a invalid expression for hour`);
40846
- if (isInvalidDayOfMonth(executablePatterns[3]))
40847
- throw new Error(`${patterns[3]} is a invalid expression for day of month`);
40848
- if (isInvalidMonth(executablePatterns[4]))
40849
- throw new Error(`${patterns[4]} is a invalid expression for month`);
40850
- if (isInvalidWeekDay(executablePatterns[5]))
40851
- throw new Error(`${patterns[5]} is a invalid expression for week day`);
40852
- }
40853
- function validate(pattern) {
40854
- if (typeof pattern !== 'string')
40855
- throw new TypeError('pattern must be a string!');
40856
- const patterns = pattern.split(' ');
40857
- const executablePatterns = (0, index_1.default)(pattern);
40858
- if (patterns.length === 5)
40859
- patterns.unshift('0');
40860
- validateFields(patterns, executablePatterns);
40861
- }
40862
- patternValidation.default = validate;
40863
-
40864
- return patternValidation;
40865
- }
40866
-
40867
- var backgroundScheduledTask = {};
40868
-
40869
- var hasRequiredBackgroundScheduledTask;
40870
-
40871
- function requireBackgroundScheduledTask () {
40872
- if (hasRequiredBackgroundScheduledTask) return backgroundScheduledTask;
40873
- hasRequiredBackgroundScheduledTask = 1;
40874
- var __importDefault = (backgroundScheduledTask && backgroundScheduledTask.__importDefault) || function (mod) {
40875
- return (mod && mod.__esModule) ? mod : { "default": mod };
40876
- };
40877
- Object.defineProperty(backgroundScheduledTask, "__esModule", { value: true });
40878
- const path_1 = path$1;
40879
- const child_process_1 = require$$1;
40880
- const create_id_1 = requireCreateId();
40881
- const stream_1 = require$$3;
40882
- const state_machine_1 = requireStateMachine();
40883
- const localized_time_1 = requireLocalizedTime();
40884
- const logger_1 = __importDefault(requireLogger());
40885
- const time_matcher_1 = requireTimeMatcher();
40886
- const daemonPath = (0, path_1.resolve)(__dirname, 'daemon.js');
40887
- class TaskEmitter extends stream_1.EventEmitter {
40888
- }
40889
- class BackgroundScheduledTask {
40890
- emitter;
40891
- id;
40892
- name;
40893
- cronExpression;
40894
- taskPath;
40895
- options;
40896
- forkProcess;
40897
- stateMachine;
40898
- constructor(cronExpression, taskPath, options) {
40899
- this.cronExpression = cronExpression;
40900
- this.taskPath = taskPath;
40901
- this.options = options;
40902
- this.id = (0, create_id_1.createID)('task');
40903
- this.name = options?.name || this.id;
40904
- this.emitter = new TaskEmitter();
40905
- this.stateMachine = new state_machine_1.StateMachine('stopped');
40906
- this.on('task:stopped', () => {
40907
- this.forkProcess?.kill();
40908
- this.forkProcess = undefined;
40909
- this.stateMachine.changeState('stopped');
40910
- });
40911
- this.on('task:destroyed', () => {
40912
- this.forkProcess?.kill();
40913
- this.forkProcess = undefined;
40914
- this.stateMachine.changeState('destroyed');
40915
- });
40916
- }
40917
- getNextRun() {
40918
- if (this.stateMachine.state !== 'stopped') {
40919
- const timeMatcher = new time_matcher_1.TimeMatcher(this.cronExpression, this.options?.timezone);
40920
- return timeMatcher.getNextMatch(new Date());
40921
- }
40922
- return null;
40923
- }
40924
- start() {
40925
- return new Promise((resolve, reject) => {
40926
- if (this.forkProcess) {
40927
- return resolve(undefined);
40928
- }
40929
- const timeout = setTimeout(() => {
40930
- reject(new Error('Start operation timed out'));
40931
- }, 5000);
40932
- try {
40933
- this.forkProcess = (0, child_process_1.fork)(daemonPath);
40934
- this.forkProcess.on('error', (err) => {
40935
- clearTimeout(timeout);
40936
- reject(new Error(`Error on daemon: ${err.message}`));
40937
- });
40938
- this.forkProcess.on('exit', (code, signal) => {
40939
- if (code !== 0 && signal !== 'SIGTERM') {
40940
- const erro = new Error(`node-cron daemon exited with code ${code || signal}`);
40941
- logger_1.default.error(erro);
40942
- clearTimeout(timeout);
40943
- reject(erro);
40944
- }
40945
- });
40946
- this.forkProcess.on('message', (message) => {
40947
- if (message.jsonError) {
40948
- if (message.context?.execution) {
40949
- message.context.execution.error = deserializeError(message.jsonError);
40950
- delete message.jsonError;
40951
- }
40952
- }
40953
- if (message.context?.task?.state) {
40954
- this.stateMachine.changeState(message.context?.task?.state);
40955
- }
40956
- if (message.context) {
40957
- const execution = message.context?.execution;
40958
- delete execution?.hasError;
40959
- const context = this.createContext(new Date(message.context.date), execution);
40960
- this.emitter.emit(message.event, context);
40961
- }
40962
- });
40963
- this.once('task:started', () => {
40964
- this.stateMachine.changeState('idle');
40965
- clearTimeout(timeout);
40966
- resolve(undefined);
40967
- });
40968
- this.forkProcess.send({
40969
- command: 'task:start',
40970
- path: this.taskPath,
40971
- cron: this.cronExpression,
40972
- options: this.options
40973
- });
40974
- }
40975
- catch (error) {
40976
- reject(error);
40977
- }
40978
- });
40979
- }
40980
- stop() {
40981
- return new Promise((resolve, reject) => {
40982
- if (!this.forkProcess) {
40983
- return resolve(undefined);
40984
- }
40985
- const timeoutId = setTimeout(() => {
40986
- clearTimeout(timeoutId);
40987
- reject(new Error('Stop operation timed out'));
40988
- }, 5000);
40989
- const cleanupAndResolve = () => {
40990
- clearTimeout(timeoutId);
40991
- this.off('task:stopped', onStopped);
40992
- this.forkProcess = undefined;
40993
- resolve(undefined);
40994
- };
40995
- const onStopped = () => {
40996
- cleanupAndResolve();
40997
- };
40998
- this.once('task:stopped', onStopped);
40999
- this.forkProcess.send({
41000
- command: 'task:stop'
41001
- });
41002
- });
41003
- }
41004
- getStatus() {
41005
- return this.stateMachine.state;
41006
- }
41007
- destroy() {
41008
- return new Promise((resolve, reject) => {
41009
- if (!this.forkProcess) {
41010
- return resolve(undefined);
41011
- }
41012
- const timeoutId = setTimeout(() => {
41013
- clearTimeout(timeoutId);
41014
- reject(new Error('Destroy operation timed out'));
41015
- }, 5000);
41016
- const onDestroy = () => {
41017
- clearTimeout(timeoutId);
41018
- this.off('task:destroyed', onDestroy);
41019
- resolve(undefined);
41020
- };
41021
- this.once('task:destroyed', onDestroy);
41022
- this.forkProcess.send({
41023
- command: 'task:destroy'
41024
- });
41025
- });
41026
- }
41027
- execute() {
41028
- return new Promise((resolve, reject) => {
41029
- if (!this.forkProcess) {
41030
- return reject(new Error('Cannot execute background task because it hasn\'t been started yet. Please initialize the task using the start() method before attempting to execute it.'));
41031
- }
41032
- const timeoutId = setTimeout(() => {
41033
- cleanupListeners();
41034
- reject(new Error('Execution timeout exceeded'));
41035
- }, 5000);
41036
- const cleanupListeners = () => {
41037
- clearTimeout(timeoutId);
41038
- this.off('execution:finished', onFinished);
41039
- this.off('execution:failed', onFail);
41040
- };
41041
- const onFinished = (context) => {
41042
- cleanupListeners();
41043
- resolve(context.execution?.result);
41044
- };
41045
- const onFail = (context) => {
41046
- cleanupListeners();
41047
- reject(context.execution?.error || new Error('Execution failed without specific error'));
41048
- };
41049
- this.once('execution:finished', onFinished);
41050
- this.once('execution:failed', onFail);
41051
- this.forkProcess.send({
41052
- command: 'task:execute'
41053
- });
41054
- });
41055
- }
41056
- on(event, fun) {
41057
- this.emitter.on(event, fun);
41058
- }
41059
- off(event, fun) {
41060
- this.emitter.off(event, fun);
41061
- }
41062
- once(event, fun) {
41063
- this.emitter.once(event, fun);
41064
- }
41065
- createContext(executionDate, execution) {
41066
- const localTime = new localized_time_1.LocalizedTime(executionDate, this.options?.timezone);
41067
- const ctx = {
41068
- date: localTime.toDate(),
41069
- dateLocalIso: localTime.toISO(),
41070
- triggeredAt: new Date(),
41071
- task: this,
41072
- execution: execution
41073
- };
41074
- return ctx;
41075
- }
41076
- }
41077
- function deserializeError(str) {
41078
- const data = JSON.parse(str);
41079
- const Err = globalThis[data.name] || Error;
41080
- const err = new Err(data.message);
41081
- if (data.stack) {
41082
- err.stack = data.stack;
41083
- }
41084
- Object.keys(data).forEach(key => {
41085
- if (!['name', 'message', 'stack'].includes(key)) {
41086
- err[key] = data[key];
41087
- }
41088
- });
41089
- return err;
41090
- }
41091
- backgroundScheduledTask.default = BackgroundScheduledTask;
41092
-
41093
- return backgroundScheduledTask;
41094
- }
41095
-
41096
- var hasRequiredNodeCron;
41097
-
41098
- function requireNodeCron () {
41099
- if (hasRequiredNodeCron) return nodeCron$2;
41100
- hasRequiredNodeCron = 1;
41101
- (function (exports) {
41102
- var __importDefault = (nodeCron$2 && nodeCron$2.__importDefault) || function (mod) {
41103
- return (mod && mod.__esModule) ? mod : { "default": mod };
41104
- };
41105
- Object.defineProperty(exports, "__esModule", { value: true });
41106
- exports.nodeCron = exports.getTask = exports.getTasks = void 0;
41107
- exports.schedule = schedule;
41108
- exports.createTask = createTask;
41109
- exports.solvePath = solvePath;
41110
- exports.validate = validate;
41111
- const inline_scheduled_task_1 = requireInlineScheduledTask();
41112
- const task_registry_1 = requireTaskRegistry();
41113
- const pattern_validation_1 = __importDefault(requirePatternValidation());
41114
- const background_scheduled_task_1 = __importDefault(requireBackgroundScheduledTask());
41115
- const path_1 = __importDefault(path$1);
41116
- const url_1 = require$$5;
41117
- const registry = new task_registry_1.TaskRegistry();
41118
- function schedule(expression, func, options) {
41119
- const task = createTask(expression, func, options);
41120
- task.start();
41121
- return task;
41122
- }
41123
- function createTask(expression, func, options) {
41124
- let task;
41125
- if (func instanceof Function) {
41126
- task = new inline_scheduled_task_1.InlineScheduledTask(expression, func, options);
41127
- }
41128
- else {
41129
- const taskPath = solvePath(func);
41130
- task = new background_scheduled_task_1.default(expression, taskPath, options);
41131
- }
41132
- registry.add(task);
41133
- return task;
41134
- }
41135
- function solvePath(filePath) {
41136
- if (path_1.default.isAbsolute(filePath))
41137
- return (0, url_1.pathToFileURL)(filePath).href;
41138
- if (filePath.startsWith('file://'))
41139
- return filePath;
41140
- const stackLines = new Error().stack?.split('\n');
41141
- if (stackLines) {
41142
- stackLines?.shift();
41143
- const callerLine = stackLines?.find((line) => { return line.indexOf(__filename) === -1; });
41144
- const match = callerLine?.match(/(file:\/\/)?(((\/?)(\w:))?([/\\].+)):\d+:\d+/);
41145
- if (match) {
41146
- const dir = `${match[5] ?? ""}${path_1.default.dirname(match[6])}`;
41147
- return (0, url_1.pathToFileURL)(path_1.default.resolve(dir, filePath)).href;
41148
- }
41149
- }
41150
- throw new Error(`Could not locate task file ${filePath}`);
41151
- }
41152
- function validate(expression) {
41153
- try {
41154
- (0, pattern_validation_1.default)(expression);
41155
- return true;
41156
- }
41157
- catch (e) {
41158
- return false;
41159
- }
41160
- }
41161
- exports.getTasks = registry.all;
41162
- exports.getTask = registry.get;
41163
- exports.nodeCron = {
41164
- schedule,
41165
- createTask,
41166
- validate,
41167
- getTasks: exports.getTasks,
41168
- getTask: exports.getTask,
41169
- };
41170
- exports.default = exports.nodeCron;
41171
-
41172
- } (nodeCron$2));
41173
- return nodeCron$2;
41174
- }
41175
-
41176
- var nodeCronExports = requireNodeCron();
41177
- var nodeCron = /*@__PURE__*/getDefaultExportFromCjs(nodeCronExports);
41178
-
41179
- var nodeCron$1 = /*#__PURE__*/_mergeNamespaces({
41180
- __proto__: null,
41181
- default: nodeCron
41182
- }, [nodeCronExports]);
41183
-
41184
37805
  function silhouetteScore(vectors, assignments, centroids, distanceFn = euclideanDistance) {
41185
37806
  const k = centroids.length;
41186
37807
  const n = vectors.length;