@farm.js/islx 0.0.3 → 0.0.5
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/CHANGELOG.md +29 -0
- package/README.md +36 -1
- package/SPEC.md +11 -1
- package/dist/_chunks/{actions-2sTj0A2T.mjs → actions-8RQfrM-p.mjs} +2 -2
- package/dist/_chunks/{actions-B8dJ8f7L.mjs → actions-BuS5jDi0.mjs} +3 -144
- package/dist/_chunks/{codec-DqfVYKsx.mjs → codec-C8to-RQW.mjs} +3 -2
- package/dist/_chunks/island-DVOr-OUw.mjs +262 -0
- package/dist/_chunks/{schedule-BFoGKRrU.mjs → schedule-CjoybS3b.mjs} +1 -1
- package/dist/_chunks/{tree-BvYR6-PF.mjs → tree-BUkGP4AR.mjs} +5 -205
- package/dist/_chunks/values-37UIzxM0.mjs +144 -0
- package/dist/_chunks/vite-DG7hBn8A.mjs +503 -0
- package/dist/_chunks/{wire-C3Rpbwl6.d.mts → wire-Dipqh-EB.d.mts} +11 -1
- package/dist/_chunks/{wire-CXOodE06.mjs → wire-QEQWBr5s.mjs} +11 -1
- package/dist/client.mjs +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +3 -3
- package/dist/react/client.d.mts +8 -2
- package/dist/react/client.mjs +148 -14
- package/dist/react/server.mjs +25 -5
- package/dist/react.mjs +3 -1
- package/dist/server.mjs +4 -3
- package/dist/tanstack-start/server.d.mts +11 -0
- package/dist/tanstack-start/server.mjs +54 -0
- package/dist/tanstack-start.d.mts +8 -0
- package/dist/tanstack-start.mjs +88 -0
- package/dist/vite.d.mts +21 -2
- package/dist/vite.mjs +1 -497
- package/package.json +19 -1
|
@@ -1,208 +1,8 @@
|
|
|
1
|
-
import { i as decodeTagged, n as NOT_A_VALUE, o as encodeLeaf } from "./codec-
|
|
2
|
-
import {
|
|
3
|
-
import { l as getValueStore, t as createServerReference } from "./actions-
|
|
4
|
-
import {
|
|
1
|
+
import { i as decodeTagged, n as NOT_A_VALUE, o as encodeLeaf } from "./codec-C8to-RQW.mjs";
|
|
2
|
+
import { M as VALUE_SCRIPT_TYPE, c as CHUNK_SCRIPT_TYPE, k as TREE_SCRIPT_TYPE, p as HTML_ELEMENT } from "./wire-QEQWBr5s.mjs";
|
|
3
|
+
import { l as getValueStore, t as createServerReference } from "./actions-8RQfrM-p.mjs";
|
|
4
|
+
import { a as getIslandServerHook, o as isolateIslands, r as getClientReference } from "./island-DVOr-OUw.mjs";
|
|
5
5
|
import * as React from "react";
|
|
6
|
-
import * as ReactDOM from "react-dom";
|
|
7
|
-
// src/react/island.ts
|
|
8
|
-
/**
|
|
9
|
-
* A client component, as the server renders it: its HTML inside `<f-island>`, and its props in
|
|
10
|
-
* a script after it. Shared by the server and the browser: inside a hydrating island, a nested
|
|
11
|
-
* client component is part of that island's React tree, not an island of its own.
|
|
12
|
-
*
|
|
13
|
-
* The compiler wraps every export of a `"use client"` module with `createIsland` in the server
|
|
14
|
-
* build. The browser build keeps the module as it is.
|
|
15
|
-
*/
|
|
16
|
-
const CLIENT_REFERENCE = Symbol.for("islx.react.client-reference");
|
|
17
|
-
/** The module export an island component renders, or undefined for any other type. */
|
|
18
|
-
function getClientReference(type) {
|
|
19
|
-
if (!type || typeof type !== "function" && typeof type !== "object") return void 0;
|
|
20
|
-
return type[CLIENT_REFERENCE];
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Makes `element`'s client components part of the React root around it (a layout that hydrates
|
|
24
|
-
* as one root), instead of islands of their own.
|
|
25
|
-
*/
|
|
26
|
-
function withinClientRoot(element) {
|
|
27
|
-
return React.createElement(getContexts().graph.Provider, { value: true }, element);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Starts islands again below a `withinClientRoot`: the page a hydrating layout keeps as server
|
|
31
|
-
* HTML renders its client components as islands of their own.
|
|
32
|
-
*/
|
|
33
|
-
function isolateIslands(element) {
|
|
34
|
-
return React.createElement(getContexts().graph.Provider, { value: false }, element);
|
|
35
|
-
}
|
|
36
|
-
const RENDER_STATE_RESOLVER = Symbol.for("islx.react.render-state");
|
|
37
|
-
/**
|
|
38
|
-
* For a framework that knows the request being rendered without React context (a request store):
|
|
39
|
-
* returns the render's settings when no `IslandsProvider` or `renderToStream` gave them. Keep the
|
|
40
|
-
* same `sent` set for the whole response.
|
|
41
|
-
*/
|
|
42
|
-
function setRenderStateResolver(resolve) {
|
|
43
|
-
globalThis[RENDER_STATE_RESOLVER] = resolve;
|
|
44
|
-
}
|
|
45
|
-
function resolveRenderState() {
|
|
46
|
-
const resolve = globalThis[RENDER_STATE_RESOLVER];
|
|
47
|
-
return resolve ? resolve() : null;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Gives islands a render's settings when the framework renders the page itself (not through
|
|
51
|
-
* `renderToStream`): which values the browser holds, and which this render already wrote.
|
|
52
|
-
*/
|
|
53
|
-
function IslandsProvider({ held = null, children }) {
|
|
54
|
-
const state = React.useMemo(() => ({
|
|
55
|
-
held: held && held.size > 0 ? held : null,
|
|
56
|
-
sent: /* @__PURE__ */ new Set()
|
|
57
|
-
}), [held]);
|
|
58
|
-
return React.createElement(getContexts().render.Provider, { value: state }, children);
|
|
59
|
-
}
|
|
60
|
-
const SERVER_HOOK = Symbol.for("islx.react.server");
|
|
61
|
-
/** @internal */
|
|
62
|
-
function getIslandServerHook() {
|
|
63
|
-
return globalThis[SERVER_HOOK];
|
|
64
|
-
}
|
|
65
|
-
/** @internal */
|
|
66
|
-
function setIslandServerHook(hook) {
|
|
67
|
-
globalThis[SERVER_HOOK] = hook;
|
|
68
|
-
}
|
|
69
|
-
/** Tagged objects the runtime made (slots), as opposed to app data with a "$" key. */
|
|
70
|
-
const runtimeTagged = /* @__PURE__ */ new WeakSet();
|
|
71
|
-
const CONTEXTS = Symbol.for("islx.react.contexts");
|
|
72
|
-
/** @internal */
|
|
73
|
-
function getContexts() {
|
|
74
|
-
const scope = globalThis;
|
|
75
|
-
const all = scope[CONTEXTS] ??= /* @__PURE__ */ new WeakMap();
|
|
76
|
-
let contexts = all.get(React.createElement);
|
|
77
|
-
if (!contexts) {
|
|
78
|
-
contexts = {
|
|
79
|
-
graph: React.createContext(false),
|
|
80
|
-
render: React.createContext(null)
|
|
81
|
-
};
|
|
82
|
-
all.set(React.createElement, contexts);
|
|
83
|
-
}
|
|
84
|
-
return contexts;
|
|
85
|
-
}
|
|
86
|
-
function isElement(value) {
|
|
87
|
-
const type = value?.$$typeof;
|
|
88
|
-
return type === Symbol.for("react.transitional.element") || type === Symbol.for("react.element");
|
|
89
|
-
}
|
|
90
|
-
const slotStyle = { display: "contents" };
|
|
91
|
-
/**
|
|
92
|
-
* Splits props into what renders on the server and what travels: server output passed to an
|
|
93
|
-
* island (`children`, or any element prop) renders once inside `<f-slot>`, and travels as a
|
|
94
|
-
* reference to it, so the browser reads its HTML from the page instead of receiving it twice.
|
|
95
|
-
*/
|
|
96
|
-
function splitSlots(value, prefix, counter) {
|
|
97
|
-
if (isElement(value)) {
|
|
98
|
-
const name = `${prefix}${counter.next++}`;
|
|
99
|
-
const reference = {
|
|
100
|
-
["$"]: "s",
|
|
101
|
-
["v"]: name
|
|
102
|
-
};
|
|
103
|
-
runtimeTagged.add(reference);
|
|
104
|
-
return {
|
|
105
|
-
render: React.createElement(SLOT_ELEMENT, {
|
|
106
|
-
key: value.key ?? name,
|
|
107
|
-
name,
|
|
108
|
-
style: slotStyle
|
|
109
|
-
}, value),
|
|
110
|
-
wire: reference
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
if (Array.isArray(value)) {
|
|
114
|
-
const parts = value.map((entry) => splitSlots(entry, prefix, counter));
|
|
115
|
-
return {
|
|
116
|
-
render: parts.map((part) => part.render),
|
|
117
|
-
wire: parts.map((part) => part.wire)
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
if (value && typeof value === "object") {
|
|
121
|
-
const prototype = Object.getPrototypeOf(value);
|
|
122
|
-
if (prototype !== Object.prototype && prototype !== null) return {
|
|
123
|
-
render: value,
|
|
124
|
-
wire: value
|
|
125
|
-
};
|
|
126
|
-
const render = {};
|
|
127
|
-
const wire = {};
|
|
128
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
129
|
-
const part = splitSlots(entry, prefix, counter);
|
|
130
|
-
render[key] = part.render;
|
|
131
|
-
wire[key] = part.wire;
|
|
132
|
-
}
|
|
133
|
-
return {
|
|
134
|
-
render,
|
|
135
|
-
wire
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
return {
|
|
139
|
-
render: value,
|
|
140
|
-
wire: value
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
const warned = /* @__PURE__ */ new Set();
|
|
144
|
-
function isThenable$1(value) {
|
|
145
|
-
return !!value && typeof value.then === "function";
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Wraps a client component so the server renders it as an island. `src` is the module the browser
|
|
149
|
-
* imports, `exportName` the component's export there.
|
|
150
|
-
*/
|
|
151
|
-
function createIsland(Component, src, exportName, strategy = "load") {
|
|
152
|
-
function Island(props) {
|
|
153
|
-
const { graph, render } = getContexts();
|
|
154
|
-
const insideIsland = React.useContext(graph);
|
|
155
|
-
const state = React.useContext(render) ?? resolveRenderState();
|
|
156
|
-
const id = React.useId();
|
|
157
|
-
if (insideIsland) return React.createElement(Component, props);
|
|
158
|
-
const hook = getIslandServerHook();
|
|
159
|
-
if (!hook) throw new Error("islx: an island rendered on a server that did not import @farm.js/islx/react/server");
|
|
160
|
-
let encoded;
|
|
161
|
-
let renderProps;
|
|
162
|
-
try {
|
|
163
|
-
const { render: rendered, wire } = splitSlots(props, `${id}s`, { next: 0 });
|
|
164
|
-
renderProps = rendered;
|
|
165
|
-
const skip = state ? /* @__PURE__ */ new Set([...state.held ?? [], ...state.sent]) : null;
|
|
166
|
-
encoded = hook.encode(wire, skip);
|
|
167
|
-
for (const value of encoded.values) state?.sent.add(value.key);
|
|
168
|
-
} catch (error) {
|
|
169
|
-
if (isThenable$1(error)) throw error;
|
|
170
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
171
|
-
const key = `${src}#${exportName}:${message}`;
|
|
172
|
-
if (!warned.has(key)) {
|
|
173
|
-
warned.add(key);
|
|
174
|
-
console.warn(`[islx] ${exportName} (${src}) was rendered without hydration: its props cannot be sent to the browser. ${message}`);
|
|
175
|
-
}
|
|
176
|
-
return React.createElement(Component, props);
|
|
177
|
-
}
|
|
178
|
-
if (strategy === "load") for (const href of getBuildDefaults().modules?.[src] ?? []) ReactDOM.preloadModule?.(href, { as: "script" });
|
|
179
|
-
return React.createElement(React.Fragment, null, ...encoded.values.map((value) => React.createElement("script", {
|
|
180
|
-
key: value.key,
|
|
181
|
-
type: VALUE_SCRIPT_TYPE,
|
|
182
|
-
name: value.key,
|
|
183
|
-
dangerouslySetInnerHTML: { __html: hook.escape(value.json) }
|
|
184
|
-
})), React.createElement(ISLAND_ELEMENT, {
|
|
185
|
-
key: "island",
|
|
186
|
-
src,
|
|
187
|
-
export: exportName,
|
|
188
|
-
props: id,
|
|
189
|
-
strategy,
|
|
190
|
-
style: slotStyle
|
|
191
|
-
}, React.createElement(graph.Provider, { value: true }, React.createElement(Component, renderProps))), React.createElement("script", {
|
|
192
|
-
key: "props",
|
|
193
|
-
type: PROPS_SCRIPT_TYPE,
|
|
194
|
-
htmlFor: id,
|
|
195
|
-
dangerouslySetInnerHTML: { __html: hook.escape(encoded.json) }
|
|
196
|
-
}));
|
|
197
|
-
}
|
|
198
|
-
Island.displayName = `Island(${Component.displayName || Component.name || exportName})`;
|
|
199
|
-
Object.defineProperty(Island, CLIENT_REFERENCE, { value: {
|
|
200
|
-
src,
|
|
201
|
-
exportName,
|
|
202
|
-
Component
|
|
203
|
-
} });
|
|
204
|
-
return Island;
|
|
205
|
-
}
|
|
206
6
|
// src/react/tree.ts
|
|
207
7
|
/**
|
|
208
8
|
* Server trees: a page as a serialized React tree, the role React Server Components' Flight
|
|
@@ -887,4 +687,4 @@ async function createServerTreeLayout(source, scope, options) {
|
|
|
887
687
|
}
|
|
888
688
|
};
|
|
889
689
|
}
|
|
890
|
-
export { renderServerTree as a,
|
|
690
|
+
export { renderServerTree as a, createServerTreeLayout as i, PAYLOAD_ONLY_ATTRIBUTE as n, updateServerTrees as o, createServerTreeElement as r, Outlet as t };
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { c as needsObjectTag, o as encodeLeaf } from "./codec-C8to-RQW.mjs";
|
|
2
|
+
import { A as VALUES_HEADER, j as VALUE_KEY_PATTERN } from "./wire-QEQWBr5s.mjs";
|
|
3
|
+
// src/server/values.ts
|
|
4
|
+
/**
|
|
5
|
+
* Values sent once: which parts of an island's props travel as values of their own, keyed by a
|
|
6
|
+
* hash of their JSON, and which of those a response leaves out because the browser already holds
|
|
7
|
+
* them. The browser side is `@farm.js/islx/client` (`getValueStore`).
|
|
8
|
+
*/
|
|
9
|
+
/** Shorter values stay inline: a reference and its script cost about 60 bytes. */
|
|
10
|
+
const VALUE_MIN_LENGTH = 512;
|
|
11
|
+
/** A 64-bit hash of a value's JSON, as 14 base-36 characters. Not cryptographic. */
|
|
12
|
+
function hashValue(json) {
|
|
13
|
+
let h1 = 3735928559 ^ json.length;
|
|
14
|
+
let h2 = 1103547991 ^ json.length;
|
|
15
|
+
for (let index = 0; index < json.length; index++) {
|
|
16
|
+
const code = json.charCodeAt(index);
|
|
17
|
+
h1 = Math.imul(h1 ^ code, 2654435761);
|
|
18
|
+
h2 = Math.imul(h2 ^ code, 1597334677);
|
|
19
|
+
}
|
|
20
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
21
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
22
|
+
return (h2 >>> 0).toString(36).padStart(7, "0") + (h1 >>> 0).toString(36).padStart(7, "0");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Serializes props with every value whose JSON reaches `minLength` replaced by a reference to it.
|
|
26
|
+
* Types JSON cannot hold are tagged first (the codec), so a `Map` or a `Date` is addressed like
|
|
27
|
+
* any other value. Inner values are addressed first, so a large value that differs between pages
|
|
28
|
+
* in one place still shares its unchanged parts.
|
|
29
|
+
*/
|
|
30
|
+
function addressValues(props, { minLength = 512, encodeSpecial, encoded = false } = {}) {
|
|
31
|
+
const values = /* @__PURE__ */ new Map();
|
|
32
|
+
const reference = (json, children, parent) => {
|
|
33
|
+
const key = hashValue(json);
|
|
34
|
+
if (!values.has(key)) values.set(key, {
|
|
35
|
+
key,
|
|
36
|
+
json,
|
|
37
|
+
children
|
|
38
|
+
});
|
|
39
|
+
parent.push(key);
|
|
40
|
+
return `{"$":"r","v":"${key}"}`;
|
|
41
|
+
};
|
|
42
|
+
const seen = /* @__PURE__ */ new Set();
|
|
43
|
+
const encode = (value, parent) => {
|
|
44
|
+
const special = encoded ? void 0 : encodeSpecial?.(value);
|
|
45
|
+
if (special !== void 0) {
|
|
46
|
+
const json = JSON.stringify(special);
|
|
47
|
+
return [json, json.length];
|
|
48
|
+
}
|
|
49
|
+
const leaf = encoded ? void 0 : encodeLeaf(value);
|
|
50
|
+
if (leaf) {
|
|
51
|
+
const json = JSON.stringify(leaf);
|
|
52
|
+
return [json, json.length];
|
|
53
|
+
}
|
|
54
|
+
if (typeof value === "function") throw new TypeError("functions are not serializable");
|
|
55
|
+
if (typeof value === "symbol") throw new TypeError("symbol values are not serializable");
|
|
56
|
+
if (value === null || typeof value !== "object") {
|
|
57
|
+
const json = JSON.stringify(value);
|
|
58
|
+
return [typeof value === "string" && json.length >= minLength ? reference(json, [], parent) : json, json.length];
|
|
59
|
+
}
|
|
60
|
+
if (seen.has(value)) throw new TypeError("circular values are not serializable");
|
|
61
|
+
seen.add(value);
|
|
62
|
+
try {
|
|
63
|
+
if (!encoded && (value instanceof Map || value instanceof Set)) {
|
|
64
|
+
const entries = value instanceof Map ? Array.from(value, ([key, entry]) => [key, entry]) : [...value];
|
|
65
|
+
const [json, length] = encode(entries, parent);
|
|
66
|
+
const prefix = `{"$":"${value instanceof Map ? "M" : "S"}","v":`;
|
|
67
|
+
return [`${prefix}${json}}`, length + prefix.length + 1];
|
|
68
|
+
}
|
|
69
|
+
const prototype = Object.getPrototypeOf(value);
|
|
70
|
+
if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) throw new TypeError("class instances are not serializable");
|
|
71
|
+
const own = [];
|
|
72
|
+
let length = 2;
|
|
73
|
+
const parts = [];
|
|
74
|
+
if (Array.isArray(value)) for (const entry of value) {
|
|
75
|
+
const [json, entryLength] = encode(entry, own);
|
|
76
|
+
parts.push(json);
|
|
77
|
+
length += entryLength + 1;
|
|
78
|
+
}
|
|
79
|
+
else for (const [key, entry] of Object.entries(value)) {
|
|
80
|
+
const [json, entryLength] = encode(entry, own);
|
|
81
|
+
const name = JSON.stringify(key);
|
|
82
|
+
parts.push(`${name}:${json}`);
|
|
83
|
+
length += name.length + entryLength + 2;
|
|
84
|
+
}
|
|
85
|
+
let json = Array.isArray(value) ? `[${parts.join(",")}]` : `{${parts.join(",")}}`;
|
|
86
|
+
if (!encoded && !Array.isArray(value) && needsObjectTag(value)) {
|
|
87
|
+
json = `{"$":"o","v":${json}}`;
|
|
88
|
+
length += 14;
|
|
89
|
+
}
|
|
90
|
+
const wrapper = own.length === 1 && json.length < minLength / 4;
|
|
91
|
+
if (length >= minLength && !wrapper) return [reference(json, own, parent), length];
|
|
92
|
+
parent.push(...own);
|
|
93
|
+
return [json, length];
|
|
94
|
+
} finally {
|
|
95
|
+
seen.delete(value);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const roots = [];
|
|
99
|
+
return {
|
|
100
|
+
json: `{${Object.entries(props).map(([key, entry]) => `${JSON.stringify(key)}:${encode(entry, roots)[0]}`).join(",")}}`,
|
|
101
|
+
values,
|
|
102
|
+
roots
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The values a response sends for `addressed`, parents first. A value the browser holds is left
|
|
107
|
+
* out with everything inside it: the browser kept those when it read the value.
|
|
108
|
+
*/
|
|
109
|
+
function selectValuesToSend(addressed, held) {
|
|
110
|
+
const send = [];
|
|
111
|
+
const seen = /* @__PURE__ */ new Set();
|
|
112
|
+
const visit = (key) => {
|
|
113
|
+
if (seen.has(key)) return;
|
|
114
|
+
seen.add(key);
|
|
115
|
+
if (held?.has(key)) return;
|
|
116
|
+
const value = addressed.values.get(key);
|
|
117
|
+
send.push(value);
|
|
118
|
+
value.children.forEach(visit);
|
|
119
|
+
};
|
|
120
|
+
addressed.roots.forEach(visit);
|
|
121
|
+
return send;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The value keys a request names (`F-Values`): well-formed keys only, at most `MAX_HELD_VALUES`.
|
|
125
|
+
*
|
|
126
|
+
* A response that leaves values out is one browser's. Integrations must keep it out of shared
|
|
127
|
+
* caches (CDN, ISR, PPR shells), and add `Vary: F-Values` to cacheable responses.
|
|
128
|
+
*/
|
|
129
|
+
function parseHeldValues(input) {
|
|
130
|
+
const header = typeof input === "string" || input == null ? input : (input instanceof Request ? input.headers : input).get(VALUES_HEADER);
|
|
131
|
+
const held = /* @__PURE__ */ new Set();
|
|
132
|
+
if (!header) return held;
|
|
133
|
+
for (const key of header.split(",", 256)) {
|
|
134
|
+
const trimmed = key.trim();
|
|
135
|
+
if (VALUE_KEY_PATTERN.test(trimmed)) held.add(trimmed);
|
|
136
|
+
if (held.size === 128) break;
|
|
137
|
+
}
|
|
138
|
+
return held;
|
|
139
|
+
}
|
|
140
|
+
/** A value script's JSON, safe inside a `<script>` element. */
|
|
141
|
+
function escapeScriptJson(json) {
|
|
142
|
+
return json.replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
143
|
+
}
|
|
144
|
+
export { parseHeldValues as a, hashValue as i, addressValues as n, selectValuesToSend as o, escapeScriptJson as r, VALUE_MIN_LENGTH as t };
|