@kuratchi/js 0.0.8 → 0.0.10
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 +94 -4
- package/dist/cli.js +13 -5
- package/dist/compiler/index.d.ts +3 -3
- package/dist/compiler/index.js +1283 -962
- package/dist/compiler/parser.d.ts +6 -3
- package/dist/compiler/parser.js +67 -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 +9 -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[];
|
|
@@ -40,11 +40,14 @@ export interface ParsedFile {
|
|
|
40
40
|
loadReturnVars: string[];
|
|
41
41
|
/** Local aliases for Cloudflare Workers env imported from cloudflare:workers */
|
|
42
42
|
workerEnvAliases: string[];
|
|
43
|
+
/** Local aliases for dev imported from @kuratchi/js/environment */
|
|
44
|
+
devAliases: string[];
|
|
43
45
|
}
|
|
44
46
|
interface ParseFileOptions {
|
|
45
47
|
kind?: 'route' | 'layout' | 'component';
|
|
46
48
|
filePath?: string;
|
|
47
49
|
}
|
|
50
|
+
export declare function stripTopLevelImports(source: string): string;
|
|
48
51
|
/**
|
|
49
52
|
* Parse a .html route file.
|
|
50
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
|
}
|
|
@@ -158,6 +177,26 @@ function extractCloudflareEnvAliases(importLine) {
|
|
|
158
177
|
}
|
|
159
178
|
return aliases;
|
|
160
179
|
}
|
|
180
|
+
function extractKuratchiEnvironmentDevAliases(importLine) {
|
|
181
|
+
if (!/from\s+['"]@kuratchi\/js\/environment['"]/.test(importLine))
|
|
182
|
+
return [];
|
|
183
|
+
const namedMatch = importLine.match(/import\s*\{([\s\S]*?)\}\s*from\s+['"]@kuratchi\/js\/environment['"]/);
|
|
184
|
+
if (!namedMatch) {
|
|
185
|
+
throw new Error('[kuratchi compiler] @kuratchi/js/environment only supports named imports.');
|
|
186
|
+
}
|
|
187
|
+
const aliases = [];
|
|
188
|
+
for (const rawPart of splitTopLevel(namedMatch[1], ',')) {
|
|
189
|
+
const part = rawPart.trim();
|
|
190
|
+
if (!part)
|
|
191
|
+
continue;
|
|
192
|
+
const devMatch = part.match(/^dev(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
|
|
193
|
+
if (!devMatch) {
|
|
194
|
+
throw new Error('[kuratchi compiler] @kuratchi/js/environment currently only exports `dev`.');
|
|
195
|
+
}
|
|
196
|
+
aliases.push(devMatch[1] || 'dev');
|
|
197
|
+
}
|
|
198
|
+
return aliases;
|
|
199
|
+
}
|
|
161
200
|
function extractReturnObjectKeys(body) {
|
|
162
201
|
const keys = [];
|
|
163
202
|
let i = 0;
|
|
@@ -648,12 +687,10 @@ export function parseFile(source, options = {}) {
|
|
|
648
687
|
const clientImports = [];
|
|
649
688
|
const componentImports = {};
|
|
650
689
|
const workerEnvAliases = [];
|
|
690
|
+
const devAliases = [];
|
|
651
691
|
if (script) {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
let m;
|
|
655
|
-
while ((m = importRegex.exec(script)) !== null) {
|
|
656
|
-
const line = m[0].trim();
|
|
692
|
+
for (const statement of getTopLevelImportStatements(script)) {
|
|
693
|
+
const line = statement.text.trim();
|
|
657
694
|
// Check for component imports: import Name from '$lib/file.html' or '@kuratchi/ui/file.html'
|
|
658
695
|
const libMatch = line.match(/import\s+([A-Za-z_$][\w$]*)\s+from\s+['"]\$lib\/([^'"]+\.html)['"]/s);
|
|
659
696
|
const pkgMatch = !libMatch ? line.match(/import\s+([A-Za-z_$][\w$]*)\s+from\s+['"](@[^/'"]+\/[^/'"]+)\/([^'"]+\.html)['"]/s) : null;
|
|
@@ -669,6 +706,14 @@ export function parseFile(source, options = {}) {
|
|
|
669
706
|
componentImports[componentName] = `${pkg}:${fileName}`; // e.g. "@kuratchi/ui:badge"
|
|
670
707
|
}
|
|
671
708
|
else {
|
|
709
|
+
const devImportAliases = extractKuratchiEnvironmentDevAliases(line);
|
|
710
|
+
if (devImportAliases.length > 0) {
|
|
711
|
+
for (const alias of devImportAliases) {
|
|
712
|
+
if (!devAliases.includes(alias))
|
|
713
|
+
devAliases.push(alias);
|
|
714
|
+
}
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
672
717
|
serverImports.push(line);
|
|
673
718
|
}
|
|
674
719
|
for (const alias of extractCloudflareEnvAliases(line)) {
|
|
@@ -678,11 +723,12 @@ export function parseFile(source, options = {}) {
|
|
|
678
723
|
}
|
|
679
724
|
}
|
|
680
725
|
if (clientScript) {
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
while ((m = importRegex.exec(clientScript)) !== null) {
|
|
684
|
-
const line = m[0].trim();
|
|
726
|
+
for (const statement of getTopLevelImportStatements(clientScript)) {
|
|
727
|
+
const line = statement.text.trim();
|
|
685
728
|
clientImports.push(line);
|
|
729
|
+
if (extractKuratchiEnvironmentDevAliases(line).length > 0) {
|
|
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.`);
|
|
731
|
+
}
|
|
686
732
|
if (extractCloudflareEnvAliases(line).length > 0) {
|
|
687
733
|
throw buildEnvAccessError(kind, options.filePath, 'Client <script> blocks cannot import env from cloudflare:workers.');
|
|
688
734
|
}
|
|
@@ -697,8 +743,7 @@ export function parseFile(source, options = {}) {
|
|
|
697
743
|
let scriptBody = '';
|
|
698
744
|
let loadFunction = null;
|
|
699
745
|
if (script) {
|
|
700
|
-
|
|
701
|
-
const rawScriptBody = script.replace(/^\s*import[\s\S]*?from\s+['"][^'"]+['"]\s*;?/gm, '').trim();
|
|
746
|
+
const rawScriptBody = stripTopLevelImports(script);
|
|
702
747
|
const explicitLoad = extractExplicitLoad(rawScriptBody);
|
|
703
748
|
scriptBody = explicitLoad.remainingScript;
|
|
704
749
|
loadFunction = explicitLoad.loadFunction;
|
|
@@ -741,7 +786,7 @@ export function parseFile(source, options = {}) {
|
|
|
741
786
|
}
|
|
742
787
|
}
|
|
743
788
|
// Also collect onX={fnName(...)} candidates (e.g. onclick, onClick, onChange)
|
|
744
|
-
//
|
|
789
|
+
// — the compiler will filter these against actual imports to determine server actions.
|
|
745
790
|
const eventActionRegex = /on[A-Za-z]+\s*=\{(\w+)\s*\(/g;
|
|
746
791
|
let em;
|
|
747
792
|
while ((em = eventActionRegex.exec(templateWithoutComments)) !== null) {
|
|
@@ -813,6 +858,7 @@ export function parseFile(source, options = {}) {
|
|
|
813
858
|
clientImports,
|
|
814
859
|
loadReturnVars,
|
|
815
860
|
workerEnvAliases,
|
|
861
|
+
devAliases,
|
|
816
862
|
};
|
|
817
863
|
}
|
|
818
864
|
import { transpileTypeScript } from './transpile.js';
|
|
@@ -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;
|
|
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.10",
|
|
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"
|
|
@@ -36,6 +40,10 @@
|
|
|
36
40
|
"types": "./dist/compiler/index.d.ts",
|
|
37
41
|
"import": "./dist/compiler/index.js"
|
|
38
42
|
},
|
|
43
|
+
"./environment": {
|
|
44
|
+
"types": "./dist/index.d.ts",
|
|
45
|
+
"import": "./dist/index.js"
|
|
46
|
+
},
|
|
39
47
|
"./package.json": "./package.json"
|
|
40
48
|
},
|
|
41
49
|
"engines": {
|