@kuratchi/js 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -3
- package/dist/cli.js +13 -5
- package/dist/compiler/index.d.ts +3 -3
- package/dist/compiler/index.js +1284 -970
- package/dist/compiler/parser.d.ts +4 -3
- package/dist/compiler/parser.js +34 -21
- package/dist/compiler/template.js +75 -10
- package/dist/create.js +5 -5
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/runtime/context.d.ts +9 -3
- package/dist/runtime/context.js +17 -1
- package/dist/runtime/index.d.ts +2 -1
- package/dist/runtime/index.js +2 -1
- package/dist/runtime/request.d.ts +9 -0
- package/dist/runtime/request.js +23 -0
- package/package.json +5 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Extracts the top-level compile-time <script> block (before the HTML document).
|
|
5
5
|
* A top-level block containing reactive `$:` labels is preserved in template output
|
|
6
6
|
* as client script.
|
|
7
|
-
* Everything else is the template
|
|
7
|
+
* Everything else is the template — full HTML with native JS flow control.
|
|
8
8
|
* <style> inside the HTML is NOT extracted; it's part of the template.
|
|
9
9
|
*/
|
|
10
10
|
export interface ParsedFile {
|
|
@@ -12,7 +12,7 @@ export interface ParsedFile {
|
|
|
12
12
|
script: string | null;
|
|
13
13
|
/** Explicit server-side load function exported from the route script */
|
|
14
14
|
loadFunction: string | null;
|
|
15
|
-
/** Template
|
|
15
|
+
/** Template — the full HTML document with inline JS flow control */
|
|
16
16
|
template: string;
|
|
17
17
|
/** All imports from the script block */
|
|
18
18
|
serverImports: string[];
|
|
@@ -22,7 +22,7 @@ export interface ParsedFile {
|
|
|
22
22
|
actionFunctions: string[];
|
|
23
23
|
/** Top-level variable names declared in the script (const/let/var) */
|
|
24
24
|
dataVars: string[];
|
|
25
|
-
/** Component imports: import Name from '$lib/file.html'
|
|
25
|
+
/** Component imports: import Name from '$lib/file.html' → { Name: 'file' } */
|
|
26
26
|
componentImports: Record<string, string>;
|
|
27
27
|
/** Poll functions referenced via data-poll={fn(args)} in the template */
|
|
28
28
|
pollFunctions: string[];
|
|
@@ -47,6 +47,7 @@ interface ParseFileOptions {
|
|
|
47
47
|
kind?: 'route' | 'layout' | 'component';
|
|
48
48
|
filePath?: string;
|
|
49
49
|
}
|
|
50
|
+
export declare function stripTopLevelImports(source: string): string;
|
|
50
51
|
/**
|
|
51
52
|
* Parse a .html route file.
|
|
52
53
|
*
|
package/dist/compiler/parser.js
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
function getTopLevelImportStatements(source) {
|
|
3
|
+
const sourceFile = ts.createSourceFile('kuratchi-script.ts', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
4
|
+
const imports = [];
|
|
5
|
+
for (const statement of sourceFile.statements) {
|
|
6
|
+
if (!ts.isImportDeclaration(statement))
|
|
7
|
+
continue;
|
|
8
|
+
imports.push({
|
|
9
|
+
text: source.slice(statement.getStart(sourceFile), statement.getEnd()),
|
|
10
|
+
start: statement.getStart(sourceFile),
|
|
11
|
+
end: statement.getEnd(),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return imports;
|
|
15
|
+
}
|
|
16
|
+
export function stripTopLevelImports(source) {
|
|
17
|
+
const imports = getTopLevelImportStatements(source);
|
|
18
|
+
if (imports.length === 0)
|
|
19
|
+
return source.trim();
|
|
20
|
+
let cursor = 0;
|
|
21
|
+
let output = '';
|
|
22
|
+
for (const statement of imports) {
|
|
23
|
+
output += source.slice(cursor, statement.start);
|
|
24
|
+
cursor = statement.end;
|
|
25
|
+
}
|
|
26
|
+
output += source.slice(cursor);
|
|
27
|
+
return output.trim();
|
|
28
|
+
}
|
|
10
29
|
function hasReactiveLabel(scriptBody) {
|
|
11
30
|
return /\$\s*:/.test(scriptBody);
|
|
12
31
|
}
|
|
@@ -670,11 +689,8 @@ export function parseFile(source, options = {}) {
|
|
|
670
689
|
const workerEnvAliases = [];
|
|
671
690
|
const devAliases = [];
|
|
672
691
|
if (script) {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
let m;
|
|
676
|
-
while ((m = importRegex.exec(script)) !== null) {
|
|
677
|
-
const line = m[0].trim();
|
|
692
|
+
for (const statement of getTopLevelImportStatements(script)) {
|
|
693
|
+
const line = statement.text.trim();
|
|
678
694
|
// Check for component imports: import Name from '$lib/file.html' or '@kuratchi/ui/file.html'
|
|
679
695
|
const libMatch = line.match(/import\s+([A-Za-z_$][\w$]*)\s+from\s+['"]\$lib\/([^'"]+\.html)['"]/s);
|
|
680
696
|
const pkgMatch = !libMatch ? line.match(/import\s+([A-Za-z_$][\w$]*)\s+from\s+['"](@[^/'"]+\/[^/'"]+)\/([^'"]+\.html)['"]/s) : null;
|
|
@@ -707,10 +723,8 @@ export function parseFile(source, options = {}) {
|
|
|
707
723
|
}
|
|
708
724
|
}
|
|
709
725
|
if (clientScript) {
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
while ((m = importRegex.exec(clientScript)) !== null) {
|
|
713
|
-
const line = m[0].trim();
|
|
726
|
+
for (const statement of getTopLevelImportStatements(clientScript)) {
|
|
727
|
+
const line = statement.text.trim();
|
|
714
728
|
clientImports.push(line);
|
|
715
729
|
if (extractKuratchiEnvironmentDevAliases(line).length > 0) {
|
|
716
730
|
throw new Error(`[kuratchi compiler] ${options.filePath || kind}\nClient <script> blocks cannot import from @kuratchi/js/environment.\nUse route server script code and pass values into the template explicitly.`);
|
|
@@ -729,8 +743,7 @@ export function parseFile(source, options = {}) {
|
|
|
729
743
|
let scriptBody = '';
|
|
730
744
|
let loadFunction = null;
|
|
731
745
|
if (script) {
|
|
732
|
-
|
|
733
|
-
const rawScriptBody = script.replace(/^\s*import[\s\S]*?from\s+['"][^'"]+['"]\s*;?/gm, '').trim();
|
|
746
|
+
const rawScriptBody = stripTopLevelImports(script);
|
|
734
747
|
const explicitLoad = extractExplicitLoad(rawScriptBody);
|
|
735
748
|
scriptBody = explicitLoad.remainingScript;
|
|
736
749
|
loadFunction = explicitLoad.loadFunction;
|
|
@@ -773,7 +786,7 @@ export function parseFile(source, options = {}) {
|
|
|
773
786
|
}
|
|
774
787
|
}
|
|
775
788
|
// Also collect onX={fnName(...)} candidates (e.g. onclick, onClick, onChange)
|
|
776
|
-
//
|
|
789
|
+
// — the compiler will filter these against actual imports to determine server actions.
|
|
777
790
|
const eventActionRegex = /on[A-Za-z]+\s*=\{(\w+)\s*\(/g;
|
|
778
791
|
let em;
|
|
779
792
|
while ((em = eventActionRegex.exec(templateWithoutComments)) !== null) {
|
|
@@ -49,6 +49,8 @@ export function compileTemplate(template, componentNames, actionNames, rpcNameMa
|
|
|
49
49
|
let inStyle = false;
|
|
50
50
|
let inScript = false;
|
|
51
51
|
let scriptBuffer = [];
|
|
52
|
+
let inHtmlTag = false;
|
|
53
|
+
let htmlAttrQuote = null;
|
|
52
54
|
for (let i = 0; i < lines.length; i++) {
|
|
53
55
|
const line = lines[i];
|
|
54
56
|
const trimmed = line.trim();
|
|
@@ -87,6 +89,16 @@ export function compileTemplate(template, componentNames, actionNames, rpcNameMa
|
|
|
87
89
|
}
|
|
88
90
|
continue;
|
|
89
91
|
}
|
|
92
|
+
const startedInsideQuotedAttr = !!htmlAttrQuote;
|
|
93
|
+
const nextHtmlState = advanceHtmlTagState(line, inHtmlTag, htmlAttrQuote);
|
|
94
|
+
if (startedInsideQuotedAttr) {
|
|
95
|
+
out.push(`__html += \`${escapeLiteral(line)}\n\`;`);
|
|
96
|
+
inHtmlTag = nextHtmlState.inTag;
|
|
97
|
+
htmlAttrQuote = nextHtmlState.quote;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
inHtmlTag = nextHtmlState.inTag;
|
|
101
|
+
htmlAttrQuote = nextHtmlState.quote;
|
|
90
102
|
// Skip empty lines
|
|
91
103
|
if (!trimmed) {
|
|
92
104
|
out.push('__html += "\\n";');
|
|
@@ -167,6 +179,36 @@ export function compileTemplate(template, componentNames, actionNames, rpcNameMa
|
|
|
167
179
|
}
|
|
168
180
|
return out.join('\n');
|
|
169
181
|
}
|
|
182
|
+
function advanceHtmlTagState(src, startInTag, startQuote) {
|
|
183
|
+
let inTag = startInTag;
|
|
184
|
+
let quote = startQuote;
|
|
185
|
+
for (let i = 0; i < src.length; i++) {
|
|
186
|
+
const ch = src[i];
|
|
187
|
+
if (quote) {
|
|
188
|
+
if (ch === '\\') {
|
|
189
|
+
i++;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (ch === quote)
|
|
193
|
+
quote = null;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (!inTag) {
|
|
197
|
+
if (ch === '<') {
|
|
198
|
+
inTag = true;
|
|
199
|
+
}
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (ch === '"' || ch === "'") {
|
|
203
|
+
quote = ch;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (ch === '>') {
|
|
207
|
+
inTag = false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return { inTag, quote };
|
|
211
|
+
}
|
|
170
212
|
function transformClientScriptBlock(block) {
|
|
171
213
|
const match = block.match(/^([\s\S]*?<script\b[^>]*>)([\s\S]*?)(<\/script>\s*)$/i);
|
|
172
214
|
if (!match)
|
|
@@ -566,7 +608,7 @@ function compileHtmlLine(line, actionNames, rpcNameMap) {
|
|
|
566
608
|
let pos = 0;
|
|
567
609
|
let hasExpr = false;
|
|
568
610
|
while (pos < line.length) {
|
|
569
|
-
const braceIdx = line
|
|
611
|
+
const braceIdx = findNextTemplateBrace(line, pos);
|
|
570
612
|
if (braceIdx === -1) {
|
|
571
613
|
// No more braces — rest is literal
|
|
572
614
|
result += escapeLiteral(line.slice(pos));
|
|
@@ -777,13 +819,36 @@ function escapeLiteral(text) {
|
|
|
777
819
|
.replace(/`/g, '\\`')
|
|
778
820
|
.replace(/\$\{/g, '\\${');
|
|
779
821
|
}
|
|
822
|
+
function findNextTemplateBrace(src, startPos) {
|
|
823
|
+
return src.indexOf('{', startPos);
|
|
824
|
+
}
|
|
780
825
|
/** Find the matching closing `}` for an opening `{`, handling nesting. */
|
|
781
826
|
function findClosingBrace(src, openPos) {
|
|
782
827
|
let depth = 0;
|
|
828
|
+
let quote = null;
|
|
829
|
+
let escaped = false;
|
|
783
830
|
for (let i = openPos; i < src.length; i++) {
|
|
784
|
-
|
|
831
|
+
const ch = src[i];
|
|
832
|
+
if (quote) {
|
|
833
|
+
if (escaped) {
|
|
834
|
+
escaped = false;
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
if (ch === '\\') {
|
|
838
|
+
escaped = true;
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
if (ch === quote)
|
|
842
|
+
quote = null;
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
846
|
+
quote = ch;
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
if (ch === '{')
|
|
785
850
|
depth++;
|
|
786
|
-
if (
|
|
851
|
+
if (ch === '}') {
|
|
787
852
|
depth--;
|
|
788
853
|
if (depth === 0)
|
|
789
854
|
return i;
|
|
@@ -816,13 +881,13 @@ export function generateRenderFunction(template) {
|
|
|
816
881
|
const __esc = (v) => {
|
|
817
882
|
if (v == null) return '';
|
|
818
883
|
return String(v)
|
|
819
|
-
.replace(/&/g, '&')
|
|
820
|
-
.replace(/</g, '<')
|
|
821
|
-
.replace(/>/g, '>')
|
|
822
|
-
.replace(/"/g, '"')
|
|
823
|
-
.replace(/'/g, ''');
|
|
824
|
-
};
|
|
825
|
-
${body}
|
|
884
|
+
.replace(/&/g, '&')
|
|
885
|
+
.replace(/</g, '<')
|
|
886
|
+
.replace(/>/g, '>')
|
|
887
|
+
.replace(/"/g, '"')
|
|
888
|
+
.replace(/'/g, ''');
|
|
889
|
+
};
|
|
890
|
+
${body}
|
|
826
891
|
}`;
|
|
827
892
|
}
|
|
828
893
|
import { transpileTypeScript } from './transpile.js';
|
package/dist/create.js
CHANGED
|
@@ -633,7 +633,7 @@ ${types}
|
|
|
633
633
|
function genItemsCrud() {
|
|
634
634
|
return `import { env } from 'cloudflare:workers';
|
|
635
635
|
import { kuratchiORM } from '@kuratchi/orm';
|
|
636
|
-
import {
|
|
636
|
+
import { redirect } from '${FRAMEWORK_PACKAGE_NAME}';
|
|
637
637
|
import type { Item } from './schemas/app';
|
|
638
638
|
|
|
639
639
|
const db = kuratchiORM(() => (env as any).DB);
|
|
@@ -718,7 +718,7 @@ import {
|
|
|
718
718
|
parseSessionCookie,
|
|
719
719
|
} from '@kuratchi/auth';
|
|
720
720
|
import { getAuth } from '@kuratchi/auth';
|
|
721
|
-
import {
|
|
721
|
+
import { redirect } from '${FRAMEWORK_PACKAGE_NAME}';
|
|
722
722
|
import type { User } from '../schemas/app';
|
|
723
723
|
|
|
724
724
|
const db = kuratchiORM(() => (env as any).DB);
|
|
@@ -759,7 +759,7 @@ export async function signUp(formData: FormData): Promise<void> {
|
|
|
759
759
|
}
|
|
760
760
|
|
|
761
761
|
// Redirect to login after successful signup
|
|
762
|
-
|
|
762
|
+
redirect('/auth/login');
|
|
763
763
|
}
|
|
764
764
|
|
|
765
765
|
// ── Sign In ─────────────────────────────────────────────────
|
|
@@ -823,7 +823,7 @@ export async function signIn(formData: FormData): Promise<void> {
|
|
|
823
823
|
locals.__setCookieHeaders.push(setCookieHeader);
|
|
824
824
|
|
|
825
825
|
// Redirect to admin after successful login
|
|
826
|
-
|
|
826
|
+
redirect('/admin');
|
|
827
827
|
}
|
|
828
828
|
|
|
829
829
|
// ── Sign Out ────────────────────────────────────────────────
|
|
@@ -847,7 +847,7 @@ export async function signOut(formData: FormData): Promise<void> {
|
|
|
847
847
|
locals.__setCookieHeaders.push(clearHeader);
|
|
848
848
|
|
|
849
849
|
// Redirect to login after sign out
|
|
850
|
-
|
|
850
|
+
redirect('/auth/login');
|
|
851
851
|
}
|
|
852
852
|
|
|
853
853
|
// ── Get Current User ────────────────────────────────────────
|
package/dist/index.d.ts
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
export { createApp } from './runtime/app.js';
|
|
7
7
|
export { defineConfig } from './runtime/config.js';
|
|
8
8
|
export { defineRuntime } from './runtime/runtime.js';
|
|
9
|
-
export { getCtx, getEnv, getRequest, getLocals, getParams, getParam, redirect, goto, setBreadcrumbs, getBreadcrumbs, breadcrumbsHome, breadcrumbsPrev, breadcrumbsNext, breadcrumbsCurrent, buildDefaultBreadcrumbs, } from './runtime/context.js';
|
|
9
|
+
export { getCtx, getEnv, getRequest, getLocals, getParams, getParam, RedirectError, redirect, goto, setBreadcrumbs, getBreadcrumbs, breadcrumbsHome, breadcrumbsPrev, breadcrumbsNext, breadcrumbsCurrent, buildDefaultBreadcrumbs, } from './runtime/context.js';
|
|
10
10
|
export { kuratchiDO, doRpc, getDb } from './runtime/do.js';
|
|
11
11
|
export { ActionError } from './runtime/action.js';
|
|
12
12
|
export { PageError } from './runtime/page-error.js';
|
|
13
13
|
export { extractSubdomainSlug, extractSlugFromPrefix, matchContainerViewPath, rewriteProxyLocationHeader, buildContainerRequest, createContainerEnvVars, startContainer, proxyToContainer, handleContainerRouting, forwardJsonPostToContainerDO, matchSiteViewPath, buildSiteContainerRequest, createWpContainerEnvVars, startSiteContainer, proxyToSiteContainer, } from './runtime/containers.js';
|
|
14
14
|
export type { AppConfig, kuratchiConfig, DatabaseConfig, AuthConfig, RouteContext, RouteModule, RuntimeContext, RuntimeDefinition, RuntimeStep, RuntimeNext, RuntimeErrorResult, } from './runtime/types.js';
|
|
15
15
|
export type { RpcOf } from './runtime/do.js';
|
|
16
|
+
export { url, pathname, searchParams, headers, method, params, slug } from './runtime/request.js';
|
package/dist/index.js
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
export { createApp } from './runtime/app.js';
|
|
8
8
|
export { defineConfig } from './runtime/config.js';
|
|
9
9
|
export { defineRuntime } from './runtime/runtime.js';
|
|
10
|
-
export { getCtx, getEnv, getRequest, getLocals, getParams, getParam, redirect, goto, setBreadcrumbs, getBreadcrumbs, breadcrumbsHome, breadcrumbsPrev, breadcrumbsNext, breadcrumbsCurrent, buildDefaultBreadcrumbs, } from './runtime/context.js';
|
|
10
|
+
export { getCtx, getEnv, getRequest, getLocals, getParams, getParam, RedirectError, redirect, goto, setBreadcrumbs, getBreadcrumbs, breadcrumbsHome, breadcrumbsPrev, breadcrumbsNext, breadcrumbsCurrent, buildDefaultBreadcrumbs, } from './runtime/context.js';
|
|
11
11
|
export { kuratchiDO, doRpc, getDb } from './runtime/do.js';
|
|
12
12
|
export { ActionError } from './runtime/action.js';
|
|
13
13
|
export { PageError } from './runtime/page-error.js';
|
|
14
14
|
export { extractSubdomainSlug, extractSlugFromPrefix, matchContainerViewPath, rewriteProxyLocationHeader, buildContainerRequest, createContainerEnvVars, startContainer, proxyToContainer, handleContainerRouting, forwardJsonPostToContainerDO,
|
|
15
15
|
// Compatibility aliases
|
|
16
16
|
matchSiteViewPath, buildSiteContainerRequest, createWpContainerEnvVars, startSiteContainer, proxyToSiteContainer, } from './runtime/containers.js';
|
|
17
|
+
export { url, pathname, searchParams, headers, method, params, slug } from './runtime/request.js';
|
|
@@ -12,6 +12,12 @@ export interface BreadcrumbItem {
|
|
|
12
12
|
href?: string;
|
|
13
13
|
current?: boolean;
|
|
14
14
|
}
|
|
15
|
+
export declare class RedirectError extends Error {
|
|
16
|
+
readonly isRedirectError = true;
|
|
17
|
+
readonly location: string;
|
|
18
|
+
readonly status: number;
|
|
19
|
+
constructor(path: string, status?: number);
|
|
20
|
+
}
|
|
15
21
|
/** Called by the framework at the start of each request */
|
|
16
22
|
export declare function __setRequestContext(ctx: any, request: Request, env?: Record<string, any>): void;
|
|
17
23
|
/** Get the execution context (Worker: ExecutionContext, DO: DurableObjectState) */
|
|
@@ -28,11 +34,11 @@ export declare function getParams<T = Record<string, string>>(): T;
|
|
|
28
34
|
export declare function getParam(name: string): string | undefined;
|
|
29
35
|
/**
|
|
30
36
|
* Server-side redirect helper for actions/load logic.
|
|
31
|
-
*
|
|
37
|
+
* Throws a redirect signal consumed by the framework's PRG flow.
|
|
32
38
|
*/
|
|
33
|
-
export declare function redirect(path: string, status?: number):
|
|
39
|
+
export declare function redirect(path: string, status?: number): never;
|
|
34
40
|
/** Backward-compatible alias for redirect() */
|
|
35
|
-
export declare function goto(path: string, status?: number):
|
|
41
|
+
export declare function goto(path: string, status?: number): never;
|
|
36
42
|
export declare function setBreadcrumbs(items: BreadcrumbItem[]): void;
|
|
37
43
|
export declare function getBreadcrumbs(): BreadcrumbItem[];
|
|
38
44
|
export declare function breadcrumbsHome(label?: string, href?: string): BreadcrumbItem;
|
package/dist/runtime/context.js
CHANGED
|
@@ -8,16 +8,29 @@
|
|
|
8
8
|
* variables are safe and require no Node.js compat flags.
|
|
9
9
|
*/
|
|
10
10
|
import { __getDoSelf } from './do.js';
|
|
11
|
+
import { __setRequestParams, __setRequestState } from './request.js';
|
|
11
12
|
let __ctx = null;
|
|
12
13
|
let __request = null;
|
|
13
14
|
let __env = null;
|
|
14
15
|
let __locals = {};
|
|
16
|
+
export class RedirectError extends Error {
|
|
17
|
+
isRedirectError = true;
|
|
18
|
+
location;
|
|
19
|
+
status;
|
|
20
|
+
constructor(path, status = 303) {
|
|
21
|
+
super(`Redirect to ${path}`);
|
|
22
|
+
this.name = 'RedirectError';
|
|
23
|
+
this.location = path;
|
|
24
|
+
this.status = status;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
15
27
|
/** Called by the framework at the start of each request */
|
|
16
28
|
export function __setRequestContext(ctx, request, env) {
|
|
17
29
|
__ctx = ctx;
|
|
18
30
|
__request = request;
|
|
19
31
|
__env = env ?? null;
|
|
20
32
|
__locals = {};
|
|
33
|
+
__setRequestState(request);
|
|
21
34
|
// Expose context on globalThis for @kuratchi/auth and other packages
|
|
22
35
|
// Workers are single-threaded per request — this is safe
|
|
23
36
|
globalThis.__kuratchi_context__ = {
|
|
@@ -63,11 +76,12 @@ export function getParam(name) {
|
|
|
63
76
|
}
|
|
64
77
|
/**
|
|
65
78
|
* Server-side redirect helper for actions/load logic.
|
|
66
|
-
*
|
|
79
|
+
* Throws a redirect signal consumed by the framework's PRG flow.
|
|
67
80
|
*/
|
|
68
81
|
export function redirect(path, status = 303) {
|
|
69
82
|
__locals.__redirectTo = path;
|
|
70
83
|
__locals.__redirectStatus = status;
|
|
84
|
+
throw new RedirectError(path, status);
|
|
71
85
|
}
|
|
72
86
|
/** Backward-compatible alias for redirect() */
|
|
73
87
|
export function goto(path, status = 303) {
|
|
@@ -120,6 +134,8 @@ export function buildDefaultBreadcrumbs(pathname, _params = {}) {
|
|
|
120
134
|
/** Set a value on request-scoped locals (used by framework internals) */
|
|
121
135
|
export function __setLocal(key, value) {
|
|
122
136
|
__locals[key] = value;
|
|
137
|
+
if (key === 'params')
|
|
138
|
+
__setRequestParams(value);
|
|
123
139
|
}
|
|
124
140
|
/** Get the full locals object reference (used by framework internals) */
|
|
125
141
|
export function __getLocals() {
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -2,8 +2,9 @@ export { createApp } from './app.js';
|
|
|
2
2
|
export { defineConfig } from './config.js';
|
|
3
3
|
export { defineRuntime } from './runtime.js';
|
|
4
4
|
export { Router, filePathToPattern } from './router.js';
|
|
5
|
-
export { getCtx, getRequest, getLocals, getParams, getParam, redirect, goto, setBreadcrumbs, getBreadcrumbs, breadcrumbsHome, breadcrumbsPrev, breadcrumbsNext, breadcrumbsCurrent, buildDefaultBreadcrumbs, } from './context.js';
|
|
5
|
+
export { getCtx, getRequest, getLocals, getParams, getParam, RedirectError, redirect, goto, setBreadcrumbs, getBreadcrumbs, breadcrumbsHome, breadcrumbsPrev, breadcrumbsNext, breadcrumbsCurrent, buildDefaultBreadcrumbs, } from './context.js';
|
|
6
6
|
export { kuratchiDO, doRpc } from './do.js';
|
|
7
7
|
export { extractSubdomainSlug, extractSlugFromPrefix, matchContainerViewPath, rewriteProxyLocationHeader, buildContainerRequest, createContainerEnvVars, startContainer, proxyToContainer, handleContainerRouting, forwardJsonPostToContainerDO, matchSiteViewPath, buildSiteContainerRequest, createWpContainerEnvVars, startSiteContainer, proxyToSiteContainer, } from './containers.js';
|
|
8
8
|
export type { AppConfig, Env, AuthConfig, RouteContext, RouteModule, ApiRouteModule, HttpMethod, LayoutModule, RuntimeContext, RuntimeDefinition, RuntimeStep, RuntimeNext, RuntimeErrorResult, } from './types.js';
|
|
9
9
|
export type { RpcOf } from './do.js';
|
|
10
|
+
export { url, pathname, searchParams, headers, method, params, slug } from './request.js';
|
package/dist/runtime/index.js
CHANGED
|
@@ -2,8 +2,9 @@ export { createApp } from './app.js';
|
|
|
2
2
|
export { defineConfig } from './config.js';
|
|
3
3
|
export { defineRuntime } from './runtime.js';
|
|
4
4
|
export { Router, filePathToPattern } from './router.js';
|
|
5
|
-
export { getCtx, getRequest, getLocals, getParams, getParam, redirect, goto, setBreadcrumbs, getBreadcrumbs, breadcrumbsHome, breadcrumbsPrev, breadcrumbsNext, breadcrumbsCurrent, buildDefaultBreadcrumbs, } from './context.js';
|
|
5
|
+
export { getCtx, getRequest, getLocals, getParams, getParam, RedirectError, redirect, goto, setBreadcrumbs, getBreadcrumbs, breadcrumbsHome, breadcrumbsPrev, breadcrumbsNext, breadcrumbsCurrent, buildDefaultBreadcrumbs, } from './context.js';
|
|
6
6
|
export { kuratchiDO, doRpc } from './do.js';
|
|
7
7
|
export { extractSubdomainSlug, extractSlugFromPrefix, matchContainerViewPath, rewriteProxyLocationHeader, buildContainerRequest, createContainerEnvVars, startContainer, proxyToContainer, handleContainerRouting, forwardJsonPostToContainerDO,
|
|
8
8
|
// Compatibility aliases
|
|
9
9
|
matchSiteViewPath, buildSiteContainerRequest, createWpContainerEnvVars, startSiteContainer, proxyToSiteContainer, } from './containers.js';
|
|
10
|
+
export { url, pathname, searchParams, headers, method, params, slug } from './request.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare let url: URL;
|
|
2
|
+
export declare let pathname: string;
|
|
3
|
+
export declare let searchParams: URLSearchParams;
|
|
4
|
+
export declare let headers: Headers;
|
|
5
|
+
export declare let method: string;
|
|
6
|
+
export declare let params: Record<string, string>;
|
|
7
|
+
export declare let slug: string | undefined;
|
|
8
|
+
export declare function __setRequestState(request: Request): void;
|
|
9
|
+
export declare function __setRequestParams(nextParams: Record<string, string> | null | undefined): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export let url = new URL('http://localhost/');
|
|
2
|
+
export let pathname = '/';
|
|
3
|
+
export let searchParams = url.searchParams;
|
|
4
|
+
export let headers = new Headers();
|
|
5
|
+
export let method = 'GET';
|
|
6
|
+
export let params = {};
|
|
7
|
+
export let slug = undefined;
|
|
8
|
+
function __syncDerivedState() {
|
|
9
|
+
pathname = url.pathname;
|
|
10
|
+
searchParams = url.searchParams;
|
|
11
|
+
slug = params.slug ?? Object.values(params)[0];
|
|
12
|
+
}
|
|
13
|
+
export function __setRequestState(request) {
|
|
14
|
+
url = new URL(request.url);
|
|
15
|
+
headers = request.headers;
|
|
16
|
+
method = request.method;
|
|
17
|
+
params = {};
|
|
18
|
+
__syncDerivedState();
|
|
19
|
+
}
|
|
20
|
+
export function __setRequestParams(nextParams) {
|
|
21
|
+
params = nextParams ?? {};
|
|
22
|
+
__syncDerivedState();
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kuratchi/js",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "A thin, Cloudflare Workers-native web framework with Svelte-inspired syntax",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
"types": "./dist/runtime/context.d.ts",
|
|
29
29
|
"import": "./dist/runtime/context.js"
|
|
30
30
|
},
|
|
31
|
+
"./request": {
|
|
32
|
+
"types": "./dist/runtime/request.d.ts",
|
|
33
|
+
"import": "./dist/runtime/request.js"
|
|
34
|
+
},
|
|
31
35
|
"./runtime/do.js": {
|
|
32
36
|
"types": "./dist/runtime/do.d.ts",
|
|
33
37
|
"import": "./dist/runtime/do.js"
|