@orieljs/framework 0.1.0-alpha.4 → 0.1.0-alpha.6
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/NOTICE +3 -0
- package/dist/{chunk-WSGWYXAO.js → chunk-I63ZFMX6.js} +3 -1
- package/dist/chunk-I63ZFMX6.js.map +1 -0
- package/dist/{chunk-3DETYZMZ.js → chunk-MFTZQMZM.js} +3 -3
- package/dist/chunk-POEP4B2M.js +2613 -0
- package/dist/chunk-POEP4B2M.js.map +1 -0
- package/dist/{chunk-SBZQ6L3Z.js → chunk-ZOEPKBVF.js} +1009 -279
- package/dist/chunk-ZOEPKBVF.js.map +1 -0
- package/dist/contracts.d.ts +63 -1
- package/dist/contracts.js +3 -1
- package/dist/index.d-BJyJx6U1.d.ts +319 -0
- package/dist/index.d.ts +1310 -5533
- package/dist/index.js +1203 -194
- package/dist/index.js.map +1 -1
- package/dist/{jsx-runtime-WKTl3EEw.d.ts → jsx-runtime-CBZQYYDS.d.ts} +2 -47
- package/dist/jsx-runtime.d.ts +2 -2
- package/dist/jsx-runtime.js +3 -3
- package/dist/{testing-D3dCOkTm.d.ts → testing-BM481Hdz.d.ts} +217 -986
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +19 -5
- package/package.json +30 -30
- package/dist/chunk-DCI2T7WJ.js +0 -766
- package/dist/chunk-DCI2T7WJ.js.map +0 -1
- package/dist/chunk-SBZQ6L3Z.js.map +0 -1
- package/dist/chunk-WSGWYXAO.js.map +0 -1
- package/dist/index.d-DOlTeG-8.d.ts +0 -99
- /package/dist/{chunk-3DETYZMZ.js.map → chunk-MFTZQMZM.js.map} +0 -0
|
@@ -8,8 +8,9 @@ import {
|
|
|
8
8
|
Str,
|
|
9
9
|
currentContainer,
|
|
10
10
|
facade,
|
|
11
|
+
fileType_exports,
|
|
11
12
|
inject
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-POEP4B2M.js";
|
|
13
14
|
import {
|
|
14
15
|
Application,
|
|
15
16
|
AuthFactory,
|
|
@@ -32,6 +33,7 @@ import {
|
|
|
32
33
|
MailManager,
|
|
33
34
|
MiddlewareCallbacks,
|
|
34
35
|
NotificationDispatcher,
|
|
36
|
+
PaginationState,
|
|
35
37
|
PipelineFactory,
|
|
36
38
|
QueueManager,
|
|
37
39
|
Redirector,
|
|
@@ -43,7 +45,7 @@ import {
|
|
|
43
45
|
ValidationFactory,
|
|
44
46
|
ViewFactory,
|
|
45
47
|
token
|
|
46
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-I63ZFMX6.js";
|
|
47
49
|
|
|
48
50
|
// ../Config/dist/index.js
|
|
49
51
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -490,7 +492,7 @@ var Application2 = class _Application extends Container {
|
|
|
490
492
|
this.scoped(Vite, () => new Vite(this));
|
|
491
493
|
}
|
|
492
494
|
base;
|
|
493
|
-
version = "0.1.0-alpha.
|
|
495
|
+
version = "0.1.0-alpha.6";
|
|
494
496
|
providers = [];
|
|
495
497
|
bootingCallbacks = [];
|
|
496
498
|
bootedCallbacks = [];
|
|
@@ -885,20 +887,60 @@ function mimeFromExtension(pathOrExtension) {
|
|
|
885
887
|
function extensionFromMime(type) {
|
|
886
888
|
return EXTENSIONS[type.split(";")[0].trim().toLowerCase()];
|
|
887
889
|
}
|
|
888
|
-
var
|
|
889
|
-
|
|
890
|
+
var NEVER_CLAIMED = /* @__PURE__ */ new Set([
|
|
891
|
+
...fileType_exports.BINARY_AND_MARKUP_TYPES.flatMap(({ extensions }) => extensions),
|
|
892
|
+
"jpe",
|
|
893
|
+
"svgz",
|
|
894
|
+
"xhtml",
|
|
895
|
+
"xht",
|
|
896
|
+
"shtml",
|
|
897
|
+
"xsl",
|
|
898
|
+
"xslt",
|
|
899
|
+
"mht",
|
|
900
|
+
"mhtml",
|
|
901
|
+
"js",
|
|
902
|
+
"mjs",
|
|
903
|
+
"rss",
|
|
904
|
+
"atom",
|
|
905
|
+
"rdf",
|
|
906
|
+
"mathml",
|
|
907
|
+
"mml",
|
|
908
|
+
"xul"
|
|
909
|
+
]);
|
|
910
|
+
var PLAIN_EXTENSION = /^[a-z0-9]+$/;
|
|
911
|
+
var FormUploadedFile = class _FormUploadedFile {
|
|
912
|
+
constructor(file, detected) {
|
|
890
913
|
this.file = file;
|
|
914
|
+
this.detected = detected;
|
|
891
915
|
this.clientName = file.name ?? "";
|
|
892
916
|
this.mimeType = file.type === "" ? "application/octet-stream" : file.type;
|
|
893
917
|
this.size = file.size;
|
|
894
918
|
}
|
|
895
919
|
file;
|
|
920
|
+
detected;
|
|
896
921
|
clientName;
|
|
897
922
|
mimeType;
|
|
898
923
|
size;
|
|
899
|
-
/**
|
|
924
|
+
/**
|
|
925
|
+
* Reads the leading bytes first, so extension(), hashName() and store() name the file by its content. The request
|
|
926
|
+
* builds its uploads this way.
|
|
927
|
+
*/
|
|
928
|
+
static async fromFile(file) {
|
|
929
|
+
return new _FormUploadedFile(file, fileType_exports.detectFileType(new Uint8Array(await file.slice(0, fileType_exports.SNIFF_BYTES).arrayBuffer())));
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Laravel's extension(): the type the bytes show (guessExtension()), when they were read and match a known format.
|
|
933
|
+
* Text and bytes no format matches keep the client's claim (its MIME type, else its file name) when it is a plain
|
|
934
|
+
* extension, unless it names a format the bytes would have shown or one a browser renders or runs: otherwise 'txt'
|
|
935
|
+
* for text, '' for the rest. Unread (built directly), the client's claim when it is a plain extension, else ''.
|
|
936
|
+
*/
|
|
900
937
|
extension() {
|
|
901
|
-
|
|
938
|
+
const claimed = extensionFromMime(this.mimeType) ?? this.clientExtension();
|
|
939
|
+
const detected = this.detected;
|
|
940
|
+
if (detected === void 0) return PLAIN_EXTENSION.test(claimed) ? claimed : "";
|
|
941
|
+
if (detected !== fileType_exports.TEXT && detected.extensions.length > 0) return detected.extensions[0];
|
|
942
|
+
if (PLAIN_EXTENSION.test(claimed) && !NEVER_CLAIMED.has(claimed)) return claimed;
|
|
943
|
+
return detected === fileType_exports.TEXT ? "txt" : "";
|
|
902
944
|
}
|
|
903
945
|
clientExtension() {
|
|
904
946
|
const dot = this.clientName.lastIndexOf(".");
|
|
@@ -1078,7 +1120,7 @@ var IncomingRequest = class {
|
|
|
1078
1120
|
this.#content = new TextDecoder().decode(bytes);
|
|
1079
1121
|
this.#body = fromEntries(new URLSearchParams(this.#content).entries());
|
|
1080
1122
|
} else {
|
|
1081
|
-
this.#parseForm(await new Response(bytes, { headers: { "content-type": this.raw.headers.get("content-type") ?? "" } }).formData());
|
|
1123
|
+
await this.#parseForm(await new Response(bytes, { headers: { "content-type": this.raw.headers.get("content-type") ?? "" } }).formData());
|
|
1082
1124
|
}
|
|
1083
1125
|
} catch {
|
|
1084
1126
|
}
|
|
@@ -1103,12 +1145,12 @@ var IncomingRequest = class {
|
|
|
1103
1145
|
if (Array.isArray(parsed)) return sanitize({ ...parsed });
|
|
1104
1146
|
return isPlainObject(parsed) ? sanitize(parsed) : {};
|
|
1105
1147
|
}
|
|
1106
|
-
#parseForm(form) {
|
|
1148
|
+
async #parseForm(form) {
|
|
1107
1149
|
const fields = [];
|
|
1108
1150
|
const files = [];
|
|
1109
1151
|
for (const [name, value] of form.entries()) {
|
|
1110
1152
|
if (typeof value === "string") fields.push([name, value]);
|
|
1111
|
-
else if (!((value.name ?? "") === "" && value.size === 0)) files.push([name,
|
|
1153
|
+
else if (!((value.name ?? "") === "" && value.size === 0)) files.push([name, await FormUploadedFile.fromFile(value)]);
|
|
1112
1154
|
}
|
|
1113
1155
|
this.#body = fromEntries(fields);
|
|
1114
1156
|
this.#files = fromEntries(files);
|
|
@@ -1248,9 +1290,14 @@ var IncomingRequest = class {
|
|
|
1248
1290
|
filled(key) {
|
|
1249
1291
|
return !isBlank(Arr.get(this.all(), key));
|
|
1250
1292
|
}
|
|
1293
|
+
/** Laravel's string(): the input as a Stringable (the default when it is missing or null): request().string('q').trim(). */
|
|
1251
1294
|
string(key, defaultValue = "") {
|
|
1252
1295
|
const value = this.input(key);
|
|
1253
|
-
return value === void 0 || value === null ? defaultValue :
|
|
1296
|
+
return Str.of(value === void 0 || value === null ? defaultValue : value);
|
|
1297
|
+
}
|
|
1298
|
+
/** Laravel's str(): string() under its short name. */
|
|
1299
|
+
str(key, defaultValue = "") {
|
|
1300
|
+
return this.string(key, defaultValue);
|
|
1254
1301
|
}
|
|
1255
1302
|
/** PHP's FILTER_VALIDATE_BOOLEAN: '1', 'true', 'on', 'yes' (any case) and true are true. */
|
|
1256
1303
|
boolean(key, defaultValue = false) {
|
|
@@ -2458,15 +2505,58 @@ var HttpServiceProvider = class extends ServiceProvider {
|
|
|
2458
2505
|
// ../Live/dist/index.js
|
|
2459
2506
|
import { AssertionError } from "assert";
|
|
2460
2507
|
import { isDeepStrictEqual } from "util";
|
|
2461
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
2508
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
2462
2509
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
2463
2510
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
2464
2511
|
import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
|
|
2465
2512
|
import { join as join5, relative as relative2, sep as sep2 } from "path";
|
|
2466
2513
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2514
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
2467
2515
|
import { existsSync as existsSync22 } from "fs";
|
|
2468
2516
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
2469
2517
|
import { dirname as dirname2, join as join23, relative as relative22, sep as sep22 } from "path";
|
|
2518
|
+
var LiveError = class extends Error {
|
|
2519
|
+
name = "LiveError";
|
|
2520
|
+
kind = "Server";
|
|
2521
|
+
status = 500;
|
|
2522
|
+
};
|
|
2523
|
+
var CorruptComponentPayloadError = class extends LiveError {
|
|
2524
|
+
name = "CorruptComponentPayloadError";
|
|
2525
|
+
kind = "Authorization";
|
|
2526
|
+
status = 403;
|
|
2527
|
+
constructor(component = "unknown") {
|
|
2528
|
+
super(`Live encountered corrupt data when trying to hydrate the [${component}] component.`);
|
|
2529
|
+
}
|
|
2530
|
+
};
|
|
2531
|
+
var MethodNotCallableError = class extends LiveError {
|
|
2532
|
+
name = "MethodNotCallableError";
|
|
2533
|
+
kind = "Authorization";
|
|
2534
|
+
status = 403;
|
|
2535
|
+
constructor(method, component) {
|
|
2536
|
+
super(`Unable to call component method. Public method [${method}] not found on component [${component}].`);
|
|
2537
|
+
}
|
|
2538
|
+
};
|
|
2539
|
+
var PropertyNotUpdatableError = class extends LiveError {
|
|
2540
|
+
name = "PropertyNotUpdatableError";
|
|
2541
|
+
kind = "Authorization";
|
|
2542
|
+
status = 403;
|
|
2543
|
+
constructor(path, component) {
|
|
2544
|
+
super(`Cannot update property [${path}] on component [${component}]: it is not a public, unlocked property.`);
|
|
2545
|
+
}
|
|
2546
|
+
};
|
|
2547
|
+
var LiveComponentNotFoundError = class extends LiveError {
|
|
2548
|
+
name = "LiveComponentNotFoundError";
|
|
2549
|
+
kind = "Http";
|
|
2550
|
+
status = 404;
|
|
2551
|
+
constructor(component) {
|
|
2552
|
+
super(`Unable to find component: [${component}].`);
|
|
2553
|
+
}
|
|
2554
|
+
};
|
|
2555
|
+
var InvalidLiveRequestError = class extends LiveError {
|
|
2556
|
+
name = "InvalidLiveRequestError";
|
|
2557
|
+
kind = "Http";
|
|
2558
|
+
status = 400;
|
|
2559
|
+
};
|
|
2470
2560
|
Symbol.metadata ??= /* @__PURE__ */ Symbol.for("Symbol.metadata");
|
|
2471
2561
|
var LISTENERS = /* @__PURE__ */ Symbol.for("oriel:live.listeners");
|
|
2472
2562
|
var LOCKED = /* @__PURE__ */ Symbol.for("oriel:live.locked");
|
|
@@ -2538,6 +2628,145 @@ function lockedOf(cls) {
|
|
|
2538
2628
|
function computedOf(cls) {
|
|
2539
2629
|
return /* @__PURE__ */ new Set([...metadataOf(cls)[COMPUTED] ?? [], ...staticsOf(cls, "computed").flat()]);
|
|
2540
2630
|
}
|
|
2631
|
+
function isModel(value) {
|
|
2632
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2633
|
+
const candidate = value;
|
|
2634
|
+
return typeof candidate.getKey === "function" && typeof candidate.getKeyName === "function" && typeof candidate.getTable === "function" && typeof candidate.exists === "boolean";
|
|
2635
|
+
}
|
|
2636
|
+
var isPlainObject2 = (value) => {
|
|
2637
|
+
const proto = Object.getPrototypeOf(value);
|
|
2638
|
+
return proto === Object.prototype || proto === null;
|
|
2639
|
+
};
|
|
2640
|
+
var isTuple = (value) => Array.isArray(value) && value.length === 2 && typeof value[1] === "object" && value[1] !== null && !Array.isArray(value[1]) && typeof value[1].s === "string";
|
|
2641
|
+
function classNameOf(registry, cls) {
|
|
2642
|
+
return registry.nameOf(cls) ?? registry.register(cls);
|
|
2643
|
+
}
|
|
2644
|
+
function dehydrate(value, registry, path, seen = /* @__PURE__ */ new Set()) {
|
|
2645
|
+
switch (typeof value) {
|
|
2646
|
+
case "undefined":
|
|
2647
|
+
return [null, { s: "u" }];
|
|
2648
|
+
case "string":
|
|
2649
|
+
case "boolean":
|
|
2650
|
+
return value;
|
|
2651
|
+
case "number":
|
|
2652
|
+
if (!Number.isFinite(value)) throw new LiveError(`Property [${path}] is ${value}: Live state must be JSON (finite numbers only).`);
|
|
2653
|
+
return value;
|
|
2654
|
+
case "bigint":
|
|
2655
|
+
case "symbol":
|
|
2656
|
+
case "function":
|
|
2657
|
+
throw new LiveError(`Property [${path}] holds a ${typeof value}: Live state may only hold primitives, arrays, plain objects, Dates, models and collections.`);
|
|
2658
|
+
}
|
|
2659
|
+
if (value === null) return null;
|
|
2660
|
+
const object = value;
|
|
2661
|
+
if (seen.has(object)) throw new LiveError(`Property [${path}] contains a circular reference.`);
|
|
2662
|
+
seen.add(object);
|
|
2663
|
+
try {
|
|
2664
|
+
if (object instanceof Date) return [object.toISOString(), { s: "dt" }];
|
|
2665
|
+
if (Array.isArray(object)) return [object.map((item, i) => dehydrate(item, registry, `${path}.${i}`, seen)), { s: "arr" }];
|
|
2666
|
+
if (isModel(object)) {
|
|
2667
|
+
const name = classNameOf(registry, object.constructor);
|
|
2668
|
+
if (!object.exists) throw new LiveError(`Property [${path}] holds an unsaved [${name}] model: only saved models can be kept between requests.`);
|
|
2669
|
+
return [null, { s: "mdl", class: name, key: object.getKey() }];
|
|
2670
|
+
}
|
|
2671
|
+
if (object instanceof Collection) {
|
|
2672
|
+
const items = object.all();
|
|
2673
|
+
const first = items[0];
|
|
2674
|
+
if (items.length > 0 && isModel(first) && items.every((item) => isModel(item) && item.constructor === first.constructor)) {
|
|
2675
|
+
for (const [i, item] of items.entries()) {
|
|
2676
|
+
if (!item.exists) throw new LiveError(`Property [${path}.${i}] holds an unsaved model.`);
|
|
2677
|
+
}
|
|
2678
|
+
const name = classNameOf(registry, first.constructor);
|
|
2679
|
+
return [null, { s: "elcln", class: name, keys: items.map((item) => item.getKey()) }];
|
|
2680
|
+
}
|
|
2681
|
+
return [items.map((item, i) => dehydrate(item, registry, `${path}.${i}`, seen)), { s: "clctn" }];
|
|
2682
|
+
}
|
|
2683
|
+
if (isPlainObject2(object)) {
|
|
2684
|
+
const out = {};
|
|
2685
|
+
for (const [key, item] of Object.entries(object)) out[key] = dehydrate(item, registry, `${path}.${key}`, seen);
|
|
2686
|
+
return [out, { s: "obj" }];
|
|
2687
|
+
}
|
|
2688
|
+
const type = object.constructor?.name || "object";
|
|
2689
|
+
throw new LiveError(
|
|
2690
|
+
`Property [${path}] holds a [${type}] instance: Live state may only hold primitives, arrays, plain objects, Dates, models and collections.`
|
|
2691
|
+
);
|
|
2692
|
+
} finally {
|
|
2693
|
+
seen.delete(object);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
function modelClass(registry, name) {
|
|
2697
|
+
if (typeof name !== "string" || !registry.has(name)) throw new LiveError(`Unable to hydrate the [${String(name)}] model: it is not registered.`);
|
|
2698
|
+
return registry.resolve(name);
|
|
2699
|
+
}
|
|
2700
|
+
async function hydrate(value, registry) {
|
|
2701
|
+
if (!isTuple(value)) return value;
|
|
2702
|
+
const [inner, meta] = value;
|
|
2703
|
+
switch (meta.s) {
|
|
2704
|
+
case "u":
|
|
2705
|
+
return void 0;
|
|
2706
|
+
case "dt":
|
|
2707
|
+
return new Date(String(inner));
|
|
2708
|
+
case "arr":
|
|
2709
|
+
return Promise.all(inner.map((item) => hydrate(item, registry)));
|
|
2710
|
+
case "clctn":
|
|
2711
|
+
return new Collection(await Promise.all(inner.map((item) => hydrate(item, registry))));
|
|
2712
|
+
case "obj": {
|
|
2713
|
+
const out = {};
|
|
2714
|
+
for (const [key, item] of Object.entries(inner)) {
|
|
2715
|
+
Object.defineProperty(out, key, { value: await hydrate(item, registry), enumerable: true, writable: true, configurable: true });
|
|
2716
|
+
}
|
|
2717
|
+
return out;
|
|
2718
|
+
}
|
|
2719
|
+
case "mdl":
|
|
2720
|
+
return modelClass(registry, meta.class).findOrFail(meta.key);
|
|
2721
|
+
case "elcln": {
|
|
2722
|
+
const cls = modelClass(registry, meta.class);
|
|
2723
|
+
const keys = meta.keys ?? [];
|
|
2724
|
+
const keyName = new cls().getKeyName();
|
|
2725
|
+
const found = [...await cls.query().whereIn(keyName, keys).get()];
|
|
2726
|
+
const byKey = new Map(found.map((model) => [String(model.getKey()), model]));
|
|
2727
|
+
return new Collection(keys.map((key) => byKey.get(String(key))).filter((model) => model !== void 0));
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
throw new LiveError(`Unknown snapshot value type [${String(meta.s)}].`);
|
|
2731
|
+
}
|
|
2732
|
+
var HOOKS = /* @__PURE__ */ new Set(["constructor", "render", "mount", "boot", "booted", "hydrate", "dehydrate", "updating", "updated", "rendering", "rendered", "exception"]);
|
|
2733
|
+
var HOOK_PREFIX = /^(updating|updated|hydrate|dehydrate)[A-Z_]/;
|
|
2734
|
+
var UNSAFE_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
2735
|
+
var baseApi;
|
|
2736
|
+
var isBaseApi = (name) => (baseApi ??= new Set(Object.getOwnPropertyNames(Component.prototype))).has(name);
|
|
2737
|
+
var INDEX = /^(?:0|[1-9]\d*)$/;
|
|
2738
|
+
function isArraySlot(array, segment) {
|
|
2739
|
+
return INDEX.test(segment) && Number(segment) <= array.length;
|
|
2740
|
+
}
|
|
2741
|
+
function isCallable(instance, method) {
|
|
2742
|
+
if (method === "" || method.startsWith("_") || method.startsWith("$") || method.startsWith("#")) return false;
|
|
2743
|
+
if (HOOKS.has(method) || HOOK_PREFIX.test(method) || isBaseApi(method)) return false;
|
|
2744
|
+
if (lockedOf(instance.constructor).has(method)) return false;
|
|
2745
|
+
const isBase = (proto) => proto === Component.prototype || proto === Object.prototype;
|
|
2746
|
+
for (let proto = Object.getPrototypeOf(instance); proto !== null && !isBase(proto); proto = Object.getPrototypeOf(proto)) {
|
|
2747
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, method);
|
|
2748
|
+
if (descriptor !== void 0) return typeof descriptor.value === "function";
|
|
2749
|
+
}
|
|
2750
|
+
return false;
|
|
2751
|
+
}
|
|
2752
|
+
function isUpdatable(instance, path) {
|
|
2753
|
+
const segments = path.split(".");
|
|
2754
|
+
const root = segments[0];
|
|
2755
|
+
if (root === "" || root.startsWith("_") || segments.some((segment) => segment === "" || UNSAFE_SEGMENTS.has(segment))) return false;
|
|
2756
|
+
if (!Object.hasOwn(instance, root)) return false;
|
|
2757
|
+
const descriptor = Object.getOwnPropertyDescriptor(instance, root);
|
|
2758
|
+
if (!descriptor.enumerable || descriptor.get !== void 0) return false;
|
|
2759
|
+
const cls = instance.constructor;
|
|
2760
|
+
if (lockedOf(cls).has(root) || computedOf(cls).has(root)) return false;
|
|
2761
|
+
let node = descriptor.value;
|
|
2762
|
+
for (const segment of segments.slice(1)) {
|
|
2763
|
+
if (isModel(node) || node instanceof Collection) return false;
|
|
2764
|
+
if (Array.isArray(node) && !isArraySlot(node, segment)) return false;
|
|
2765
|
+
if (typeof node !== "object" || node === null || !Object.hasOwn(node, segment)) return true;
|
|
2766
|
+
node = node[segment];
|
|
2767
|
+
}
|
|
2768
|
+
return !isModel(node) && !(node instanceof Collection);
|
|
2769
|
+
}
|
|
2541
2770
|
var COMPONENT = /* @__PURE__ */ Symbol.for("oriel:live.component");
|
|
2542
2771
|
var states = /* @__PURE__ */ new WeakMap();
|
|
2543
2772
|
function stateOf(component) {
|
|
@@ -2668,57 +2897,49 @@ var Component = class {
|
|
|
2668
2897
|
skipRender() {
|
|
2669
2898
|
stateOf(this).skipRender = true;
|
|
2670
2899
|
}
|
|
2900
|
+
/**
|
|
2901
|
+
* An action for an `o:` directive, typed against its method: `o:click={this.$action(this.delete, tag.id)}` writes
|
|
2902
|
+
* `delete(5)`, and a string where `delete` takes a number is a type error. The arguments are written as JSON, the
|
|
2903
|
+
* trailing `undefined` ones left out so the method's defaults apply. The method must be one of this component's
|
|
2904
|
+
* actions (a public method of its class, not a hook and not @Locked); anything else throws while rendering.
|
|
2905
|
+
*/
|
|
2906
|
+
$action(method, ...params) {
|
|
2907
|
+
const name = typeof method === "function" ? method.name : "";
|
|
2908
|
+
if (name === "" || this[name] !== method || !isCallable(this, name)) {
|
|
2909
|
+
throw new LiveError(
|
|
2910
|
+
`[${name === "" ? "anonymous function" : name}] is not an action of [${this.getName() || this.constructor.name}]: pass one of its public methods, as in this.$action(this.save).`
|
|
2911
|
+
);
|
|
2912
|
+
}
|
|
2913
|
+
let count = params.length;
|
|
2914
|
+
while (count > 0 && params[count - 1] === void 0) count--;
|
|
2915
|
+
return count === 0 ? name : `${name}(${params.slice(0, count).map((param) => JSON.stringify(param) ?? "null").join(", ")})`;
|
|
2916
|
+
}
|
|
2671
2917
|
/** A full-page component as a route action: Route.get('/counter', Counter). Route parameters go to mount(). */
|
|
2672
2918
|
async __invoke() {
|
|
2673
2919
|
return inject(LiveManager).page(this);
|
|
2674
2920
|
}
|
|
2675
2921
|
};
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
};
|
|
2689
|
-
|
|
2690
|
-
name = "MethodNotCallableError";
|
|
2691
|
-
kind = "Authorization";
|
|
2692
|
-
status = 403;
|
|
2693
|
-
constructor(method, component) {
|
|
2694
|
-
super(`Unable to call component method. Public method [${method}] not found on component [${component}].`);
|
|
2695
|
-
}
|
|
2696
|
-
};
|
|
2697
|
-
var PropertyNotUpdatableError = class extends LiveError {
|
|
2698
|
-
name = "PropertyNotUpdatableError";
|
|
2699
|
-
kind = "Authorization";
|
|
2700
|
-
status = 403;
|
|
2701
|
-
constructor(path, component) {
|
|
2702
|
-
super(`Cannot update property [${path}] on component [${component}]: it is not a public, unlocked property.`);
|
|
2703
|
-
}
|
|
2704
|
-
};
|
|
2705
|
-
var LiveComponentNotFoundError = class extends LiveError {
|
|
2706
|
-
name = "LiveComponentNotFoundError";
|
|
2707
|
-
kind = "Http";
|
|
2708
|
-
status = 404;
|
|
2709
|
-
constructor(component) {
|
|
2710
|
-
super(`Unable to find component: [${component}].`);
|
|
2711
|
-
}
|
|
2712
|
-
};
|
|
2713
|
-
var InvalidLiveRequestError = class extends LiveError {
|
|
2714
|
-
name = "InvalidLiveRequestError";
|
|
2715
|
-
kind = "Http";
|
|
2716
|
-
status = 400;
|
|
2717
|
-
};
|
|
2922
|
+
function trackFlash(session, { saved = true } = {}) {
|
|
2923
|
+
if (session === void 0) return () => void 0;
|
|
2924
|
+
const before = new Set(session.get("_flash.new", []));
|
|
2925
|
+
return (redirected) => {
|
|
2926
|
+
if (redirected) return;
|
|
2927
|
+
const flashed = session.get("_flash.new", []);
|
|
2928
|
+
const fresh = flashed.filter((key) => !before.has(key));
|
|
2929
|
+
if (fresh.length === 0) return;
|
|
2930
|
+
session.put("_flash.new", flashed.filter((key) => before.has(key)));
|
|
2931
|
+
if (!saved) return session.forget(...fresh);
|
|
2932
|
+
const old = session.get("_flash.old", []);
|
|
2933
|
+
session.put("_flash.old", [...old, ...fresh.filter((key) => !old.includes(key))]);
|
|
2934
|
+
};
|
|
2935
|
+
}
|
|
2718
2936
|
var ESCAPES = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
2719
2937
|
function escapeAttribute2(value) {
|
|
2720
2938
|
return value.replace(/[&<>"']/g, (char) => ESCAPES[char]);
|
|
2721
2939
|
}
|
|
2940
|
+
function stripTags(html) {
|
|
2941
|
+
return html.replace(/<[^>]*>/g, "");
|
|
2942
|
+
}
|
|
2722
2943
|
var VOID = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "source", "track", "wbr"]);
|
|
2723
2944
|
var RAW_TEXT = /* @__PURE__ */ new Set(["script", "style", "textarea", "title"]);
|
|
2724
2945
|
var TOKEN = /<\/([A-Za-z][\w:-]*)\s*>|<([A-Za-z][\w:-]*)(?:\s+[^\s"'<>/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*\s*(\/?)>/g;
|
|
@@ -2773,6 +2994,15 @@ function requestScope2() {
|
|
|
2773
2994
|
const container = currentContainer() ?? Container.getInstance();
|
|
2774
2995
|
return container?.bound(TestRequestScope) === true ? container.make(TestRequestScope) : (run) => run();
|
|
2775
2996
|
}
|
|
2997
|
+
function currentSession() {
|
|
2998
|
+
const container = currentContainer();
|
|
2999
|
+
if (container?.bound(Session) !== true) return void 0;
|
|
3000
|
+
try {
|
|
3001
|
+
return container.make(Session);
|
|
3002
|
+
} catch {
|
|
3003
|
+
return void 0;
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
2776
3006
|
var Testable = class _Testable {
|
|
2777
3007
|
constructor(manager, scope, result) {
|
|
2778
3008
|
this.manager = manager;
|
|
@@ -2827,7 +3057,12 @@ var Testable = class _Testable {
|
|
|
2827
3057
|
return this.#update({}, [{ method: "$refresh", params: [] }]);
|
|
2828
3058
|
}
|
|
2829
3059
|
async #update(updates, calls) {
|
|
2830
|
-
const result = await this.scope(() =>
|
|
3060
|
+
const result = await this.scope(async () => {
|
|
3061
|
+
const flashed = trackFlash(currentSession(), { saved: false });
|
|
3062
|
+
const updated = await this.manager.updateComponent(this.#snapshot, updates, calls);
|
|
3063
|
+
flashed(updated.effects.redirect !== void 0);
|
|
3064
|
+
return updated;
|
|
3065
|
+
});
|
|
2831
3066
|
this.#snapshot = JSON.stringify(result.snapshot);
|
|
2832
3067
|
if (result.html !== void 0) this.#html = result.html;
|
|
2833
3068
|
this.#effects = result.effects;
|
|
@@ -2857,6 +3092,17 @@ var Testable = class _Testable {
|
|
|
2857
3092
|
for (const value of values) if (!this.html().includes(value)) fail(`Failed asserting that [${value}] is in the rendered HTML.`, this.html(), value);
|
|
2858
3093
|
return this;
|
|
2859
3094
|
}
|
|
3095
|
+
/** Each value, HTML-escaped, appears in the rendered text: the HTML with its tags stripped (Livewire's assertSeeText). */
|
|
3096
|
+
assertSeeText(...values) {
|
|
3097
|
+
const text = stripTags(this.html());
|
|
3098
|
+
for (const value of values) if (!text.includes(escapeAttribute2(value))) fail(`Failed asserting that [${value}] is in the rendered text.`, text, value);
|
|
3099
|
+
return this;
|
|
3100
|
+
}
|
|
3101
|
+
assertDontSeeText(...values) {
|
|
3102
|
+
const text = stripTags(this.html());
|
|
3103
|
+
for (const value of values) if (text.includes(escapeAttribute2(value))) fail(`Failed asserting that [${value}] is not in the rendered text.`, text, value);
|
|
3104
|
+
return this;
|
|
3105
|
+
}
|
|
2860
3106
|
assertDispatched(name, params) {
|
|
2861
3107
|
const dispatched = (this.#effects.dispatches ?? []).filter((event) => event.name === name);
|
|
2862
3108
|
if (dispatched.length === 0) fail(`Failed asserting that an event [${name}] was dispatched.`, this.#effects.dispatches ?? [], name);
|
|
@@ -2958,10 +3204,11 @@ async function discover(directory) {
|
|
|
2958
3204
|
}
|
|
2959
3205
|
return found;
|
|
2960
3206
|
}
|
|
2961
|
-
var PERSISTENT_MIDDLEWARE = ["auth", "verified", "guest"];
|
|
3207
|
+
var PERSISTENT_MIDDLEWARE = ["auth", "verified", "guest", "can"];
|
|
3208
|
+
var ROUTER_METHODS = ["getRoutesByMatchOrder", "getPatterns", "getPatternVersion", "getMiddleware", "getMiddlewareGroups", "substituteBindings"];
|
|
2962
3209
|
function canMatch(router) {
|
|
2963
3210
|
const methods = router;
|
|
2964
|
-
return
|
|
3211
|
+
return ROUTER_METHODS.every((name) => typeof methods[name] === "function");
|
|
2965
3212
|
}
|
|
2966
3213
|
function isMatchingRoute(route2) {
|
|
2967
3214
|
const candidate = route2;
|
|
@@ -2990,7 +3237,7 @@ function expand(references, aliases, groups, out = [], visiting = /* @__PURE__ *
|
|
|
2990
3237
|
}
|
|
2991
3238
|
return out;
|
|
2992
3239
|
}
|
|
2993
|
-
function
|
|
3240
|
+
function persistentPass(router, persistent, method, path) {
|
|
2994
3241
|
if (!canMatch(router)) throw new LiveError("Live re-applies the page route's middleware to updates, which needs Oriel's router.");
|
|
2995
3242
|
const routes = router.getRoutesByMatchOrder();
|
|
2996
3243
|
if (!Array.isArray(routes) || !routes.every(isMatchingRoute)) throw new LiveError("Live re-applies the page route's middleware to updates, which needs Oriel's routes.");
|
|
@@ -2998,177 +3245,139 @@ function persistentMiddleware(router, persistent, method, path) {
|
|
|
2998
3245
|
const target2 = routePath(path);
|
|
2999
3246
|
const patterns = router.getPatterns();
|
|
3000
3247
|
const version = router.getPatternVersion();
|
|
3001
|
-
|
|
3002
|
-
|
|
3248
|
+
let matched;
|
|
3249
|
+
for (const candidate of routes) {
|
|
3250
|
+
const values2 = candidate.methods.includes(verb) ? candidate.match(target2, patterns, version) : void 0;
|
|
3251
|
+
if (values2 !== void 0) {
|
|
3252
|
+
matched = { route: candidate, values: values2 };
|
|
3253
|
+
break;
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
if (matched === void 0) return void 0;
|
|
3257
|
+
const { route: route2, values } = matched;
|
|
3003
3258
|
const aliases = router.getMiddleware();
|
|
3004
3259
|
const groups = router.getMiddlewareGroups();
|
|
3005
3260
|
const wanted = new Set(persistent.flatMap((name) => Object.hasOwn(aliases, name) ? [aliases[name]] : []));
|
|
3006
3261
|
const excluded = new Set(expand(route2.excludedMiddleware(), aliases, groups).map((entry) => entry.cls));
|
|
3007
3262
|
const ids = /* @__PURE__ */ new Map();
|
|
3008
3263
|
const seen = /* @__PURE__ */ new Set();
|
|
3009
|
-
|
|
3264
|
+
const middleware = expand(route2.gatherMiddleware(), aliases, groups).filter((entry) => {
|
|
3010
3265
|
if (!ids.has(entry.cls)) ids.set(entry.cls, ids.size);
|
|
3011
3266
|
const key = `${ids.get(entry.cls)}:${entry.parameters.join(",")}`;
|
|
3012
3267
|
if (!wanted.has(entry.cls) || excluded.has(entry.cls) || seen.has(key)) return false;
|
|
3013
3268
|
seen.add(key);
|
|
3014
3269
|
return true;
|
|
3015
|
-
});
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
const
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
}
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
const
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
};
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
return value;
|
|
3055
|
-
case "bigint":
|
|
3056
|
-
case "symbol":
|
|
3057
|
-
case "function":
|
|
3058
|
-
throw new LiveError(`Property [${path}] holds a ${typeof value}: Live state may only hold primitives, arrays, plain objects, Dates, models and collections.`);
|
|
3059
|
-
}
|
|
3060
|
-
if (value === null) return null;
|
|
3061
|
-
const object = value;
|
|
3062
|
-
if (seen.has(object)) throw new LiveError(`Property [${path}] contains a circular reference.`);
|
|
3063
|
-
seen.add(object);
|
|
3064
|
-
try {
|
|
3065
|
-
if (object instanceof Date) return [object.toISOString(), { s: "dt" }];
|
|
3066
|
-
if (Array.isArray(object)) return [object.map((item, i) => dehydrate(item, registry, `${path}.${i}`, seen)), { s: "arr" }];
|
|
3067
|
-
if (isModel(object)) {
|
|
3068
|
-
const name = classNameOf(registry, object.constructor);
|
|
3069
|
-
if (!object.exists) throw new LiveError(`Property [${path}] holds an unsaved [${name}] model: only saved models can be kept between requests.`);
|
|
3070
|
-
return [null, { s: "mdl", class: name, key: object.getKey() }];
|
|
3071
|
-
}
|
|
3072
|
-
if (object instanceof Collection) {
|
|
3073
|
-
const items = object.all();
|
|
3074
|
-
const first = items[0];
|
|
3075
|
-
if (items.length > 0 && isModel(first) && items.every((item) => isModel(item) && item.constructor === first.constructor)) {
|
|
3076
|
-
for (const [i, item] of items.entries()) {
|
|
3077
|
-
if (!item.exists) throw new LiveError(`Property [${path}.${i}] holds an unsaved model.`);
|
|
3078
|
-
}
|
|
3079
|
-
const name = classNameOf(registry, first.constructor);
|
|
3080
|
-
return [null, { s: "elcln", class: name, keys: items.map((item) => item.getKey()) }];
|
|
3081
|
-
}
|
|
3082
|
-
return [items.map((item, i) => dehydrate(item, registry, `${path}.${i}`, seen)), { s: "clctn" }];
|
|
3083
|
-
}
|
|
3084
|
-
if (isPlainObject2(object)) {
|
|
3085
|
-
const out = {};
|
|
3086
|
-
for (const [key, item] of Object.entries(object)) out[key] = dehydrate(item, registry, `${path}.${key}`, seen);
|
|
3087
|
-
return [out, { s: "obj" }];
|
|
3270
|
+
});
|
|
3271
|
+
const early = new Set(expand(["web", "api"], aliases, groups).map((entry) => entry.cls));
|
|
3272
|
+
for (const name of ["auth", "guest"]) if (Object.hasOwn(aliases, name)) early.add(aliases[name]);
|
|
3273
|
+
const before = middleware.filter((entry) => early.has(entry.cls));
|
|
3274
|
+
const after = middleware.filter((entry) => !early.has(entry.cls));
|
|
3275
|
+
return { router, route: route2, values, middleware: [...before, ...after], at: before.length };
|
|
3276
|
+
}
|
|
3277
|
+
function persistentMiddleware(router, persistent, method, path) {
|
|
3278
|
+
return [...persistentPass(router, persistent, method, path)?.middleware ?? []];
|
|
3279
|
+
}
|
|
3280
|
+
function currentRoute(container) {
|
|
3281
|
+
if (!container.bound(CurrentRoute)) return void 0;
|
|
3282
|
+
try {
|
|
3283
|
+
return container.make(CurrentRoute);
|
|
3284
|
+
} catch {
|
|
3285
|
+
return void 0;
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
async function throughMiddleware(container, request2, pass, destination) {
|
|
3289
|
+
const original2 = currentRoute(container);
|
|
3290
|
+
const page = { route: pass.route, parameters: { ...pass.values }, middleware: [] };
|
|
3291
|
+
let swapped = false;
|
|
3292
|
+
const show = () => {
|
|
3293
|
+
container.instance(CurrentRoute, page);
|
|
3294
|
+
swapped = true;
|
|
3295
|
+
};
|
|
3296
|
+
const restore = () => {
|
|
3297
|
+
if (!swapped) return;
|
|
3298
|
+
if (original2 === void 0) container.forgetInstance(CurrentRoute);
|
|
3299
|
+
else container.instance(CurrentRoute, original2);
|
|
3300
|
+
swapped = false;
|
|
3301
|
+
};
|
|
3302
|
+
let ran;
|
|
3303
|
+
const run = async (index) => {
|
|
3304
|
+
if (index === pass.at) Object.assign(page.parameters, await pass.router.substituteBindings(pass.route, pass.values, container));
|
|
3305
|
+
if (index === pass.middleware.length) {
|
|
3306
|
+
restore();
|
|
3307
|
+
ran = { result: await destination() };
|
|
3308
|
+
return new Response(null, { status: 204 });
|
|
3088
3309
|
}
|
|
3089
|
-
const
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
);
|
|
3310
|
+
const { cls, parameters } = pass.middleware[index];
|
|
3311
|
+
const instance = container.make(cls);
|
|
3312
|
+
const next = () => run(index + 1);
|
|
3313
|
+
return instance.handle(request2, next, ...parameters);
|
|
3314
|
+
};
|
|
3315
|
+
show();
|
|
3316
|
+
try {
|
|
3317
|
+
const response2 = await run(0);
|
|
3318
|
+
return ran ? ran.result : response2;
|
|
3093
3319
|
} finally {
|
|
3094
|
-
|
|
3320
|
+
restore();
|
|
3095
3321
|
}
|
|
3096
3322
|
}
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3323
|
+
var PAGINATED = /* @__PURE__ */ Symbol.for("oriel:live.pagination");
|
|
3324
|
+
function toPage(value) {
|
|
3325
|
+
const page = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : Number.NaN;
|
|
3326
|
+
return Number.isFinite(page) && page >= 1 ? Math.min(Math.floor(page), Number.MAX_SAFE_INTEGER) : 1;
|
|
3100
3327
|
}
|
|
3101
|
-
async function
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
return Promise.all(inner.map((item) => hydrate(item, registry)));
|
|
3111
|
-
case "clctn":
|
|
3112
|
-
return new Collection(await Promise.all(inner.map((item) => hydrate(item, registry))));
|
|
3113
|
-
case "obj": {
|
|
3114
|
-
const out = {};
|
|
3115
|
-
for (const [key, item] of Object.entries(inner)) {
|
|
3116
|
-
Object.defineProperty(out, key, { value: await hydrate(item, registry), enumerable: true, writable: true, configurable: true });
|
|
3117
|
-
}
|
|
3118
|
-
return out;
|
|
3328
|
+
async function hook(instance, name, ...args) {
|
|
3329
|
+
const method = instance[name];
|
|
3330
|
+
if (typeof method === "function") await method.apply(instance, args);
|
|
3331
|
+
}
|
|
3332
|
+
function WithPagination(Base) {
|
|
3333
|
+
class WithPaginationMixin extends Base {
|
|
3334
|
+
page = 1;
|
|
3335
|
+
nextPage() {
|
|
3336
|
+
return this.setPage(toPage(this.page) + 1);
|
|
3119
3337
|
}
|
|
3120
|
-
|
|
3121
|
-
return
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3338
|
+
previousPage() {
|
|
3339
|
+
return this.setPage(Math.max(toPage(this.page) - 1, 1));
|
|
3340
|
+
}
|
|
3341
|
+
gotoPage(page) {
|
|
3342
|
+
return this.setPage(page);
|
|
3343
|
+
}
|
|
3344
|
+
resetPage() {
|
|
3345
|
+
return this.setPage(1);
|
|
3346
|
+
}
|
|
3347
|
+
async setPage(page) {
|
|
3348
|
+
const next = toPage(page);
|
|
3349
|
+
await hook(this, "updatingPage", next);
|
|
3350
|
+
this.page = next;
|
|
3351
|
+
await hook(this, "updatedPage", next);
|
|
3129
3352
|
}
|
|
3130
3353
|
}
|
|
3131
|
-
|
|
3354
|
+
Object.defineProperty(WithPaginationMixin.prototype, PAGINATED, { value: true });
|
|
3355
|
+
return WithPaginationMixin;
|
|
3132
3356
|
}
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
var UNSAFE_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
3136
|
-
var BASE_API = new Set(Object.getOwnPropertyNames(Component.prototype));
|
|
3137
|
-
var INDEX = /^(?:0|[1-9]\d*)$/;
|
|
3138
|
-
function isArraySlot(array, segment) {
|
|
3139
|
-
return INDEX.test(segment) && Number(segment) <= array.length;
|
|
3357
|
+
function isPaginated(component) {
|
|
3358
|
+
return component[PAGINATED] === true;
|
|
3140
3359
|
}
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
if (lockedOf(instance.constructor).has(method)) return false;
|
|
3145
|
-
const isBase = (proto) => proto === Component.prototype || proto === Object.prototype;
|
|
3146
|
-
for (let proto = Object.getPrototypeOf(instance); proto !== null && !isBase(proto); proto = Object.getPrototypeOf(proto)) {
|
|
3147
|
-
const descriptor = Object.getOwnPropertyDescriptor(proto, method);
|
|
3148
|
-
if (descriptor !== void 0) return typeof descriptor.value === "function";
|
|
3149
|
-
}
|
|
3150
|
-
return false;
|
|
3360
|
+
var rendering = new AsyncLocalStorage();
|
|
3361
|
+
function renderPaginated(component, url2, run) {
|
|
3362
|
+
return rendering.run(isPaginated(component) ? { component, url: url2 } : void 0, run);
|
|
3151
3363
|
}
|
|
3152
|
-
function
|
|
3153
|
-
|
|
3154
|
-
const root = segments[0];
|
|
3155
|
-
if (root === "" || root.startsWith("_") || segments.some((segment) => segment === "" || UNSAFE_SEGMENTS.has(segment))) return false;
|
|
3156
|
-
if (!Object.hasOwn(instance, root)) return false;
|
|
3157
|
-
const descriptor = Object.getOwnPropertyDescriptor(instance, root);
|
|
3158
|
-
if (!descriptor.enumerable || descriptor.get !== void 0) return false;
|
|
3159
|
-
const cls = instance.constructor;
|
|
3160
|
-
if (lockedOf(cls).has(root) || computedOf(cls).has(root)) return false;
|
|
3161
|
-
let node = descriptor.value;
|
|
3162
|
-
for (const segment of segments.slice(1)) {
|
|
3163
|
-
if (isModel(node) || node instanceof Collection) return false;
|
|
3164
|
-
if (Array.isArray(node) && !isArraySlot(node, segment)) return false;
|
|
3165
|
-
if (typeof node !== "object" || node === null || !Object.hasOwn(node, segment)) return true;
|
|
3166
|
-
node = node[segment];
|
|
3167
|
-
}
|
|
3168
|
-
return !isModel(node) && !(node instanceof Collection);
|
|
3364
|
+
function outsidePagination(run) {
|
|
3365
|
+
return rendering.run(void 0, run);
|
|
3169
3366
|
}
|
|
3170
|
-
var
|
|
3171
|
-
|
|
3367
|
+
var livePaginationState = {
|
|
3368
|
+
currentPage(pageName) {
|
|
3369
|
+
const current = rendering.getStore();
|
|
3370
|
+
if (current === void 0 || pageName !== "page") return void 0;
|
|
3371
|
+
const page = toPage(current.component.page);
|
|
3372
|
+
current.component.page = page;
|
|
3373
|
+
return page;
|
|
3374
|
+
},
|
|
3375
|
+
currentPath() {
|
|
3376
|
+
return rendering.getStore()?.url;
|
|
3377
|
+
}
|
|
3378
|
+
};
|
|
3379
|
+
var renderContext = new AsyncLocalStorage2();
|
|
3380
|
+
var pageRequest = new AsyncLocalStorage2();
|
|
3172
3381
|
function optional(container, token2) {
|
|
3173
3382
|
if (!container.bound(token2)) return void 0;
|
|
3174
3383
|
try {
|
|
@@ -3181,7 +3390,7 @@ var isRecord = (value) => typeof value === "object" && value !== null && !Array.
|
|
|
3181
3390
|
function setOwn(object, key, value) {
|
|
3182
3391
|
Object.defineProperty(object, key, { value, enumerable: true, writable: true, configurable: true });
|
|
3183
3392
|
}
|
|
3184
|
-
async function
|
|
3393
|
+
async function hook2(instance, name, ...args) {
|
|
3185
3394
|
const method = instance[name];
|
|
3186
3395
|
if (typeof method === "function") await method.apply(instance, args);
|
|
3187
3396
|
}
|
|
@@ -3232,7 +3441,7 @@ var LiveComponentManager = class {
|
|
|
3232
3441
|
setOwn(context.children, childKey, existing);
|
|
3233
3442
|
return `<${existing[0]} o:id="${escapeAttribute2(existing[1])}"></${existing[0]}>`;
|
|
3234
3443
|
}
|
|
3235
|
-
const result = await this.mountComponent(component, params);
|
|
3444
|
+
const result = await outsidePagination(() => this.mountComponent(component, params));
|
|
3236
3445
|
setOwn(context.children, childKey, [result.tag, result.snapshot.memo.id]);
|
|
3237
3446
|
return result.html;
|
|
3238
3447
|
}
|
|
@@ -3244,14 +3453,14 @@ var LiveComponentManager = class {
|
|
|
3244
3453
|
const state = stateOf(instance);
|
|
3245
3454
|
state.id = randomBytes2(15).toString("base64url");
|
|
3246
3455
|
state.name = name;
|
|
3247
|
-
await
|
|
3456
|
+
await hook2(instance, "boot");
|
|
3248
3457
|
for (const [property, value] of Object.entries(params)) {
|
|
3249
3458
|
if (Object.hasOwn(instance, property)) instance[property] = value;
|
|
3250
3459
|
}
|
|
3251
3460
|
this.prepareComputed(instance);
|
|
3252
|
-
await hook(instance, "mount", params);
|
|
3253
3461
|
const request2 = optional(this.container(), HttpRequest);
|
|
3254
3462
|
const page = pageRequest.getStore() ?? { path: request2?.path() ?? "", method: request2?.method() ?? "GET" };
|
|
3463
|
+
await renderPaginated(instance, this.pageUrl(page.path), () => hook2(instance, "mount", params));
|
|
3255
3464
|
const memo = { id: state.id, name, path: page.path, method: page.method, children: {}, errors: {} };
|
|
3256
3465
|
const { html, tag } = await this.render(instance, memo, {});
|
|
3257
3466
|
const effects = this.effects(instance);
|
|
@@ -3279,22 +3488,24 @@ var LiveComponentManager = class {
|
|
|
3279
3488
|
state.id = snapshot.memo.id;
|
|
3280
3489
|
state.name = snapshot.memo.name;
|
|
3281
3490
|
state.errors = { ...snapshot.memo.errors };
|
|
3282
|
-
await
|
|
3491
|
+
await hook2(instance, "boot");
|
|
3283
3492
|
const registry = this.registry();
|
|
3284
3493
|
for (const [property, value] of Object.entries(snapshot.data)) {
|
|
3285
3494
|
Object.defineProperty(instance, property, { value: await hydrate(value, registry), enumerable: true, writable: true, configurable: true });
|
|
3286
3495
|
}
|
|
3287
3496
|
this.prepareComputed(instance);
|
|
3288
|
-
await hook(instance, "hydrate");
|
|
3289
3497
|
const returns = [];
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3498
|
+
await renderPaginated(instance, this.pageUrl(snapshot.memo.path), async () => {
|
|
3499
|
+
await hook2(instance, "hydrate");
|
|
3500
|
+
try {
|
|
3501
|
+
for (const [path, value] of Object.entries(updates)) await this.applyUpdate(instance, path, value);
|
|
3502
|
+
for (const call of calls) returns.push(await this.call(instance, call.method, call.params));
|
|
3503
|
+
} catch (error) {
|
|
3504
|
+
const errors = error.errors;
|
|
3505
|
+
if (error.kind !== "Validation" || !isRecord(errors)) throw error;
|
|
3506
|
+
state.errors = { ...errors };
|
|
3507
|
+
}
|
|
3508
|
+
});
|
|
3298
3509
|
const memo = { ...snapshot.memo, children: { ...snapshot.memo.children } };
|
|
3299
3510
|
const effects = this.effects(instance);
|
|
3300
3511
|
let html;
|
|
@@ -3309,25 +3520,30 @@ var LiveComponentManager = class {
|
|
|
3309
3520
|
return { instance, snapshot: await this.snapshot(instance, memo), effects, html, tag };
|
|
3310
3521
|
}
|
|
3311
3522
|
/**
|
|
3312
|
-
* The route action of POST /oriel/live/update. Each component's update runs behind the
|
|
3313
|
-
* that rendered its page (Livewire's
|
|
3314
|
-
*
|
|
3523
|
+
* The route action of POST /oriel/live/update. Each component's update runs behind the persistent middleware of the
|
|
3524
|
+
* route that rendered its page (Livewire's): `auth`, `verified`, `guest` and `can`, with the page route's parameters
|
|
3525
|
+
* bound again. A signed-out tab gets a 401, a revoked ability a 403 and a deleted model a 404, before the component
|
|
3526
|
+
* hydrates. When a middleware answers instead (a redirect), that is the response. A key first flashed during the
|
|
3527
|
+
* update shows in this response only, unless the update redirects.
|
|
3315
3528
|
*/
|
|
3316
3529
|
async handle(request2) {
|
|
3317
3530
|
if (!this.isLiveRequest(request2)) throw new InvalidLiveRequestError("Live update requests need the X-Oriel-Live header.");
|
|
3318
3531
|
const container = this.container();
|
|
3319
3532
|
const persistent = optional(container, ConfigRepository)?.get("live.persistent_middleware", PERSISTENT_MIDDLEWARE) ?? PERSISTENT_MIDDLEWARE;
|
|
3320
3533
|
const components = [];
|
|
3534
|
+
const flashed = trackFlash(optional(container, Session));
|
|
3321
3535
|
for (const component of this.payload(request2).components) {
|
|
3322
3536
|
const { memo } = this.verify(component.snapshot);
|
|
3323
|
-
const
|
|
3537
|
+
const pass = persistentPass(container.make(Router), persistent, memo.method, memo.path);
|
|
3538
|
+
const update = () => this.updateComponent(component.snapshot, component.updates, component.calls);
|
|
3324
3539
|
const result = await pageRequest.run(
|
|
3325
3540
|
{ path: memo.path, method: memo.method },
|
|
3326
|
-
() =>
|
|
3541
|
+
() => pass === void 0 || pass.middleware.length === 0 ? update() : throughMiddleware(container, request2, pass, update)
|
|
3327
3542
|
);
|
|
3328
3543
|
if (result instanceof Response) return result;
|
|
3329
3544
|
components.push({ snapshot: JSON.stringify(result.snapshot), effects: result.effects });
|
|
3330
3545
|
}
|
|
3546
|
+
flashed(components.some((component) => component.effects.redirect !== void 0));
|
|
3331
3547
|
return { components };
|
|
3332
3548
|
}
|
|
3333
3549
|
/** A full-page component (Route.get('/counter', Counter)): mounted with the route parameters, inside the layout. */
|
|
@@ -3402,8 +3618,8 @@ var LiveComponentManager = class {
|
|
|
3402
3618
|
const [root, ...rest] = path.split(".");
|
|
3403
3619
|
const key = rest.length > 0 ? rest.join(".") : void 0;
|
|
3404
3620
|
const studly = Str.studly(root);
|
|
3405
|
-
await
|
|
3406
|
-
await
|
|
3621
|
+
await hook2(instance, "updating", path, value);
|
|
3622
|
+
await hook2(instance, `updating${studly}`, value, key);
|
|
3407
3623
|
const target2 = instance;
|
|
3408
3624
|
if (rest.length === 0) target2[root] = value;
|
|
3409
3625
|
else {
|
|
@@ -3421,8 +3637,8 @@ var LiveComponentManager = class {
|
|
|
3421
3637
|
assertSlot(node, last);
|
|
3422
3638
|
Object.defineProperty(node, last, { value, enumerable: true, writable: true, configurable: true });
|
|
3423
3639
|
}
|
|
3424
|
-
await
|
|
3425
|
-
await
|
|
3640
|
+
await hook2(instance, "updated", path, value);
|
|
3641
|
+
await hook2(instance, `updated${studly}`, value, key);
|
|
3426
3642
|
}
|
|
3427
3643
|
/** Runs one call from the browser: a magic action, an event, or a public method. Returns its return value. */
|
|
3428
3644
|
async call(instance, method, params) {
|
|
@@ -3450,18 +3666,35 @@ var LiveComponentManager = class {
|
|
|
3450
3666
|
if (!isCallable(instance, method)) throw new MethodNotCallableError(method, name);
|
|
3451
3667
|
return instance[method](...params);
|
|
3452
3668
|
}
|
|
3453
|
-
/**
|
|
3669
|
+
/**
|
|
3670
|
+
* Renders the component (rendering/rendered hooks, nested children tracked) and checks for a single root. A
|
|
3671
|
+
* WithPagination component's page and page URL are what paginate() reads while it renders, as in its mount() and in
|
|
3672
|
+
* its update's hooks and actions.
|
|
3673
|
+
*/
|
|
3454
3674
|
async render(instance, memo, previous) {
|
|
3455
|
-
await
|
|
3675
|
+
await hook2(instance, "rendering");
|
|
3456
3676
|
const context = { previous, children: {}, index: 0 };
|
|
3457
|
-
const html = await renderContext.run(
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3677
|
+
const html = await renderContext.run(
|
|
3678
|
+
context,
|
|
3679
|
+
() => renderPaginated(instance, this.pageUrl(memo.path), async () => {
|
|
3680
|
+
const output = await instance.render();
|
|
3681
|
+
return typeof output === "string" ? output : this.container().make(ViewFactory).renderToString(output);
|
|
3682
|
+
})
|
|
3683
|
+
);
|
|
3684
|
+
await hook2(instance, "rendered", html);
|
|
3462
3685
|
memo.children = context.children;
|
|
3463
3686
|
return { html: html.trim(), tag: rootTag(html, memo.name) };
|
|
3464
3687
|
}
|
|
3688
|
+
/** The absolute URL of the page a memo path belongs to, on the current request's origin ('/' + path without one). */
|
|
3689
|
+
pageUrl(path) {
|
|
3690
|
+
const relative3 = `/${path.replace(/^\/+/, "")}`;
|
|
3691
|
+
const url2 = optional(this.container(), HttpRequest)?.url();
|
|
3692
|
+
try {
|
|
3693
|
+
return url2 === void 0 ? relative3 : new URL(relative3, url2).href;
|
|
3694
|
+
} catch {
|
|
3695
|
+
return relative3;
|
|
3696
|
+
}
|
|
3697
|
+
}
|
|
3465
3698
|
/** Dispatches, redirects: the effects every response carries. */
|
|
3466
3699
|
effects(instance) {
|
|
3467
3700
|
const state = stateOf(instance);
|
|
@@ -3480,7 +3713,7 @@ var LiveComponentManager = class {
|
|
|
3480
3713
|
}
|
|
3481
3714
|
/** dehydrate() hook, then the state as snapshot data, the error bag, and the checksum over both. */
|
|
3482
3715
|
async snapshot(instance, memo) {
|
|
3483
|
-
await
|
|
3716
|
+
await hook2(instance, "dehydrate");
|
|
3484
3717
|
const registry = this.registry();
|
|
3485
3718
|
const data = {};
|
|
3486
3719
|
for (const [property, value] of Object.entries(instance)) data[property] = dehydrate(value, registry, `${memo.name}.${property}`);
|
|
@@ -3553,6 +3786,7 @@ var LiveServiceProvider = class extends ServiceProvider {
|
|
|
3553
3786
|
this.mergeConfigFrom({ class_path: this.app.appPath("Live"), layout: "components.layouts.app", persistent_middleware: [...PERSISTENT_MIDDLEWARE] }, "live");
|
|
3554
3787
|
this.app.singleton(LiveComponentManager, () => new LiveComponentManager(this.app));
|
|
3555
3788
|
this.app.singleton(LiveManager, (container) => container.make(LiveComponentManager));
|
|
3789
|
+
this.app.singleton(PaginationState, () => livePaginationState);
|
|
3556
3790
|
}
|
|
3557
3791
|
async boot() {
|
|
3558
3792
|
const manager = this.app.make(LiveComponentManager);
|
|
@@ -3566,6 +3800,40 @@ var LiveServiceProvider = class extends ServiceProvider {
|
|
|
3566
3800
|
}
|
|
3567
3801
|
}
|
|
3568
3802
|
};
|
|
3803
|
+
function urlWindow(current, last, onEachSide) {
|
|
3804
|
+
const range = (from, to) => Array.from({ length: Math.max(to - from + 1, 0) }, (_, index) => from + index);
|
|
3805
|
+
if (last < onEachSide * 2 + 8) return range(1, last);
|
|
3806
|
+
const size = onEachSide + 4;
|
|
3807
|
+
if (current <= size) return [...range(1, size + onEachSide), "...", ...range(last - 1, last)];
|
|
3808
|
+
if (current > last - size) return [...range(1, 2), "...", ...range(last - (size + (onEachSide - 1)), last)];
|
|
3809
|
+
return [...range(1, 2), "...", ...range(current - onEachSide, current + onEachSide), "...", ...range(last - 1, last)];
|
|
3810
|
+
}
|
|
3811
|
+
function button(label, action, attributes = "") {
|
|
3812
|
+
const live2 = action === void 0 ? " disabled" : ` o:click="${escapeAttribute2(action)}"`;
|
|
3813
|
+
return `<button type="button" class="btn btn-secondary"${attributes}${live2}>${escapeAttribute2(label)}</button>`;
|
|
3814
|
+
}
|
|
3815
|
+
function Pagination({ paginator, class: className }) {
|
|
3816
|
+
if (typeof paginator.nextCursor === "function") {
|
|
3817
|
+
throw new LiveError("<Pagination> needs paginate() or simplePaginate(): a cursor paginator has no page numbers.");
|
|
3818
|
+
}
|
|
3819
|
+
const current = paginator.currentPage();
|
|
3820
|
+
const onFirst = paginator.onFirstPage();
|
|
3821
|
+
const hasMore = paginator.hasMorePages();
|
|
3822
|
+
if (onFirst && !hasMore) return { toHtml: () => "" };
|
|
3823
|
+
const parts = [button("\xAB Previous", onFirst ? void 0 : "previousPage")];
|
|
3824
|
+
if (typeof paginator.lastPage === "function") {
|
|
3825
|
+
const onEachSide = Number.isInteger(paginator.onEachSideCount) && paginator.onEachSideCount >= 0 ? paginator.onEachSideCount : 3;
|
|
3826
|
+
for (const page of urlWindow(current, paginator.lastPage(), onEachSide)) {
|
|
3827
|
+
if (page === "...") parts.push('<span aria-hidden="true">...</span>');
|
|
3828
|
+
else if (page === current) parts.push(`<button type="button" class="btn" aria-current="page" disabled>${page}</button>`);
|
|
3829
|
+
else parts.push(button(String(page), `gotoPage(${page})`, ` aria-label="Go to page ${page}"`));
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
parts.push(button("Next \xBB", hasMore ? "nextPage" : void 0));
|
|
3833
|
+
const classes = className === void 0 || className === "" ? "" : ` class="${escapeAttribute2(className)}"`;
|
|
3834
|
+
const html = `<nav aria-label="Pagination"${classes}>${parts.join("")}</nav>`;
|
|
3835
|
+
return { toHtml: () => html };
|
|
3836
|
+
}
|
|
3569
3837
|
|
|
3570
3838
|
// ../Testing/dist/index.js
|
|
3571
3839
|
import { AssertionError as AssertionError2 } from "assert";
|
|
@@ -3577,7 +3845,131 @@ import { randomUUID } from "crypto";
|
|
|
3577
3845
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3578
3846
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3579
3847
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
3848
|
+
import { posix } from "path";
|
|
3580
3849
|
import { deflateSync } from "zlib";
|
|
3850
|
+
var original;
|
|
3851
|
+
var testNow;
|
|
3852
|
+
var realDate = () => original ?? globalThis.Date;
|
|
3853
|
+
function install() {
|
|
3854
|
+
if (original !== void 0) return;
|
|
3855
|
+
const Real = original = globalThis.Date;
|
|
3856
|
+
const current = () => testNow ?? Real.now();
|
|
3857
|
+
const Travelling = new Proxy(Real, {
|
|
3858
|
+
construct: (target2, args, newTarget) => Reflect.construct(target2, args.length === 0 ? [current()] : args, newTarget === Travelling ? target2 : newTarget),
|
|
3859
|
+
apply: (target2) => new target2(current()).toString(),
|
|
3860
|
+
get: (target2, property) => property === "now" ? current : Reflect.get(target2, property, target2)
|
|
3861
|
+
});
|
|
3862
|
+
globalThis.Date = Travelling;
|
|
3863
|
+
}
|
|
3864
|
+
function setTestNow(date) {
|
|
3865
|
+
const time = new (realDate())(date).getTime();
|
|
3866
|
+
if (Number.isNaN(time)) throw new TypeError(`Invalid date: ${String(date)}`);
|
|
3867
|
+
install();
|
|
3868
|
+
testNow = time;
|
|
3869
|
+
return new (realDate())(time);
|
|
3870
|
+
}
|
|
3871
|
+
function thenTravelBack(callback) {
|
|
3872
|
+
let result;
|
|
3873
|
+
try {
|
|
3874
|
+
result = callback();
|
|
3875
|
+
} catch (error) {
|
|
3876
|
+
travelBack();
|
|
3877
|
+
throw error;
|
|
3878
|
+
}
|
|
3879
|
+
if (result instanceof Promise) return result.finally(travelBack);
|
|
3880
|
+
travelBack();
|
|
3881
|
+
return result;
|
|
3882
|
+
}
|
|
3883
|
+
function travelTo(date, callback) {
|
|
3884
|
+
const at = setTestNow(date);
|
|
3885
|
+
if (callback) return thenTravelBack(() => callback(at));
|
|
3886
|
+
}
|
|
3887
|
+
function travelBack() {
|
|
3888
|
+
if (original !== void 0) globalThis.Date = original;
|
|
3889
|
+
original = void 0;
|
|
3890
|
+
testNow = void 0;
|
|
3891
|
+
return /* @__PURE__ */ new Date();
|
|
3892
|
+
}
|
|
3893
|
+
function freezeTime(callback) {
|
|
3894
|
+
const at = new (realDate())(testNow ?? Date.now());
|
|
3895
|
+
if (callback) return travelTo(at, callback);
|
|
3896
|
+
travelTo(at);
|
|
3897
|
+
return at;
|
|
3898
|
+
}
|
|
3899
|
+
function freezeSecond(callback) {
|
|
3900
|
+
const at = new (realDate())(testNow ?? Date.now());
|
|
3901
|
+
at.setMilliseconds(0);
|
|
3902
|
+
if (callback) return travelTo(at, callback);
|
|
3903
|
+
travelTo(at);
|
|
3904
|
+
return at;
|
|
3905
|
+
}
|
|
3906
|
+
function travel(value) {
|
|
3907
|
+
return new Wormhole(value);
|
|
3908
|
+
}
|
|
3909
|
+
var Wormhole = class {
|
|
3910
|
+
constructor(value) {
|
|
3911
|
+
this.value = value;
|
|
3912
|
+
}
|
|
3913
|
+
value;
|
|
3914
|
+
millisecond(callback) {
|
|
3915
|
+
return this.milliseconds(callback);
|
|
3916
|
+
}
|
|
3917
|
+
milliseconds(callback) {
|
|
3918
|
+
return this.#move((date) => date.setTime(date.getTime() + this.value), callback);
|
|
3919
|
+
}
|
|
3920
|
+
second(callback) {
|
|
3921
|
+
return this.seconds(callback);
|
|
3922
|
+
}
|
|
3923
|
+
seconds(callback) {
|
|
3924
|
+
return this.#move((date) => date.setTime(date.getTime() + this.value * 1e3), callback);
|
|
3925
|
+
}
|
|
3926
|
+
minute(callback) {
|
|
3927
|
+
return this.minutes(callback);
|
|
3928
|
+
}
|
|
3929
|
+
minutes(callback) {
|
|
3930
|
+
return this.#move((date) => date.setTime(date.getTime() + this.value * 6e4), callback);
|
|
3931
|
+
}
|
|
3932
|
+
hour(callback) {
|
|
3933
|
+
return this.hours(callback);
|
|
3934
|
+
}
|
|
3935
|
+
hours(callback) {
|
|
3936
|
+
return this.#move((date) => date.setTime(date.getTime() + this.value * 36e5), callback);
|
|
3937
|
+
}
|
|
3938
|
+
day(callback) {
|
|
3939
|
+
return this.days(callback);
|
|
3940
|
+
}
|
|
3941
|
+
days(callback) {
|
|
3942
|
+
return this.#move((date) => date.setUTCDate(date.getUTCDate() + this.value), callback);
|
|
3943
|
+
}
|
|
3944
|
+
week(callback) {
|
|
3945
|
+
return this.weeks(callback);
|
|
3946
|
+
}
|
|
3947
|
+
weeks(callback) {
|
|
3948
|
+
return this.#move((date) => date.setUTCDate(date.getUTCDate() + this.value * 7), callback);
|
|
3949
|
+
}
|
|
3950
|
+
month(callback) {
|
|
3951
|
+
return this.months(callback);
|
|
3952
|
+
}
|
|
3953
|
+
months(callback) {
|
|
3954
|
+
return this.#move((date) => date.setUTCMonth(date.getUTCMonth() + this.value), callback);
|
|
3955
|
+
}
|
|
3956
|
+
year(callback) {
|
|
3957
|
+
return this.years(callback);
|
|
3958
|
+
}
|
|
3959
|
+
years(callback) {
|
|
3960
|
+
return this.#move((date) => date.setUTCFullYear(date.getUTCFullYear() + this.value), callback);
|
|
3961
|
+
}
|
|
3962
|
+
/** Laravel's Wormhole::back(). */
|
|
3963
|
+
static back() {
|
|
3964
|
+
return travelBack();
|
|
3965
|
+
}
|
|
3966
|
+
#move(step, callback) {
|
|
3967
|
+
const date = new (realDate())(testNow ?? Date.now());
|
|
3968
|
+
step(date);
|
|
3969
|
+
setTestNow(date);
|
|
3970
|
+
if (callback) return thenTravelBack(callback);
|
|
3971
|
+
}
|
|
3972
|
+
};
|
|
3581
3973
|
function fail2(message, details = {}) {
|
|
3582
3974
|
const error = new AssertionError2({ message, actual: details.actual, expected: details.expected, operator: "fail" });
|
|
3583
3975
|
delete error.diff;
|
|
@@ -3599,7 +3991,7 @@ function format(value) {
|
|
|
3599
3991
|
function escapeHtml2(value) {
|
|
3600
3992
|
return value.replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[char]);
|
|
3601
3993
|
}
|
|
3602
|
-
function
|
|
3994
|
+
function stripTags2(html) {
|
|
3603
3995
|
return html.replace(/<[^>]*>/g, "");
|
|
3604
3996
|
}
|
|
3605
3997
|
function equals(a, b) {
|
|
@@ -4500,7 +4892,7 @@ ${this.excerpt()}`);
|
|
|
4500
4892
|
}
|
|
4501
4893
|
/** Like assertSee, against the content with its tags stripped. */
|
|
4502
4894
|
assertSeeText(value, escape = true) {
|
|
4503
|
-
const text =
|
|
4895
|
+
const text = stripTags2(this.content);
|
|
4504
4896
|
for (const needle of this.needles(value, escape)) {
|
|
4505
4897
|
assert(text.includes(needle), `Failed asserting that the response text contains [${needle}].
|
|
4506
4898
|
|
|
@@ -4509,14 +4901,14 @@ ${this.excerpt(text)}`);
|
|
|
4509
4901
|
return this;
|
|
4510
4902
|
}
|
|
4511
4903
|
assertDontSeeText(value, escape = true) {
|
|
4512
|
-
const text =
|
|
4904
|
+
const text = stripTags2(this.content);
|
|
4513
4905
|
for (const needle of this.needles(value, escape)) {
|
|
4514
4906
|
assert(!text.includes(needle), `Failed asserting that the response text does not contain [${needle}].`);
|
|
4515
4907
|
}
|
|
4516
4908
|
return this;
|
|
4517
4909
|
}
|
|
4518
4910
|
assertSeeTextInOrder(values, escape = true) {
|
|
4519
|
-
return this.inOrder(
|
|
4911
|
+
return this.inOrder(stripTags2(this.content), this.needles(values, escape));
|
|
4520
4912
|
}
|
|
4521
4913
|
// JSON --------------------------------------------------------------------------------------------------
|
|
4522
4914
|
/**
|
|
@@ -5464,9 +5856,33 @@ var InteractsWithConsole = class extends InteractsWithDatabase {
|
|
|
5464
5856
|
return new PendingCommand(this.app, command, parameters);
|
|
5465
5857
|
}
|
|
5466
5858
|
};
|
|
5467
|
-
var
|
|
5859
|
+
var InteractsWithTime = class extends InteractsWithConsole {
|
|
5860
|
+
travelTo(date, callback) {
|
|
5861
|
+
return callback ? travelTo(date, callback) : travelTo(date);
|
|
5862
|
+
}
|
|
5863
|
+
travel(value) {
|
|
5864
|
+
return travel(value);
|
|
5865
|
+
}
|
|
5866
|
+
travelBack() {
|
|
5867
|
+
return travelBack();
|
|
5868
|
+
}
|
|
5869
|
+
freezeTime(callback) {
|
|
5870
|
+
return callback ? freezeTime(callback) : freezeTime();
|
|
5871
|
+
}
|
|
5872
|
+
freezeSecond(callback) {
|
|
5873
|
+
return callback ? freezeSecond(callback) : freezeSecond();
|
|
5874
|
+
}
|
|
5875
|
+
async tearDown() {
|
|
5876
|
+
try {
|
|
5877
|
+
await super.tearDown();
|
|
5878
|
+
} finally {
|
|
5879
|
+
travelBack();
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5882
|
+
};
|
|
5883
|
+
var TestCase = class extends InteractsWithTime {
|
|
5468
5884
|
};
|
|
5469
|
-
function
|
|
5885
|
+
function install2(token2, fake) {
|
|
5470
5886
|
facade(token2).swap(fake);
|
|
5471
5887
|
return fake;
|
|
5472
5888
|
}
|
|
@@ -5495,10 +5911,27 @@ var QUEUE_OPTIONS = ["connection", "queue", "delay", "tries", "backoff", "timeou
|
|
|
5495
5911
|
function isQueueable(job) {
|
|
5496
5912
|
if (typeof job !== "object" || job === null) return false;
|
|
5497
5913
|
const members = job;
|
|
5498
|
-
if (members[SHOULD_QUEUE] === true) return true;
|
|
5914
|
+
if (members.shouldQueue === true || members[SHOULD_QUEUE] === true) return true;
|
|
5499
5915
|
if (QUEUE_OPTIONS.some((option) => members[option] !== void 0)) return true;
|
|
5500
5916
|
return typeof members.uniqueId === "function" || typeof members.retryUntil === "function";
|
|
5501
5917
|
}
|
|
5918
|
+
var isClass = (value) => typeof value === "function" && typeof value.prototype?.handle === "function";
|
|
5919
|
+
var asPair = (listener) => {
|
|
5920
|
+
if (Array.isArray(listener) && listener.length === 2 && typeof listener[1] === "string") return [listener[0], listener[1]];
|
|
5921
|
+
return isClass(listener) ? [listener, "handle"] : void 0;
|
|
5922
|
+
};
|
|
5923
|
+
function listenerMatches(registered, expected) {
|
|
5924
|
+
if (expected === Function) return typeof registered === "function" && !isClass(registered);
|
|
5925
|
+
const [left, right] = [asPair(registered), asPair(expected)];
|
|
5926
|
+
if (left && right) return left[0] === right[0] && left[1] === right[1];
|
|
5927
|
+
return registered === expected;
|
|
5928
|
+
}
|
|
5929
|
+
function listenerName(listener) {
|
|
5930
|
+
const pair = asPair(listener);
|
|
5931
|
+
if (pair === void 0) return "Closure";
|
|
5932
|
+
const [owner, method] = pair;
|
|
5933
|
+
return Array.isArray(listener) ? `${owner.name}@${method}` : owner.name;
|
|
5934
|
+
}
|
|
5502
5935
|
var EventFake = class _EventFake {
|
|
5503
5936
|
constructor(dispatcher, eventsToFake = [], eventsToDispatch = []) {
|
|
5504
5937
|
this.dispatcher = dispatcher;
|
|
@@ -5511,11 +5944,11 @@ var EventFake = class _EventFake {
|
|
|
5511
5944
|
events = [];
|
|
5512
5945
|
/** Installs the fake (on the current container) and returns it: `const events = EventFake.fake([OrderShipped])`. */
|
|
5513
5946
|
static fake(eventsToFake = []) {
|
|
5514
|
-
return
|
|
5947
|
+
return install2(EventDispatcher, new _EventFake(_EventFake.original(), [eventsToFake].flat()));
|
|
5515
5948
|
}
|
|
5516
5949
|
/** Fakes every event except these. */
|
|
5517
5950
|
static fakeExcept(eventsToDispatch) {
|
|
5518
|
-
return
|
|
5951
|
+
return install2(EventDispatcher, new _EventFake(_EventFake.original(), [], [eventsToDispatch].flat()));
|
|
5519
5952
|
}
|
|
5520
5953
|
static original() {
|
|
5521
5954
|
const current = bound(EventDispatcher);
|
|
@@ -5572,9 +6005,20 @@ var EventFake = class _EventFake {
|
|
|
5572
6005
|
|
|
5573
6006
|
- ${names.join("\n- ")}`);
|
|
5574
6007
|
}
|
|
5575
|
-
/**
|
|
5576
|
-
|
|
5577
|
-
|
|
6008
|
+
/**
|
|
6009
|
+
* Laravel's assertListening(event, listener): the original dispatcher has that listener for the event (a class, a
|
|
6010
|
+
* [class, method] pair, the same closure, or Function for any closure; wildcard listeners count for a string event).
|
|
6011
|
+
* With only the event, any listener will do.
|
|
6012
|
+
*/
|
|
6013
|
+
assertListening(event, listener) {
|
|
6014
|
+
if (listener === void 0) {
|
|
6015
|
+
assert(this.hasListeners(event), `Event [${nameOf(event)}] does not have any listeners.`);
|
|
6016
|
+
return;
|
|
6017
|
+
}
|
|
6018
|
+
assert(
|
|
6019
|
+
this.listenersFor(event).some((registered) => listenerMatches(registered, listener)),
|
|
6020
|
+
`Event [${nameOf(event)}] does not have the [${listenerName(listener)}] listener attached to it`
|
|
6021
|
+
);
|
|
5578
6022
|
}
|
|
5579
6023
|
/** The recorded events (objects, or names for string events) that match. */
|
|
5580
6024
|
dispatched(event, callback) {
|
|
@@ -5583,6 +6027,17 @@ var EventFake = class _EventFake {
|
|
|
5583
6027
|
hasDispatched(event) {
|
|
5584
6028
|
return this.dispatched(event).length > 0;
|
|
5585
6029
|
}
|
|
6030
|
+
/** The original dispatcher's listeners for the event, as registered, wildcard ones included for a string event. */
|
|
6031
|
+
listenersFor(event) {
|
|
6032
|
+
const raw = this.dispatcher?.getRawListeners?.();
|
|
6033
|
+
if (raw === void 0) return [];
|
|
6034
|
+
const listeners = [...raw.get(event) ?? []];
|
|
6035
|
+
if (typeof event !== "string") return listeners;
|
|
6036
|
+
for (const [key, list] of raw) {
|
|
6037
|
+
if (typeof key === "string" && key !== event && key.includes("*") && Str.is(key, event)) listeners.push(...list);
|
|
6038
|
+
}
|
|
6039
|
+
return listeners;
|
|
6040
|
+
}
|
|
5586
6041
|
shouldFake(event) {
|
|
5587
6042
|
if (matchesAny(event, this.eventsToDispatch)) return false;
|
|
5588
6043
|
return this.eventsToFake.length === 0 || matchesAny(event, this.eventsToFake);
|
|
@@ -5636,10 +6091,10 @@ var BusFake = class _BusFake {
|
|
|
5636
6091
|
/** The rest of the chain, by the first job (Bus.chain([a, b, c]) records a with [b, c]). */
|
|
5637
6092
|
chains = /* @__PURE__ */ new Map();
|
|
5638
6093
|
static fake(jobsToFake = []) {
|
|
5639
|
-
return
|
|
6094
|
+
return install2(BusDispatcher, new _BusFake(_BusFake.original(), [jobsToFake].flat()));
|
|
5640
6095
|
}
|
|
5641
6096
|
static fakeExcept(jobsToDispatch) {
|
|
5642
|
-
return
|
|
6097
|
+
return install2(BusDispatcher, new _BusFake(_BusFake.original(), [], [jobsToDispatch].flat()));
|
|
5643
6098
|
}
|
|
5644
6099
|
static original() {
|
|
5645
6100
|
const current = bound(BusDispatcher);
|
|
@@ -5844,10 +6299,10 @@ var QueueFake = class _QueueFake {
|
|
|
5844
6299
|
jobsToPush;
|
|
5845
6300
|
jobs = [];
|
|
5846
6301
|
static fake(jobsToFake = []) {
|
|
5847
|
-
return
|
|
6302
|
+
return install2(QueueManager, new _QueueFake(_QueueFake.original(), [jobsToFake].flat()));
|
|
5848
6303
|
}
|
|
5849
6304
|
static fakeExcept(jobsToPush) {
|
|
5850
|
-
return
|
|
6305
|
+
return install2(QueueManager, new _QueueFake(_QueueFake.original(), [], [jobsToPush].flat()));
|
|
5851
6306
|
}
|
|
5852
6307
|
static original() {
|
|
5853
6308
|
const current = bound(QueueManager);
|
|
@@ -6026,13 +6481,14 @@ var MailFake = class _MailFake {
|
|
|
6026
6481
|
records = [];
|
|
6027
6482
|
static fake() {
|
|
6028
6483
|
const current = bound(MailManager);
|
|
6029
|
-
return
|
|
6484
|
+
return install2(MailManager, new _MailFake(current instanceof _MailFake ? current.manager : current));
|
|
6030
6485
|
}
|
|
6031
6486
|
// MailManager ----------------------------------------------------------------------------------------------
|
|
6032
6487
|
mailer(name) {
|
|
6033
6488
|
return {
|
|
6034
6489
|
to: (recipients) => this.pending(name, recipients),
|
|
6035
6490
|
send: (mailable) => this.sendVia(name, mailable, empty()),
|
|
6491
|
+
sendNow: (mailable) => this.sendNowVia(name, mailable, empty()),
|
|
6036
6492
|
queue: (mailable) => this.queueVia(name, mailable, empty(), void 0)
|
|
6037
6493
|
};
|
|
6038
6494
|
}
|
|
@@ -6042,6 +6498,10 @@ var MailFake = class _MailFake {
|
|
|
6042
6498
|
send(mailable) {
|
|
6043
6499
|
return this.sendVia(void 0, mailable, empty());
|
|
6044
6500
|
}
|
|
6501
|
+
/** Recorded as sent, never queued, like the real sendNow(). */
|
|
6502
|
+
sendNow(mailable) {
|
|
6503
|
+
return this.sendNowVia(void 0, mailable, empty());
|
|
6504
|
+
}
|
|
6045
6505
|
queue(mailable) {
|
|
6046
6506
|
return this.queueVia(void 0, mailable, empty(), void 0);
|
|
6047
6507
|
}
|
|
@@ -6125,6 +6585,7 @@ var MailFake = class _MailFake {
|
|
|
6125
6585
|
return pending;
|
|
6126
6586
|
},
|
|
6127
6587
|
send: (mailable) => this.sendVia(mailer, mailable, overrides),
|
|
6588
|
+
sendNow: (mailable) => this.sendNowVia(mailer, mailable, overrides),
|
|
6128
6589
|
queue: (mailable) => this.queueVia(mailer, mailable, overrides, void 0),
|
|
6129
6590
|
later: (delay, mailable) => this.queueVia(mailer, mailable, overrides, delay)
|
|
6130
6591
|
};
|
|
@@ -6134,6 +6595,10 @@ var MailFake = class _MailFake {
|
|
|
6134
6595
|
await this.record(mailer, mailable, overrides, isQueueable(mailable), void 0);
|
|
6135
6596
|
return void 0;
|
|
6136
6597
|
}
|
|
6598
|
+
async sendNowVia(mailer, mailable, overrides) {
|
|
6599
|
+
await this.record(mailer, mailable, overrides, false, void 0);
|
|
6600
|
+
return void 0;
|
|
6601
|
+
}
|
|
6137
6602
|
async queueVia(mailer, mailable, overrides, delay) {
|
|
6138
6603
|
await this.record(mailer, mailable, overrides, true, delay);
|
|
6139
6604
|
}
|
|
@@ -6204,7 +6669,7 @@ var NotificationFake = class _NotificationFake {
|
|
|
6204
6669
|
notifications = [];
|
|
6205
6670
|
static fake() {
|
|
6206
6671
|
const current = bound(NotificationDispatcher);
|
|
6207
|
-
return
|
|
6672
|
+
return install2(NotificationDispatcher, new _NotificationFake(current instanceof _NotificationFake ? current.dispatcher : current));
|
|
6208
6673
|
}
|
|
6209
6674
|
// NotificationDispatcher -------------------------------------------------------------------------------------
|
|
6210
6675
|
async send(notifiables, notification) {
|
|
@@ -6313,8 +6778,74 @@ var MIME = {
|
|
|
6313
6778
|
mp4: "video/mp4"
|
|
6314
6779
|
};
|
|
6315
6780
|
function guessMimeType(path) {
|
|
6316
|
-
const
|
|
6317
|
-
|
|
6781
|
+
const base = path.slice(path.lastIndexOf("/") + 1);
|
|
6782
|
+
const dot = base.lastIndexOf(".");
|
|
6783
|
+
const extension = dot === -1 ? "" : base.slice(dot + 1).toLowerCase();
|
|
6784
|
+
return Object.hasOwn(MIME, extension) ? MIME[extension] : void 0;
|
|
6785
|
+
}
|
|
6786
|
+
var DISK_TYPES = {
|
|
6787
|
+
txt: "text/plain",
|
|
6788
|
+
html: "text/html",
|
|
6789
|
+
htm: "text/html",
|
|
6790
|
+
css: "text/css",
|
|
6791
|
+
csv: "text/csv",
|
|
6792
|
+
md: "text/markdown",
|
|
6793
|
+
js: "text/javascript",
|
|
6794
|
+
mjs: "text/javascript",
|
|
6795
|
+
json: "application/json",
|
|
6796
|
+
xml: "application/xml",
|
|
6797
|
+
pdf: "application/pdf",
|
|
6798
|
+
zip: "application/zip",
|
|
6799
|
+
gz: "application/gzip",
|
|
6800
|
+
tar: "application/x-tar",
|
|
6801
|
+
"7z": "application/x-7z-compressed",
|
|
6802
|
+
rar: "application/vnd.rar",
|
|
6803
|
+
epub: "application/epub+zip",
|
|
6804
|
+
rtf: "application/rtf",
|
|
6805
|
+
doc: "application/msword",
|
|
6806
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
6807
|
+
xls: "application/vnd.ms-excel",
|
|
6808
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
6809
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
6810
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
6811
|
+
odt: "application/vnd.oasis.opendocument.text",
|
|
6812
|
+
ods: "application/vnd.oasis.opendocument.spreadsheet",
|
|
6813
|
+
odp: "application/vnd.oasis.opendocument.presentation",
|
|
6814
|
+
png: "image/png",
|
|
6815
|
+
jpg: "image/jpeg",
|
|
6816
|
+
jpeg: "image/jpeg",
|
|
6817
|
+
gif: "image/gif",
|
|
6818
|
+
webp: "image/webp",
|
|
6819
|
+
avif: "image/avif",
|
|
6820
|
+
svg: "image/svg+xml",
|
|
6821
|
+
ico: "image/x-icon",
|
|
6822
|
+
bmp: "image/bmp",
|
|
6823
|
+
tif: "image/tiff",
|
|
6824
|
+
tiff: "image/tiff",
|
|
6825
|
+
heic: "image/heic",
|
|
6826
|
+
heif: "image/heif",
|
|
6827
|
+
mp3: "audio/mpeg",
|
|
6828
|
+
m4a: "audio/mp4",
|
|
6829
|
+
wav: "audio/wav",
|
|
6830
|
+
ogg: "audio/ogg",
|
|
6831
|
+
oga: "audio/ogg",
|
|
6832
|
+
opus: "audio/ogg",
|
|
6833
|
+
flac: "audio/flac",
|
|
6834
|
+
aac: "audio/aac",
|
|
6835
|
+
mp4: "video/mp4",
|
|
6836
|
+
mov: "video/quicktime",
|
|
6837
|
+
webm: "video/webm",
|
|
6838
|
+
ogv: "video/ogg",
|
|
6839
|
+
avi: "video/x-msvideo",
|
|
6840
|
+
woff: "font/woff",
|
|
6841
|
+
woff2: "font/woff2",
|
|
6842
|
+
ttf: "font/ttf",
|
|
6843
|
+
otf: "font/otf",
|
|
6844
|
+
wasm: "application/wasm"
|
|
6845
|
+
};
|
|
6846
|
+
function diskType(extension) {
|
|
6847
|
+
const key = extension.replace(/^\./, "").toLowerCase();
|
|
6848
|
+
return Object.hasOwn(DISK_TYPES, key) ? DISK_TYPES[key] : void 0;
|
|
6318
6849
|
}
|
|
6319
6850
|
function hashName(file) {
|
|
6320
6851
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
@@ -6370,17 +6901,24 @@ var FakeDisk = class {
|
|
|
6370
6901
|
async put(path, contents, options = {}) {
|
|
6371
6902
|
const key = normalize(path);
|
|
6372
6903
|
const bytes = typeof contents === "string" ? new TextEncoder().encode(contents) : contents instanceof Uint8Array ? contents.slice() : await collect(contents);
|
|
6373
|
-
this.
|
|
6904
|
+
this.write(key, bytes, options.mimeType ?? diskType(posix.extname(key)), options);
|
|
6374
6905
|
return true;
|
|
6375
6906
|
}
|
|
6376
6907
|
putFile(directory, file, options = {}) {
|
|
6377
6908
|
return this.putFileAs(directory, file, hashName(file), options);
|
|
6378
6909
|
}
|
|
6910
|
+
/**
|
|
6911
|
+
* The upload's own type, as the S3 disk sends it: its extension(), never the MIME type the client sent as such nor
|
|
6912
|
+
* the stored name's type.
|
|
6913
|
+
*/
|
|
6379
6914
|
async putFileAs(directory, file, name, options = {}) {
|
|
6380
6915
|
const path = normalize(`${directory}/${name}`);
|
|
6381
|
-
|
|
6916
|
+
this.write(path, new Uint8Array(await file.arrayBuffer()), options.mimeType ?? diskType(file.extension()), options);
|
|
6382
6917
|
return path;
|
|
6383
6918
|
}
|
|
6919
|
+
write(key, bytes, mimeType, options) {
|
|
6920
|
+
this.stored.set(key, { bytes, visibility: options.visibility ?? "private", mimeType, modified: /* @__PURE__ */ new Date() });
|
|
6921
|
+
}
|
|
6384
6922
|
async delete(paths) {
|
|
6385
6923
|
for (const path of [paths].flat()) this.stored.delete(normalize(path));
|
|
6386
6924
|
return true;
|
|
@@ -6494,7 +7032,7 @@ var StorageFake = class _StorageFake {
|
|
|
6494
7032
|
static fake(disk) {
|
|
6495
7033
|
const current = bound(FilesystemManager);
|
|
6496
7034
|
const config = bound(ConfigRepository);
|
|
6497
|
-
const fake = current instanceof _StorageFake ? current :
|
|
7035
|
+
const fake = current instanceof _StorageFake ? current : install2(FilesystemManager, new _StorageFake(current, config?.get("filesystems.default", "local") ?? "local"));
|
|
6498
7036
|
const name = disk ?? fake.defaultDisk;
|
|
6499
7037
|
const faked = new FakeDisk(name);
|
|
6500
7038
|
fake.disks.set(name, faked);
|
|
@@ -6574,6 +7112,37 @@ var StorageFake = class _StorageFake {
|
|
|
6574
7112
|
return this.disk().path(path);
|
|
6575
7113
|
}
|
|
6576
7114
|
};
|
|
7115
|
+
var ascii = (text) => [...text].map((char) => char.charCodeAt(0));
|
|
7116
|
+
var u16be = (n) => [n >>> 8 & 255, n & 255];
|
|
7117
|
+
var u16le = (n) => [n & 255, n >>> 8 & 255];
|
|
7118
|
+
var u32le = (n) => [n & 255, n >>> 8 & 255, n >>> 16 & 255, n >>> 24 & 255];
|
|
7119
|
+
function concat(...parts) {
|
|
7120
|
+
const out = new Uint8Array(parts.reduce((n, part) => n + part.length, 0));
|
|
7121
|
+
let offset = 0;
|
|
7122
|
+
for (const part of parts) {
|
|
7123
|
+
out.set(part, offset);
|
|
7124
|
+
offset += part.length;
|
|
7125
|
+
}
|
|
7126
|
+
return out;
|
|
7127
|
+
}
|
|
7128
|
+
var BitWriter = class {
|
|
7129
|
+
bytes = [];
|
|
7130
|
+
buffer = 0;
|
|
7131
|
+
count = 0;
|
|
7132
|
+
write(value, bits) {
|
|
7133
|
+
this.buffer |= value << this.count;
|
|
7134
|
+
this.count += bits;
|
|
7135
|
+
while (this.count >= 8) {
|
|
7136
|
+
this.bytes.push(this.buffer & 255);
|
|
7137
|
+
this.buffer >>>= 8;
|
|
7138
|
+
this.count -= 8;
|
|
7139
|
+
}
|
|
7140
|
+
}
|
|
7141
|
+
finish() {
|
|
7142
|
+
if (this.count > 0) this.bytes.push(this.buffer & 255);
|
|
7143
|
+
return Uint8Array.from(this.bytes);
|
|
7144
|
+
}
|
|
7145
|
+
};
|
|
6577
7146
|
var CRC_TABLE = Array.from({ length: 256 }, (_, n) => {
|
|
6578
7147
|
let c = n;
|
|
6579
7148
|
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
@@ -6601,23 +7170,175 @@ function png(width, height) {
|
|
|
6601
7170
|
header.set([8, 2, 0, 0, 0], 8);
|
|
6602
7171
|
const rows = new Uint8Array((width * 3 + 1) * height).fill(255);
|
|
6603
7172
|
for (let y = 0; y < height; y++) rows[y * (width * 3 + 1)] = 0;
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
7173
|
+
return concat([137, 80, 78, 71, 13, 10, 26, 10], chunk("IHDR", header), chunk("IDAT", deflateSync(rows)), chunk("IEND", new Uint8Array()));
|
|
7174
|
+
}
|
|
7175
|
+
var oneCodeTable = (tableClass) => [255, 196, ...u16be(20), tableClass, 1, ...new Array(15).fill(0), 0];
|
|
7176
|
+
function jpeg(width, height) {
|
|
7177
|
+
const bits = Math.ceil(width / 8) * Math.ceil(height / 8) * 2;
|
|
7178
|
+
const scan = new Uint8Array(Math.ceil(bits / 8));
|
|
7179
|
+
const padding = scan.length * 8 - bits;
|
|
7180
|
+
if (padding > 0) scan[scan.length - 1] = (1 << padding) - 1;
|
|
7181
|
+
return concat(
|
|
7182
|
+
[255, 216],
|
|
7183
|
+
// SOI
|
|
7184
|
+
[255, 224, ...u16be(16), ...ascii("JFIF"), 0, 1, 1, 0, 0, 1, 0, 1, 0, 0],
|
|
7185
|
+
// APP0
|
|
7186
|
+
[255, 219, ...u16be(67), 0, ...new Array(64).fill(1)],
|
|
7187
|
+
// DQT: table 0, all ones
|
|
7188
|
+
[255, 192, ...u16be(11), 8, ...u16be(height), ...u16be(width), 1, 1, 17, 0],
|
|
7189
|
+
// SOF0: one component
|
|
7190
|
+
oneCodeTable(0),
|
|
7191
|
+
// DHT: DC table 0
|
|
7192
|
+
oneCodeTable(16),
|
|
7193
|
+
// DHT: AC table 0
|
|
7194
|
+
[255, 218, ...u16be(8), 1, 1, 0, 0, 63, 0],
|
|
7195
|
+
// SOS
|
|
7196
|
+
scan,
|
|
7197
|
+
[255, 217]
|
|
7198
|
+
// EOI
|
|
7199
|
+
);
|
|
7200
|
+
}
|
|
7201
|
+
function lzwZeros(pixels, minCodeSize) {
|
|
7202
|
+
const clear = 1 << minCodeSize;
|
|
7203
|
+
const end = clear + 1;
|
|
7204
|
+
const out = new BitWriter();
|
|
7205
|
+
const table = /* @__PURE__ */ new Map();
|
|
7206
|
+
let codeSize = minCodeSize + 1;
|
|
7207
|
+
let next = end + 1;
|
|
7208
|
+
let current = 0;
|
|
7209
|
+
out.write(clear, codeSize);
|
|
7210
|
+
for (let i = 1; i < pixels; i++) {
|
|
7211
|
+
const key = current << 8;
|
|
7212
|
+
const known = table.get(key);
|
|
7213
|
+
if (known !== void 0) {
|
|
7214
|
+
current = known;
|
|
7215
|
+
continue;
|
|
7216
|
+
}
|
|
7217
|
+
out.write(current, codeSize);
|
|
7218
|
+
if (next === 4096) {
|
|
7219
|
+
out.write(clear, codeSize);
|
|
7220
|
+
table.clear();
|
|
7221
|
+
codeSize = minCodeSize + 1;
|
|
7222
|
+
next = end + 1;
|
|
7223
|
+
} else {
|
|
7224
|
+
if (next >= 1 << codeSize) codeSize++;
|
|
7225
|
+
table.set(key, next++);
|
|
7226
|
+
}
|
|
7227
|
+
current = 0;
|
|
7228
|
+
}
|
|
7229
|
+
out.write(current, codeSize);
|
|
7230
|
+
out.write(end, codeSize);
|
|
7231
|
+
return out.finish();
|
|
7232
|
+
}
|
|
7233
|
+
function gif(width, height) {
|
|
7234
|
+
const data = lzwZeros(width * height, 2);
|
|
7235
|
+
const blocks = [];
|
|
7236
|
+
for (let offset = 0; offset < data.length; offset += 255) {
|
|
7237
|
+
const block = data.subarray(offset, offset + 255);
|
|
7238
|
+
blocks.push(block.length, ...block);
|
|
7239
|
+
}
|
|
7240
|
+
return concat(
|
|
7241
|
+
ascii("GIF89a"),
|
|
7242
|
+
[...u16le(width), ...u16le(height), 128, 0, 0],
|
|
7243
|
+
// a global palette of two colours
|
|
7244
|
+
[255, 255, 255, 0, 0, 0],
|
|
7245
|
+
[44, 0, 0, 0, 0, ...u16le(width), ...u16le(height), 0],
|
|
7246
|
+
// the image descriptor
|
|
7247
|
+
[2],
|
|
7248
|
+
blocks,
|
|
7249
|
+
[0, 59]
|
|
7250
|
+
// the end of the data, the trailer
|
|
7251
|
+
);
|
|
7252
|
+
}
|
|
7253
|
+
function webp(width, height) {
|
|
7254
|
+
const bits = new BitWriter();
|
|
7255
|
+
bits.write(47, 8);
|
|
7256
|
+
bits.write(width - 1, 14);
|
|
7257
|
+
bits.write(height - 1, 14);
|
|
7258
|
+
bits.write(0, 4);
|
|
7259
|
+
bits.write(0, 3);
|
|
7260
|
+
for (const symbol of [255, 255, 255, 255, 0]) {
|
|
7261
|
+
bits.write(1, 1);
|
|
7262
|
+
bits.write(0, 1);
|
|
7263
|
+
bits.write(1, 1);
|
|
7264
|
+
bits.write(symbol, 8);
|
|
7265
|
+
}
|
|
7266
|
+
const data = bits.finish();
|
|
7267
|
+
const padded = data.length % 2 === 0 ? data : concat(data, [0]);
|
|
7268
|
+
return concat(ascii("RIFF"), u32le(12 + padded.length), ascii("WEBP"), ascii("VP8L"), u32le(data.length), padded);
|
|
7269
|
+
}
|
|
7270
|
+
function bmp(width, height) {
|
|
7271
|
+
const row = Math.ceil(width * 3 / 4) * 4;
|
|
7272
|
+
const out = new Uint8Array(54 + row * height);
|
|
7273
|
+
const view = new DataView(out.buffer);
|
|
7274
|
+
out.set(ascii("BM"));
|
|
7275
|
+
view.setUint32(2, out.length, true);
|
|
7276
|
+
view.setUint32(10, 54, true);
|
|
7277
|
+
view.setUint32(14, 40, true);
|
|
7278
|
+
view.setInt32(18, width, true);
|
|
7279
|
+
view.setInt32(22, height, true);
|
|
7280
|
+
view.setUint16(26, 1, true);
|
|
7281
|
+
view.setUint16(28, 24, true);
|
|
7282
|
+
view.setUint32(34, row * height, true);
|
|
7283
|
+
for (let y = 0; y < height; y++) out.fill(255, 54 + y * row, 54 + y * row + width * 3);
|
|
7284
|
+
return out;
|
|
7285
|
+
}
|
|
7286
|
+
var IMAGE_FORMATS = {
|
|
7287
|
+
png: { type: "image/png", encode: png },
|
|
7288
|
+
jpg: { type: "image/jpeg", encode: jpeg },
|
|
7289
|
+
jpeg: { type: "image/jpeg", encode: jpeg },
|
|
7290
|
+
gif: { type: "image/gif", encode: gif },
|
|
7291
|
+
webp: { type: "image/webp", encode: webp },
|
|
7292
|
+
bmp: { type: "image/bmp", encode: bmp }
|
|
7293
|
+
};
|
|
7294
|
+
var extensionOf = (name) => name.includes(".") ? name.slice(name.lastIndexOf(".") + 1).toLowerCase() : "";
|
|
7295
|
+
var MORE_MIME_TYPES = {
|
|
7296
|
+
bmp: "image/bmp",
|
|
7297
|
+
avif: "image/avif",
|
|
7298
|
+
xml: "text/xml",
|
|
7299
|
+
htm: "text/html",
|
|
7300
|
+
md: "text/markdown",
|
|
7301
|
+
tsv: "text/tab-separated-values",
|
|
7302
|
+
text: "text/plain"
|
|
7303
|
+
};
|
|
7304
|
+
var LEADING_BYTES = {
|
|
7305
|
+
"image/jpeg": { bytes: [255, 216, 255, 224, ...u16be(16), ...ascii("JFIF"), 0, 1, 1, 0, 0, 1, 0, 1, 0, 0] },
|
|
7306
|
+
"image/png": { bytes: [137, 80, 78, 71, 13, 10, 26, 10] },
|
|
7307
|
+
"image/gif": { bytes: "GIF89a" },
|
|
7308
|
+
"image/webp": { bytes: "RIFF\0\0\0\0WEBP" },
|
|
7309
|
+
"image/avif": { bytes: [0, 0, 0, 20, ...ascii("ftypavif"), 0, 0, 0, 0, ...ascii("avifmif1")] },
|
|
7310
|
+
"image/bmp": { bytes: [...ascii("BM"), 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 40, 0, 0, 0] },
|
|
7311
|
+
"application/pdf": { bytes: "%PDF-1.4\n" },
|
|
7312
|
+
"image/svg+xml": { bytes: '<svg xmlns="http://www.w3.org/2000/svg"/>\n', text: true },
|
|
7313
|
+
"text/html": { bytes: "<!doctype html>\n", text: true },
|
|
7314
|
+
"text/xml": { bytes: '<?xml version="1.0"?>\n', text: true }
|
|
7315
|
+
};
|
|
7316
|
+
function contentFor(type, size) {
|
|
7317
|
+
const text = type.startsWith("text/") || type === "application/json";
|
|
7318
|
+
const leading = LEADING_BYTES[type] ?? { bytes: text ? "\n" : [], text };
|
|
7319
|
+
const head = typeof leading.bytes === "string" ? new TextEncoder().encode(leading.bytes) : Uint8Array.from(leading.bytes);
|
|
7320
|
+
const out = new Uint8Array(Math.max(size, head.length, 1)).fill(leading.text ? 32 : 0);
|
|
7321
|
+
out.set(head);
|
|
6611
7322
|
return out;
|
|
6612
7323
|
}
|
|
6613
7324
|
var FakeFile = {
|
|
6614
|
-
/**
|
|
7325
|
+
/**
|
|
7326
|
+
* A file of `kilobytes` KiB typed by `mimeType` or the name's extension. Validation reads a file's bytes, so the
|
|
7327
|
+
* file starts like its type (a PDF's `%PDF-`, a PNG's signature, text for text types) and passes `mimes` for its
|
|
7328
|
+
* name, as Laravel's fakes do. It is at least its type's leading bytes and never empty, whatever the size asked for.
|
|
7329
|
+
* An image made this way has no readable size: use image() for `dimensions`.
|
|
7330
|
+
*/
|
|
6615
7331
|
create(name, kilobytes = 0, mimeType) {
|
|
6616
|
-
|
|
7332
|
+
const type = mimeType ?? guessMimeType(name) ?? MORE_MIME_TYPES[extensionOf(name)] ?? "application/octet-stream";
|
|
7333
|
+
return new File([contentFor(type, Math.round(kilobytes * 1024))], name, { type });
|
|
6617
7334
|
},
|
|
6618
|
-
/**
|
|
7335
|
+
/**
|
|
7336
|
+
* A real white image of the given size in the format the name's extension asks for: png, jpg/jpeg, gif, webp or
|
|
7337
|
+
* bmp (any other name gets a JPEG, as Laravel's fakes do), so image, mimes and dimensions pass on it.
|
|
7338
|
+
*/
|
|
6619
7339
|
image(name, width = 10, height = 10) {
|
|
6620
|
-
|
|
7340
|
+
const format2 = IMAGE_FORMATS[extensionOf(name)] ?? IMAGE_FORMATS.jpeg;
|
|
7341
|
+
return new File([format2.encode(width, height)], name, { type: guessMimeType(name) ?? format2.type });
|
|
6621
7342
|
}
|
|
6622
7343
|
};
|
|
6623
7344
|
|
|
@@ -6699,18 +7420,18 @@ export {
|
|
|
6699
7420
|
addApiRouting,
|
|
6700
7421
|
registerHttpCommands,
|
|
6701
7422
|
HttpServiceProvider,
|
|
6702
|
-
On,
|
|
6703
|
-
Locked,
|
|
6704
|
-
Computed,
|
|
6705
|
-
ErrorBag,
|
|
6706
|
-
PendingEvent,
|
|
6707
|
-
Component,
|
|
6708
7423
|
LiveError,
|
|
6709
7424
|
CorruptComponentPayloadError,
|
|
6710
7425
|
MethodNotCallableError,
|
|
6711
7426
|
PropertyNotUpdatableError,
|
|
6712
7427
|
LiveComponentNotFoundError,
|
|
6713
7428
|
InvalidLiveRequestError,
|
|
7429
|
+
On,
|
|
7430
|
+
Locked,
|
|
7431
|
+
Computed,
|
|
7432
|
+
ErrorBag,
|
|
7433
|
+
PendingEvent,
|
|
7434
|
+
Component,
|
|
6714
7435
|
TestRequestScope,
|
|
6715
7436
|
Testable,
|
|
6716
7437
|
live,
|
|
@@ -6718,9 +7439,17 @@ export {
|
|
|
6718
7439
|
componentName,
|
|
6719
7440
|
PERSISTENT_MIDDLEWARE,
|
|
6720
7441
|
persistentMiddleware,
|
|
7442
|
+
WithPagination,
|
|
6721
7443
|
LiveComponentManager,
|
|
6722
7444
|
UPDATE_URI,
|
|
6723
7445
|
LiveServiceProvider,
|
|
7446
|
+
Pagination,
|
|
7447
|
+
travelTo,
|
|
7448
|
+
travelBack,
|
|
7449
|
+
freezeTime,
|
|
7450
|
+
freezeSecond,
|
|
7451
|
+
travel,
|
|
7452
|
+
Wormhole,
|
|
6724
7453
|
PendingCommand,
|
|
6725
7454
|
RefreshDatabaseState,
|
|
6726
7455
|
runCommand,
|
|
@@ -6749,8 +7478,9 @@ export {
|
|
|
6749
7478
|
MakesHttpRequests,
|
|
6750
7479
|
InteractsWithDatabase,
|
|
6751
7480
|
InteractsWithConsole,
|
|
7481
|
+
InteractsWithTime,
|
|
6752
7482
|
TestCase,
|
|
6753
|
-
|
|
7483
|
+
install2,
|
|
6754
7484
|
nameOf,
|
|
6755
7485
|
matches,
|
|
6756
7486
|
EventFake,
|
|
@@ -6766,4 +7496,4 @@ export {
|
|
|
6766
7496
|
StorageFake,
|
|
6767
7497
|
FakeFile
|
|
6768
7498
|
};
|
|
6769
|
-
//# sourceMappingURL=chunk-
|
|
7499
|
+
//# sourceMappingURL=chunk-ZOEPKBVF.js.map
|