@kuratchi/js 0.0.11 → 0.0.13

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kuratchi contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -54,6 +54,7 @@ Kuratchi routes are server-first.
54
54
  - Top-level route `<script>` blocks run on the server.
55
55
  - Template expressions, `if`, and `for` blocks render on the server.
56
56
  - `src/server` is for private server-only modules and reusable backend logic.
57
+ - `src/server/runtime.hook.ts` is the server runtime hook entrypoint for request interception.
57
58
  - Reactive `$:` code is the browser-only escape hatch.
58
59
 
59
60
  Route files are not client files. They are server-rendered routes that can opt into small browser-side reactive behavior when needed.
@@ -542,6 +543,38 @@ const postSlug = slug;
542
543
  - `slug` is `params.slug` when the matched route defines a `slug` param.
543
544
  - `headers`, `method`, and `params` are also exported from `@kuratchi/js/request`.
544
545
  - Use `getRequest()` when you want the raw native `Request` object.
546
+
547
+ ## Runtime Hook
548
+
549
+ Optional server runtime hook file. Export a `RuntimeDefinition` from `src/server/runtime.hook.ts`
550
+ to intercept requests before they reach the framework router. Use it for agent routing,
551
+ pre-route auth, or custom response/error handling.
552
+
553
+ ```ts
554
+ import type { RuntimeDefinition } from '@kuratchi/js';
555
+
556
+ const runtime: RuntimeDefinition = {
557
+ agents: {
558
+ async request(ctx, next) {
559
+ if (!ctx.url.pathname.startsWith('/agents/')) {
560
+ return next();
561
+ }
562
+
563
+ return new Response('Agent response');
564
+ },
565
+ },
566
+ };
567
+
568
+ export default runtime;
569
+ ```
570
+
571
+ `ctx` includes:
572
+
573
+ - `ctx.url` - parsed URL
574
+ - `ctx.request` - raw Request
575
+ - `ctx.env` - Cloudflare env bindings
576
+ - `next()` - pass control to the next handler
577
+
545
578
  ## Environment bindings
546
579
 
547
580
  Cloudflare env is server-only.
@@ -725,6 +725,8 @@ export function compile(options) {
725
725
  // 500.html receives `error` as a variable; others don't need it
726
726
  compiledErrorPages.set(status, `function __error_${status}(error) {\n ${body}\n return __html;\n}`);
727
727
  }
728
+ // Read assets prefix from kuratchi.config.ts (default: /assets/)
729
+ const assetsPrefix = readAssetsPrefix(projectDir);
728
730
  // Read kuratchi.config.ts at build time to discover ORM database configs
729
731
  const ormDatabases = readOrmConfig(projectDir);
730
732
  // Read auth config from kuratchi.config.ts
@@ -1262,6 +1264,7 @@ export function compile(options) {
1262
1264
  compiledLayoutActions,
1263
1265
  hasRuntime,
1264
1266
  runtimeImportPath,
1267
+ assetsPrefix,
1265
1268
  });
1266
1269
  // Write to .kuratchi/routes.js
1267
1270
  const outFile = options.outFile ?? path.join(projectDir, '.kuratchi', 'routes.js');
@@ -1668,7 +1671,9 @@ function buildRouteObject(opts) {
1668
1671
  explicitLoadFunction = transpileTypeScript(explicitLoadFunction, `route-load:${pattern}.ts`);
1669
1672
  }
1670
1673
  const scriptReturnVars = parsed.script
1671
- ? parsed.dataVars.filter((v) => !queryVars.includes(v))
1674
+ ? parsed.dataVars.filter((v) => !queryVars.includes(v) &&
1675
+ !parsed.actionFunctions.includes(v) &&
1676
+ !parsed.pollFunctions.includes(v))
1672
1677
  : [];
1673
1678
  // Load function �" internal server prepass for async route script bodies
1674
1679
  // and data-get query state hydration.
@@ -2047,6 +2052,21 @@ function resolveClassExportFromFile(absPath, errorLabel) {
2047
2052
  }
2048
2053
  throw new Error(`[kuratchi] ${errorLabel} must export a class via "export class X" or "export default class X". File: ${absPath}`);
2049
2054
  }
2055
+ function readAssetsPrefix(projectDir) {
2056
+ const configPath = path.join(projectDir, 'kuratchi.config.ts');
2057
+ if (!fs.existsSync(configPath))
2058
+ return '/assets/';
2059
+ const source = fs.readFileSync(configPath, 'utf-8');
2060
+ const match = source.match(/assetsPrefix\s*:\s*['"]([^'"]+)['"]/);
2061
+ if (!match)
2062
+ return '/assets/';
2063
+ let prefix = match[1];
2064
+ if (!prefix.startsWith('/'))
2065
+ prefix = '/' + prefix;
2066
+ if (!prefix.endsWith('/'))
2067
+ prefix += '/';
2068
+ return prefix;
2069
+ }
2050
2070
  function discoverConventionClassFiles(projectDir, dir, suffix, errorLabel) {
2051
2071
  const absDir = path.join(projectDir, dir);
2052
2072
  const files = discoverFilesWithSuffix(absDir, suffix);
@@ -2941,9 +2961,9 @@ ${migrationInit ? ' await __runMigrations();\n' : ''}${authInit ? ' __init
2941
2961
  const url = __runtimeCtx.url;
2942
2962
  ${ac?.hasRateLimit ? '\n // Rate limiting - check before route handlers\n { const __rlRes = await __checkRL(); if (__rlRes) return __secHeaders(__rlRes); }\n' : ''}${ac?.hasTurnstile ? ' // Turnstile bot protection\n { const __tsRes = await __checkTS(); if (__tsRes) return __secHeaders(__tsRes); }\n' : ''}${ac?.hasGuards ? ' // Route guards - redirect if not authenticated\n { const __gRes = __checkGuard(); if (__gRes) return __secHeaders(__gRes); }\n' : ''}
2943
2963
 
2944
- // Serve static assets from src/assets/ at /_assets/*
2945
- if (url.pathname.startsWith('/_assets/')) {
2946
- const name = url.pathname.slice('/_assets/'.length);
2964
+ // Serve static assets from src/assets/
2965
+ if (url.pathname.startsWith('${opts.assetsPrefix}')) {
2966
+ const name = url.pathname.slice('${opts.assetsPrefix}'.length);
2947
2967
  const asset = __assets[name];
2948
2968
  if (asset) {
2949
2969
  if (request.headers.get('if-none-match') === asset.etag) {
@@ -3124,12 +3144,7 @@ ${ac?.hasRateLimit ? '\n // Rate limiting - check before route handlers\n
3124
3144
  }
3125
3145
  function resolveRuntimeImportPath(projectDir) {
3126
3146
  const candidates = [
3127
- { file: 'src/kuratchi.runtime.ts', importPath: '../src/kuratchi.runtime' },
3128
- { file: 'src/kuratchi.runtime.js', importPath: '../src/kuratchi.runtime' },
3129
- { file: 'src/kuratchi.runtime.mjs', importPath: '../src/kuratchi.runtime' },
3130
- { file: 'kuratchi.runtime.ts', importPath: '../kuratchi.runtime' },
3131
- { file: 'kuratchi.runtime.js', importPath: '../kuratchi.runtime' },
3132
- { file: 'kuratchi.runtime.mjs', importPath: '../kuratchi.runtime' },
3147
+ { file: 'src/server/runtime.hook.ts', importPath: '../src/server/runtime.hook' },
3133
3148
  ];
3134
3149
  for (const candidate of candidates) {
3135
3150
  if (fs.existsSync(path.join(projectDir, candidate.file))) {
@@ -770,6 +770,21 @@ export function parseFile(source, options = {}) {
770
770
  if (!dataVars.includes(name))
771
771
  dataVars.push(name);
772
772
  }
773
+ // Server import named bindings are also data vars (available in templates)
774
+ for (const line of serverImports) {
775
+ const namesMatch = line.match(/import\s*\{([^}]+)\}/);
776
+ if (!namesMatch)
777
+ continue;
778
+ for (const part of namesMatch[1].split(',')) {
779
+ const trimmed = part.trim();
780
+ if (!trimmed)
781
+ continue;
782
+ const segments = trimmed.split(/\s+as\s+/);
783
+ const localName = (segments[1] || segments[0]).trim();
784
+ if (localName && !dataVars.includes(localName))
785
+ dataVars.push(localName);
786
+ }
787
+ }
773
788
  }
774
789
  const hasLoad = scriptBody.length > 0 || !!loadFunction;
775
790
  // Strip HTML comments from the template before scanning for action references.
package/package.json CHANGED
@@ -1,16 +1,18 @@
1
1
  {
2
2
  "name": "@kuratchi/js",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "A thin, Cloudflare Workers-native web framework with Svelte-inspired syntax",
5
+ "license": "MIT",
5
6
  "type": "module",
6
7
  "main": "./dist/index.js",
7
8
  "types": "./dist/index.d.ts",
8
9
  "bin": {
9
- "kuratchi": "./dist/cli.js"
10
+ "kuratchi": "dist/cli.js"
10
11
  },
11
12
  "files": [
12
13
  "dist",
13
- "README.md"
14
+ "README.md",
15
+ "LICENSE"
14
16
  ],
15
17
  "scripts": {
16
18
  "build": "tsc -p tsconfig.build.json",