@stacksjs/stx 0.2.175 → 0.2.177
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/a11y.d.ts +72 -2
- package/dist/a11y.js +25 -10
- package/dist/builtins/attrs.d.ts +19 -0
- package/dist/builtins/attrs.js +18 -0
- package/dist/builtins/safe-image.js +2 -1
- package/dist/builtins/stx-image.js +2 -1
- package/dist/builtins/stx-link.js +2 -1
- package/dist/cli-flags.d.ts +34 -0
- package/dist/cli-flags.js +20 -0
- package/dist/cli.js +1132 -895
- package/dist/client-script.js +2 -1
- package/dist/component-registry.d.ts +9 -0
- package/dist/component-renderer.js +20 -1
- package/dist/composables/use-fetch.d.ts +6 -1
- package/dist/composables/use-fetch.js +40 -18
- package/dist/composables/use-query.d.ts +7 -2
- package/dist/cookie-serialize.d.ts +40 -0
- package/dist/cookie-serialize.js +18 -0
- package/dist/craft.js +670 -442
- package/dist/crosswind-config.d.ts +13 -0
- package/dist/crosswind-config.js +6 -2
- package/dist/dev-server/crosswind.js +2 -1
- package/dist/edge-runtime.d.ts +2 -9
- package/dist/edge-runtime.js +2 -16
- package/dist/includes.js +8 -4
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/page-action.d.ts +10 -20
- package/dist/page-action.js +33 -9
- package/dist/process.js +2 -1
- package/dist/production-server.js +8 -7
- package/dist/props.d.ts +51 -8
- package/dist/pwa.js +637 -409
- package/dist/reactive.js +2 -2
- package/dist/resolve-stx-targets.d.ts +49 -0
- package/dist/resolve-stx-targets.js +38 -0
- package/dist/runtime-globals.d.ts +24 -0
- package/dist/runtime-globals.js +16 -0
- package/dist/runtime-injection.d.ts +36 -0
- package/dist/runtime-injection.js +14 -0
- package/dist/server-bindings.js +3 -0
- package/dist/signal-processing.js +38 -7
- package/dist/signals.js +157 -35
- package/dist/ssg.d.ts +23 -0
- package/dist/ssg.js +615 -383
- package/dist/ssr.js +2 -16
- package/dist/stx-virtual-ts.d.ts +72 -4
- package/dist/stx-virtual-ts.js +111 -5
- package/dist/stx.d.ts +66 -7
- package/dist/template-hydrator.d.ts +1 -1
- package/dist/template-hydrator.js +6 -6
- package/dist/theme-tokens.d.ts +88 -0
- package/dist/theme-tokens.js +71 -0
- package/dist/typecheck.d.ts +6 -1
- package/dist/typecheck.js +102 -67
- package/dist/types/config-types.d.ts +24 -0
- package/dist/utils.js +5 -4
- package/package.json +5 -5
- package/stx.d.ts +66 -7
package/dist/typecheck.js
CHANGED
|
@@ -3,25 +3,42 @@ import path from "node:path";
|
|
|
3
3
|
import { STX_RUNTIME_GLOBALS } from "./runtime-globals.js";
|
|
4
4
|
import { stateDir } from "./state-dir.js";
|
|
5
5
|
import {
|
|
6
|
+
absolutizeRelativeSpecifiers,
|
|
6
7
|
buildVirtualTypeScript,
|
|
7
8
|
extractScriptBlocks,
|
|
8
9
|
resolvePosition,
|
|
9
10
|
clientPayloadDeclarations,
|
|
10
11
|
scrapedBridgeNames,
|
|
11
12
|
serverContextDeclarations,
|
|
13
|
+
blankScriptDirectives,
|
|
12
14
|
substituteInterpolationsInPlace
|
|
13
15
|
} from "./stx-virtual-ts.js";
|
|
14
16
|
export {
|
|
15
17
|
extractScriptBlocks,
|
|
16
18
|
extractTemplateExpressions
|
|
17
19
|
} from "./stx-virtual-ts.js";
|
|
18
|
-
export function buildVirtualSource(block, serverCode = "") {
|
|
20
|
+
export function buildVirtualSource(block, serverCode = "", originDir = "") {
|
|
19
21
|
const leadingNewlines = block.startLine > 1 ? `
|
|
20
|
-
`.repeat(block.startLine - 1) : "", code = substituteInterpolationsInPlace(block.code), payload = block.kind === "client" || block.kind === "plain" ? clientPayloadDeclarations(serverCode) : "";
|
|
21
|
-
return `${leadingNewlines + code}
|
|
22
|
+
`.repeat(block.startLine - 1) : "", code = blankScriptDirectives(substituteInterpolationsInPlace(block.code)), payload = block.kind === "client" || block.kind === "plain" ? clientPayloadDeclarations(serverCode, block.code) : "";
|
|
23
|
+
return absolutizeRelativeSpecifiers(`${leadingNewlines + code}
|
|
22
24
|
${payload}
|
|
23
25
|
export {}
|
|
24
|
-
|
|
26
|
+
`, originDir);
|
|
27
|
+
}
|
|
28
|
+
function virtualStxModuleDeclaration(files) {
|
|
29
|
+
const anchor = files.length > 0 ? path.dirname(path.resolve(files[0])) : process.cwd();
|
|
30
|
+
try {
|
|
31
|
+
Bun.resolveSync("@stacksjs/stx", anchor);
|
|
32
|
+
} catch {
|
|
33
|
+
return "";
|
|
34
|
+
}
|
|
35
|
+
return [
|
|
36
|
+
"// Generated \u2014 `stx` is the virtual specifier client-script.ts strips.",
|
|
37
|
+
"declare module 'stx' {",
|
|
38
|
+
" export * from '@stacksjs/stx'",
|
|
39
|
+
"}"
|
|
40
|
+
].join(`
|
|
41
|
+
`);
|
|
25
42
|
}
|
|
26
43
|
export function virtualPathFor(filePath, kind, index) {
|
|
27
44
|
return `${filePath}.__stx_${kind}${index}.ts`;
|
|
@@ -39,6 +56,9 @@ export function findRuntimeTypeDeclarations() {
|
|
|
39
56
|
return null;
|
|
40
57
|
}
|
|
41
58
|
const TSC_LINE_RE = /^(.*?)\((\d+),(\d+)\):\s+(error|warning)\s+TS(\d+):\s+(.*)$/;
|
|
59
|
+
function isSyntactic(code) {
|
|
60
|
+
return code >= 1000 && code < 2000;
|
|
61
|
+
}
|
|
42
62
|
function readProjectPathAliases(startDir) {
|
|
43
63
|
let dir = path.resolve(startDir), configPath;
|
|
44
64
|
for (;; ) {
|
|
@@ -130,17 +150,15 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
130
150
|
const source = await Bun.file(file).text(), blocks = extractScriptBlocks(source).filter((b) => {
|
|
131
151
|
if (b.kind === "server")
|
|
132
152
|
return checkServer;
|
|
133
|
-
|
|
134
|
-
return checkClient;
|
|
135
|
-
return !1;
|
|
153
|
+
return checkClient;
|
|
136
154
|
}), serverCode = extractScriptBlocks(source).filter((block) => block.kind === "server").map((block) => block.code).join(`
|
|
137
155
|
`);
|
|
138
156
|
if (checkClient && serverCode) {
|
|
139
|
-
const clientBlocks = blocks.filter((block) => block.kind
|
|
157
|
+
const clientBlocks = blocks.filter((block) => block.kind !== "server"), names = [...new Set(clientBlocks.flatMap((block) => scrapedBridgeNames(serverCode, block.code)))];
|
|
140
158
|
if (names.length > 0)
|
|
141
|
-
scrapedByFile.set(file, { names, line: clientBlocks[0].startLine });
|
|
159
|
+
scrapedByFile.set(file, { names, line: clientBlocks[0].startLine, kind: clientBlocks[0].kind });
|
|
142
160
|
}
|
|
143
|
-
const templateBuffer = checkTemplates ? buildVirtualTypeScript(source, { runtimeGlobals: !runtimeTypes }) : null, expressions = templateBuffer ? [...templateBuffer.lineMap.values()].filter((m) => m.expression).length : 0;
|
|
161
|
+
const originDir = path.dirname(path.resolve(file)), templateBuffer = checkTemplates ? buildVirtualTypeScript(source, { runtimeGlobals: !runtimeTypes, originDir }) : null, expressions = templateBuffer ? [...templateBuffer.lineMap.values()].filter((m) => m.expression).length : 0;
|
|
144
162
|
if (blocks.length === 0 && expressions === 0)
|
|
145
163
|
continue;
|
|
146
164
|
checkedFiles.push(file);
|
|
@@ -148,7 +166,7 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
148
166
|
expressionCount += expressions;
|
|
149
167
|
blocks.forEach((block, i) => {
|
|
150
168
|
virtualFiles.set(virtualPathFor(file, block.kind, i), {
|
|
151
|
-
source: buildVirtualSource(block, serverCode),
|
|
169
|
+
source: buildVirtualSource(block, serverCode, originDir),
|
|
152
170
|
origin: file,
|
|
153
171
|
kind: block.kind
|
|
154
172
|
});
|
|
@@ -169,8 +187,9 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
169
187
|
const globalsDts = `${stateDir()}/typecheck/__stx_globals.d.ts`, globalDecls = [
|
|
170
188
|
"// Generated \u2014 the context serve.ts injects into <script server> blocks.",
|
|
171
189
|
serverContextDeclarations(),
|
|
172
|
-
...runtimeTypes ? [] : ["declare const window: any", ...STX_RUNTIME_GLOBALS.map((name) => `declare const ${name}: any`)]
|
|
173
|
-
|
|
190
|
+
...runtimeTypes ? [] : ["declare const window: any", ...STX_RUNTIME_GLOBALS.map((name) => `declare const ${name}: any`)],
|
|
191
|
+
virtualStxModuleDeclaration(files)
|
|
192
|
+
].filter(Boolean).join(`
|
|
174
193
|
`), workDir = `${stateDir()}/typecheck`;
|
|
175
194
|
await Bun.$`rm -rf ${workDir}`.quiet().nothrow();
|
|
176
195
|
await Bun.$`mkdir -p ${workDir}`.quiet().nothrow();
|
|
@@ -182,64 +201,80 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
182
201
|
await Bun.write(`${workDir}/${safe}`, meta.source);
|
|
183
202
|
writtenToOrigin.set(safe, meta);
|
|
184
203
|
}
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
204
|
+
const compilerOptions = {
|
|
205
|
+
target: "ESNext",
|
|
206
|
+
module: "ESNext",
|
|
207
|
+
moduleResolution: "bundler",
|
|
208
|
+
strict: !0,
|
|
209
|
+
noEmit: !0,
|
|
210
|
+
skipLibCheck: !0,
|
|
211
|
+
allowJs: !0,
|
|
212
|
+
esModuleInterop: !0,
|
|
213
|
+
resolveJsonModule: !0,
|
|
214
|
+
lib: ["ESNext", "DOM", "DOM.Iterable"],
|
|
215
|
+
types: [],
|
|
216
|
+
...readProjectPathAliases(files.length > 0 ? path.dirname(path.resolve(files[0])) : process.cwd()),
|
|
217
|
+
...options.compilerOptions ?? {}
|
|
218
|
+
}, runTsc = async (names, pass) => {
|
|
219
|
+
const tsconfigPath = `${workDir}/tsconfig${pass === 1 ? "" : `.${pass}`}.json`;
|
|
220
|
+
await Bun.write(tsconfigPath, JSON.stringify({
|
|
221
|
+
compilerOptions,
|
|
222
|
+
files: names.map((name) => `${workDir}/${name}`).concat(globalsDts, ...ambient)
|
|
223
|
+
}, null, 2));
|
|
224
|
+
const proc = Bun.spawnSync(["bun", "x", "tsc", "-p", tsconfigPath, "--pretty", "false"], {
|
|
225
|
+
stdout: "pipe",
|
|
226
|
+
stderr: "pipe"
|
|
227
|
+
});
|
|
228
|
+
return {
|
|
229
|
+
output: new TextDecoder().decode(proc.stdout) + new TextDecoder().decode(proc.stderr),
|
|
230
|
+
exitCode: proc.exitCode ?? 0
|
|
231
|
+
};
|
|
232
|
+
}, first = await runTsc([...writtenToOrigin.keys()], 1), output = first.output, diagnostics = [], exitCode = first.exitCode;
|
|
208
233
|
let printedDiagnostics = 0;
|
|
209
234
|
const ambientBasenames = new Set(ambient.map((lib) => lib.split("/").pop()));
|
|
210
235
|
let ambientDiagnostics = 0;
|
|
211
|
-
|
|
236
|
+
const unparseable = new Set, parseTscOutput = (text) => {
|
|
237
|
+
for (const rawLine of text.split(`
|
|
212
238
|
`)) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
continue;
|
|
216
|
-
printedDiagnostics++;
|
|
217
|
-
if (ambientBasenames.has(m[1].split("/").pop()))
|
|
218
|
-
ambientDiagnostics++;
|
|
219
|
-
const [, filePart, lineStr, colStr, category, code, message] = m, entry = writtenToOrigin.get(filePart.split("/").pop());
|
|
220
|
-
if (!entry)
|
|
221
|
-
continue;
|
|
222
|
-
let line = Number(lineStr), column = Number(colStr), expression;
|
|
223
|
-
if (entry.virtual) {
|
|
224
|
-
const at = resolvePosition(entry.virtual, line, column);
|
|
225
|
-
if (!at?.expression)
|
|
239
|
+
const m = rawLine.match(TSC_LINE_RE);
|
|
240
|
+
if (!m)
|
|
226
241
|
continue;
|
|
227
|
-
|
|
242
|
+
printedDiagnostics++;
|
|
243
|
+
if (ambientBasenames.has(m[1].split("/").pop()))
|
|
244
|
+
ambientDiagnostics++;
|
|
245
|
+
const [, filePart, lineStr, colStr, category, code, message] = m, written = filePart.split("/").pop(), entry = writtenToOrigin.get(written);
|
|
246
|
+
if (!entry)
|
|
228
247
|
continue;
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
248
|
+
if (isSyntactic(Number(code)))
|
|
249
|
+
unparseable.add(written);
|
|
250
|
+
let line = Number(lineStr), column = Number(colStr), expression;
|
|
251
|
+
if (entry.virtual) {
|
|
252
|
+
const at = resolvePosition(entry.virtual, line, column);
|
|
253
|
+
if (!at?.expression)
|
|
254
|
+
continue;
|
|
255
|
+
if (Number(code) === 2774)
|
|
256
|
+
continue;
|
|
257
|
+
line = at.line;
|
|
258
|
+
column = at.column;
|
|
259
|
+
expression = at.expression.code.trim();
|
|
260
|
+
}
|
|
261
|
+
diagnostics.push({
|
|
262
|
+
file: entry.origin,
|
|
263
|
+
line,
|
|
264
|
+
column,
|
|
265
|
+
code: Number(code),
|
|
266
|
+
message,
|
|
267
|
+
category: category === "error" ? "error" : "warning",
|
|
268
|
+
blockKind: entry.kind,
|
|
269
|
+
expression
|
|
270
|
+
});
|
|
232
271
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
category: category === "error" ? "error" : "warning",
|
|
240
|
-
blockKind: entry.kind,
|
|
241
|
-
expression
|
|
242
|
-
});
|
|
272
|
+
};
|
|
273
|
+
parseTscOutput(output);
|
|
274
|
+
const firstPassPrinted = printedDiagnostics, firstPassAmbient = ambientDiagnostics;
|
|
275
|
+
if (unparseable.size > 0 && unparseable.size < writtenToOrigin.size) {
|
|
276
|
+
const survivors = [...writtenToOrigin.keys()].filter((name) => !unparseable.has(name));
|
|
277
|
+
parseTscOutput((await runTsc(survivors, 2)).output);
|
|
243
278
|
}
|
|
244
279
|
if (checkClient)
|
|
245
280
|
for (const [file, scraped] of scrapedByFile) {
|
|
@@ -252,7 +287,7 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
252
287
|
code: 0,
|
|
253
288
|
message: `${scraped.names.length === 1 ? "1 value reaches" : `${scraped.names.length} values reach`} this block on the implicit bridge and ${scraped.names.length === 1 ? "is" : "are"} untyped: ${scraped.names.join(", ")}. Declare them with defineClientPayload({ ${scraped.names.join(", ")} }) in the server block to have them checked.`,
|
|
254
289
|
category: "warning",
|
|
255
|
-
blockKind:
|
|
290
|
+
blockKind: scraped.kind
|
|
256
291
|
});
|
|
257
292
|
}
|
|
258
293
|
const seen = new Set, unique = diagnostics.filter((d) => {
|
|
@@ -263,7 +298,7 @@ export async function typecheckStxFiles(files, options = {}) {
|
|
|
263
298
|
return !0;
|
|
264
299
|
});
|
|
265
300
|
unique.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.column - b.column);
|
|
266
|
-
const brokenLib =
|
|
301
|
+
const brokenLib = firstPassAmbient > 0 && unique.length === 0, failure = brokenLib || exitCode !== 0 && firstPassPrinted === 0 ? `${brokenLib ? "a file passed with --lib has errors of its own, so nothing in your code was checked" : `tsc exited ${exitCode} without reporting any diagnostic, so nothing was checked`}. ` + "A syntax error in an ambient declaration file does this \u2014 skipLibCheck suppresses " + `SEMANTIC diagnostics only. tsc said:
|
|
267
302
|
${output.trim().split(`
|
|
268
303
|
`).slice(0, 12).join(`
|
|
269
304
|
`) || "(no output)"}` : void 0;
|
|
@@ -271,7 +306,7 @@ ${output.trim().split(`
|
|
|
271
306
|
}
|
|
272
307
|
export function formatTypecheckDiagnostics(diagnostics) {
|
|
273
308
|
return diagnostics.map((d) => {
|
|
274
|
-
const where = d.blockKind === "template" ? `[template: ${d.expression ?? ""}]` : `[<script ${d.blockKind}
|
|
309
|
+
const where = d.blockKind === "template" ? `[template: ${d.expression ?? ""}]` : `[${d.blockKind === "plain" ? "<script>" : `<script ${d.blockKind}>`}]`;
|
|
275
310
|
return `${d.file}:${d.line}:${d.column} ${d.category} TS${d.code} ${d.message} ${where}`;
|
|
276
311
|
}).join(`
|
|
277
312
|
`);
|
|
@@ -594,6 +594,30 @@ export interface StxConfig {
|
|
|
594
594
|
enabled: boolean;
|
|
595
595
|
/** Path to partials directory, defaults to 'partials' in the same directory as the template */
|
|
596
596
|
partialsDir: string;
|
|
597
|
+
/**
|
|
598
|
+
* Called when an `@include` fails, instead of the failure being silent.
|
|
599
|
+
*
|
|
600
|
+
* A failed include renders the error where the markup should have been. In dev
|
|
601
|
+
* that is the right call — you see it as soon as you look. In a build it
|
|
602
|
+
* produces a file nobody would knowingly deploy, and nothing else can see it:
|
|
603
|
+
* one app shipped pre-rendered pages whose header, footer, hero and CTA were
|
|
604
|
+
* all replaced by an ENOENT banner while the build printed `Failed: 0` and
|
|
605
|
+
* exited 0 (stacksjs/stx#1921).
|
|
606
|
+
*
|
|
607
|
+
* The rendered output is unchanged — this only carries the fact out, so a
|
|
608
|
+
* caller that builds files can decide to fail. `partialsDir` is included
|
|
609
|
+
* because "it looked in the wrong place" is the usual cause and the resolved
|
|
610
|
+
* path is what makes it a ten-second fix.
|
|
611
|
+
*/
|
|
612
|
+
onIncludeError?: (failure: {
|
|
613
|
+
/** The path as written in the directive, e.g. `Bench/ConfirmHost`. */
|
|
614
|
+
includePath: string;
|
|
615
|
+
/** The template the directive appeared in. */
|
|
616
|
+
templatePath: string;
|
|
617
|
+
message: string;
|
|
618
|
+
/** The directory the include was resolved against. */
|
|
619
|
+
partialsDir: string;
|
|
620
|
+
}) => void;
|
|
597
621
|
/** Path to components directory, defaults to 'components' in the same directory as the template */
|
|
598
622
|
componentsDir: string;
|
|
599
623
|
/** @internal Configured component root retained while nested components resolve locally. */
|
package/dist/utils.js
CHANGED
|
@@ -558,7 +558,8 @@ ${triedPaths.map((p) => ` - ${p}`).join(`
|
|
|
558
558
|
const internalKeysToPreserve = new Set([
|
|
559
559
|
"__originalFilePath",
|
|
560
560
|
"__importedComponents",
|
|
561
|
-
COMPONENT_CLIENT_FACTORIES_CONTEXT_KEY
|
|
561
|
+
COMPONENT_CLIENT_FACTORIES_CONTEXT_KEY,
|
|
562
|
+
"__stx_client_signal_names"
|
|
562
563
|
]), filteredParentContext = {};
|
|
563
564
|
for (const [key, val] of Object.entries(parentContext))
|
|
564
565
|
if (!key.startsWith("__") || internalKeysToPreserve.has(key))
|
|
@@ -648,9 +649,9 @@ ${triedPaths.map((p) => ` - ${p}`).join(`
|
|
|
648
649
|
templateContent = templateContent.replace(/\{\{\s*text\s*\|\|\s*slot\s*\}\}/g, () => slotContent);
|
|
649
650
|
const { applySlots } = await importOnce("stx/slots", () => import("./slots.js")), { defaultSlot, namedSlots } = parsedSlotContent;
|
|
650
651
|
templateContent = await applySlots(templateContent, defaultSlot, namedSlots, componentContext);
|
|
651
|
-
const hasSignalScripts = clientScripts.some((s) => /\b(?:state|derived|effect|ref|reactive|computed|watch|watchEffect|useReactiveProp|defineProps|withDefaults|defineEmits|defineExpose|defineSlots)\s*(?:<[^<>()]*>)?\s*\(/.test(s)), clientSignalNames = componentClientSignalNames(clientScripts);
|
|
652
|
-
if (clientSignalNames.length > 0)
|
|
653
|
-
componentContext.__stx_client_signal_names = clientSignalNames;
|
|
652
|
+
const hasSignalScripts = clientScripts.some((s) => /\b(?:state|derived|effect|ref|reactive|computed|watch|watchEffect|useReactiveProp|defineProps|withDefaults|defineEmits|defineExpose|defineSlots)\s*(?:<[^<>()]*>)?\s*\(/.test(s)), clientSignalNames = componentClientSignalNames(clientScripts), inheritedClientSignalNames = Array.isArray(componentContext.__stx_client_signal_names) ? componentContext.__stx_client_signal_names : [];
|
|
653
|
+
if (clientSignalNames.length > 0 || inheritedClientSignalNames.length > 0)
|
|
654
|
+
componentContext.__stx_client_signal_names = [...new Set([...inheritedClientSignalNames, ...clientSignalNames])];
|
|
654
655
|
const componentOptions = {
|
|
655
656
|
...options,
|
|
656
657
|
_rootComponentsDir: options._rootComponentsDir || options.componentsDir,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/stx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.177",
|
|
5
5
|
"description": "A performant UI Framework. Powered by Bun.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -117,12 +117,12 @@
|
|
|
117
117
|
"dependencies": {
|
|
118
118
|
"@cwcss/crosswind": "^0.2.14",
|
|
119
119
|
"@stacksjs/clapp": "^0.2.0",
|
|
120
|
-
"@stacksjs/desktop": "0.2.
|
|
121
|
-
"@stacksjs/sanitizer": "0.2.
|
|
120
|
+
"@stacksjs/desktop": "0.2.177",
|
|
121
|
+
"@stacksjs/sanitizer": "0.2.177",
|
|
122
122
|
"@stacksjs/ts-i18n": "^0.1.11",
|
|
123
|
-
"bun-plugin-stx": "0.2.
|
|
123
|
+
"bun-plugin-stx": "0.2.177",
|
|
124
124
|
"bunfig": "^0.15.17",
|
|
125
|
-
"stx-router": "0.2.
|
|
125
|
+
"stx-router": "0.2.177",
|
|
126
126
|
"ts-broadcasting": "^0.0.9",
|
|
127
127
|
"ts-images": "^0.1.9",
|
|
128
128
|
"ts-syntax-highlighter": "^0.2.1"
|
package/stx.d.ts
CHANGED
|
@@ -199,11 +199,42 @@ declare function useSearchParams(): {
|
|
|
199
199
|
// Data fetching
|
|
200
200
|
// ============================================================================
|
|
201
201
|
|
|
202
|
-
|
|
202
|
+
/** How one run of a request should behave. */
|
|
203
|
+
interface StxRefetchOptions {
|
|
204
|
+
/**
|
|
205
|
+
* Refresh without touching `loading` or clearing the current error.
|
|
206
|
+
*
|
|
207
|
+
* A poll refreshes data that is already on screen, so driving the first-load
|
|
208
|
+
* state from it puts a spinner over a populated view on every tick — the
|
|
209
|
+
* reason a polling view could not use these primitives at all (#1929). Bind
|
|
210
|
+
* `isFetching` for a subtle in-flight indicator instead.
|
|
211
|
+
*/
|
|
212
|
+
background?: boolean
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
interface StxFetchResult<T> {
|
|
203
216
|
data: StxSignal<T | null>
|
|
217
|
+
/** True while a first-load request is in flight. A background one leaves it alone. */
|
|
204
218
|
loading: StxSignal<boolean>
|
|
219
|
+
/** True while ANY request is in flight, background ones included. */
|
|
220
|
+
isFetching: StxSignal<boolean>
|
|
205
221
|
error: StxSignal<Error | null>
|
|
206
|
-
refetch: () => Promise<void>
|
|
222
|
+
refetch: (_options?: StxRefetchOptions) => Promise<void>
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/*
|
|
226
|
+
* `useQuery` returns more than `useFetch` does, and declaring one shape for
|
|
227
|
+
* both hid it: `isStale` and `invalidate` existed at runtime and were invisible
|
|
228
|
+
* to the type, so reading either was an error on a value that was really there
|
|
229
|
+
* (#1929). Extending rather than widening the shared interface, because the
|
|
230
|
+
* inverse mistake is worse — declaring `invalidate` on `useFetch` would
|
|
231
|
+
* type-check a call to something that is undefined at runtime.
|
|
232
|
+
*/
|
|
233
|
+
interface StxQueryResult<T> extends StxFetchResult<T> {
|
|
234
|
+
/** True when cached data is on screen while a revalidation runs. */
|
|
235
|
+
isStale: StxSignal<boolean>
|
|
236
|
+
/** Drop this key from the cache and refetch. */
|
|
237
|
+
invalidate: (_options?: StxRefetchOptions) => Promise<void>
|
|
207
238
|
}
|
|
208
239
|
|
|
209
240
|
interface StxMutationResult<T> {
|
|
@@ -213,7 +244,7 @@ interface StxMutationResult<T> {
|
|
|
213
244
|
mutate: (body?: unknown) => Promise<T>
|
|
214
245
|
}
|
|
215
246
|
|
|
216
|
-
declare function useFetch<T = any>(_url: string, _options?: any):
|
|
247
|
+
declare function useFetch<T = any>(_url: string, _options?: any): StxFetchResult<T>
|
|
217
248
|
declare function useQuery<T = any>(_url: string, _options?: any): StxQueryResult<T>
|
|
218
249
|
declare function useMutation<T = any>(_url: string, _options?: any): StxMutationResult<T>
|
|
219
250
|
|
|
@@ -271,17 +302,45 @@ declare function useOptimistic<T = any, A = any>(
|
|
|
271
302
|
// DOM utilities
|
|
272
303
|
// ============================================================================
|
|
273
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Options the runtime reads, including the target it binds to.
|
|
307
|
+
*
|
|
308
|
+
* The target travels HERE rather than in a leading parameter, because that is
|
|
309
|
+
* what the implementation does: `var target = (options && options.target) || window`.
|
|
310
|
+
*/
|
|
311
|
+
interface StxEventListenerOptions {
|
|
312
|
+
/** Element, selector, or document/window. Defaults to `window`. */
|
|
313
|
+
target?: Window | Document | HTMLElement | string | null
|
|
314
|
+
capture?: boolean
|
|
315
|
+
passive?: boolean
|
|
316
|
+
once?: boolean
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/*
|
|
320
|
+
* Event first, then handler, then options (stacksjs/stx#1923).
|
|
321
|
+
*
|
|
322
|
+
* This was declared target-first while the runtime has always been
|
|
323
|
+
* event-first, so the two disagreed in the worst possible direction: `_target`
|
|
324
|
+
* accepted `string`, so the CORRECT call got as far as reading 'keydown' as a
|
|
325
|
+
* selector before failing on the handler, and an author who trusted the type
|
|
326
|
+
* and added a target wrote `useEventListener(window, 'keydown', fn)` — which
|
|
327
|
+
* binds an event named "window" with the string 'keydown' as its handler, and
|
|
328
|
+
* silently attaches nothing.
|
|
329
|
+
*
|
|
330
|
+
* `browser-composables.ts` had it right all along; the package shipped two
|
|
331
|
+
* declarations of one name with different arities and the registry got the
|
|
332
|
+
* wrong one. Same shape as the `reactive` split: one name, two conventions, and
|
|
333
|
+
* no way for the call site to tell which it has.
|
|
334
|
+
*/
|
|
274
335
|
declare function useEventListener<K extends keyof WindowEventMap>(
|
|
275
|
-
_target: Window | Document | HTMLElement | string | null,
|
|
276
336
|
_event: K,
|
|
277
337
|
_handler: (event: WindowEventMap[K]) => void,
|
|
278
|
-
_options?:
|
|
338
|
+
_options?: StxEventListenerOptions,
|
|
279
339
|
): StxCleanup
|
|
280
340
|
declare function useEventListener(
|
|
281
|
-
_target: Window | Document | HTMLElement | string | null,
|
|
282
341
|
_event: string,
|
|
283
342
|
_handler: (event: Event) => void,
|
|
284
|
-
_options?:
|
|
343
|
+
_options?: StxEventListenerOptions,
|
|
285
344
|
): StxCleanup
|
|
286
345
|
declare function useScrollLock(
|
|
287
346
|
_target?: HTMLElement | null | { current?: HTMLElement | null, value?: HTMLElement | null } | (() => HTMLElement | null | undefined),
|