toiljs 0.0.41 → 0.0.43
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 +19 -0
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +5 -2
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/generate.js +5 -2
- package/build/compiler/index.js +10 -1
- package/examples/basic/client/lib/useBrowserValue.ts +40 -0
- package/examples/basic/client/routes/auth.tsx +8 -4
- package/examples/basic/client/routes/cookies.tsx +12 -4
- package/examples/basic/client/routes/pq.tsx +8 -4
- package/examples/basic/server/main.ts +1 -0
- package/package.json +2 -2
- package/src/cli/create.ts +5 -2
- package/src/compiler/generate.ts +5 -2
- package/src/compiler/index.ts +13 -1
- package/examples/basic/server/AuthTestHandler.ts +0 -15
- package/examples/basic/server/AuthVerifyHandler.ts +0 -23
- package/examples/basic/server/CacheHandler.ts +0 -25
- package/examples/basic/server/DecoCache.ts +0 -18
- package/examples/basic/server/FastTrapHandler.ts +0 -8
- package/examples/basic/server/SpinHandler.ts +0 -18
- package/examples/basic/server/SsrGreetingRender.ts +0 -27
- package/examples/basic/server/authexample-main.ts +0 -8
- package/examples/basic/server/authtest-main.ts +0 -8
- package/examples/basic/server/authverify-main.ts +0 -8
- package/examples/basic/server/cache-main.ts +0 -8
- package/examples/basic/server/deco-main.ts +0 -18
- package/examples/basic/server/spin-main.ts +0 -13
- package/examples/basic/server/ssr/greeting.slots.ts +0 -19
- package/examples/basic/server/ssr-main.ts +0 -18
- package/examples/basic/server/trap-main.ts +0 -8
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
// surface plus HMAC signing and AES-256-GCM encryption, running in the server wasm.
|
|
4
4
|
// These controls call the `/api/cookies/*` routes in `server/core/AppHandler.ts`.
|
|
5
5
|
// Needs the server running to respond.
|
|
6
|
-
import {
|
|
6
|
+
import { useState, type CSSProperties } from 'react';
|
|
7
|
+
|
|
8
|
+
import { useBrowserValue } from '../lib/useBrowserValue';
|
|
7
9
|
|
|
8
10
|
export const metadata: Toil.Metadata = {
|
|
9
11
|
title: 'Cookies',
|
|
@@ -41,6 +43,11 @@ const card: CSSProperties = {
|
|
|
41
43
|
};
|
|
42
44
|
const label: CSSProperties = { opacity: 0.7, fontSize: '0.8rem', marginTop: 6 };
|
|
43
45
|
|
|
46
|
+
/** The cookies JS can read (HttpOnly cookies are absent from `document.cookie`). */
|
|
47
|
+
function readJsCookies(): string {
|
|
48
|
+
return document.cookie || '(nothing visible to JS)';
|
|
49
|
+
}
|
|
50
|
+
|
|
44
51
|
export default function CookiesDemo() {
|
|
45
52
|
const [gallery, setGallery] = useState<Record<string, string> | null>(null);
|
|
46
53
|
const [setResp, setSetResp] = useState<SetResp | null>(null);
|
|
@@ -48,11 +55,11 @@ export default function CookiesDemo() {
|
|
|
48
55
|
const [cleared, setCleared] = useState<string[] | null>(null);
|
|
49
56
|
const [seal, setSeal] = useState<SealResp | null>(null);
|
|
50
57
|
const [sealInput, setSealInput] = useState('hello toiljs');
|
|
51
|
-
const [jsCookies, setJsCookies] = useState('');
|
|
52
58
|
const [err, setErr] = useState('');
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
// Hydration-safe: '' on the server and first paint, the live `document.cookie`
|
|
61
|
+
// after mount; `readJs()` re-reads after a Set/Clear/Seal action.
|
|
62
|
+
const [jsCookies, readJs] = useBrowserValue(readJsCookies, '');
|
|
56
63
|
|
|
57
64
|
const guard = async (fn: () => Promise<void>): Promise<void> => {
|
|
58
65
|
setErr('');
|
|
@@ -62,6 +69,7 @@ export default function CookiesDemo() {
|
|
|
62
69
|
setErr(String(e));
|
|
63
70
|
}
|
|
64
71
|
};
|
|
72
|
+
|
|
65
73
|
const getJSON = async <T,>(url: string): Promise<T> => {
|
|
66
74
|
const res = await fetch(url);
|
|
67
75
|
if (!res.ok) throw new Error(`${url} -> ${String(res.status)}`);
|
|
@@ -10,11 +10,13 @@
|
|
|
10
10
|
// swap in its own. It still isn't the full production login (no single-use
|
|
11
11
|
// consume -> within the TTL a captured proof could be replayed; that needs a
|
|
12
12
|
// store) -- see Auth.login / server/routes/Auth.ts and docs/auth.md.
|
|
13
|
-
import { useCallback,
|
|
13
|
+
import { useCallback, useState } from 'react';
|
|
14
14
|
|
|
15
15
|
import { Auth, type IdentityProof } from 'toiljs/client';
|
|
16
16
|
import { Account } from 'shared/server';
|
|
17
17
|
|
|
18
|
+
import { useBrowserValue } from '../lib/useBrowserValue';
|
|
19
|
+
|
|
18
20
|
/** Read the readable companion cookie under either name (HTTP `toil_user` or
|
|
19
21
|
* HTTPS `__Secure-toil_user`) and decode it. Display-only / untrusted. */
|
|
20
22
|
function readCompanion(): Account | null {
|
|
@@ -28,6 +30,7 @@ function readCompanion(): Account | null {
|
|
|
28
30
|
break;
|
|
29
31
|
}
|
|
30
32
|
}
|
|
33
|
+
|
|
31
34
|
if (raw === null) return null;
|
|
32
35
|
try {
|
|
33
36
|
let b = raw.replace(/-/g, '+').replace(/_/g, '/');
|
|
@@ -81,11 +84,11 @@ export default function Pq(): React.JSX.Element {
|
|
|
81
84
|
const [busy, setBusy] = useState(false);
|
|
82
85
|
const [proof, setProof] = useState<IdentityProof | null>(null);
|
|
83
86
|
const [result, setResult] = useState<Result | null>(null);
|
|
84
|
-
const [companion, setCompanion] = useState<Account | null>(null);
|
|
85
87
|
const [verified, setVerified] = useState<VerifiedUser | null | 'none'>(null);
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
// Hydration-safe: null on the server and first paint, the live companion
|
|
90
|
+
// cookie after mount; `refreshCompanion()` re-reads after a PQ login.
|
|
91
|
+
const [companion, refreshCompanion] = useBrowserValue(readCompanion, null);
|
|
89
92
|
|
|
90
93
|
const prove = useCallback(
|
|
91
94
|
async (doTamper: boolean) => {
|
|
@@ -118,6 +121,7 @@ export default function Pq(): React.JSX.Element {
|
|
|
118
121
|
setVerified('none');
|
|
119
122
|
return;
|
|
120
123
|
}
|
|
124
|
+
|
|
121
125
|
const r = new DataReader(new Uint8Array(await res.arrayBuffer()));
|
|
122
126
|
setVerified({ username: r.readString(), admin: r.readBool(), score: r.readU64().toString() });
|
|
123
127
|
} finally {
|
|
@@ -6,6 +6,7 @@ import { AppHandler } from './core/AppHandler';
|
|
|
6
6
|
// Surface modules: @rest routes and @service/@remote RPC. `toiljs build` discovers every
|
|
7
7
|
// decorated file under server/ on its own; importing them here keeps a direct `toilscript`
|
|
8
8
|
// run (which only sees the toilconfig entries) building the exact same server.
|
|
9
|
+
import './routes/Auth';
|
|
9
10
|
import './routes/Players';
|
|
10
11
|
import './routes/Leaderboard';
|
|
11
12
|
import './routes/Session';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toiljs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.43",
|
|
5
5
|
"author": "Dacely",
|
|
6
6
|
"description": "The modern React framework: a file-based React frontend and a ToilScript-compiled WebAssembly backend.",
|
|
7
7
|
"repository": {
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
"juice": "^12.1.0",
|
|
130
130
|
"picocolors": "^1.1.1",
|
|
131
131
|
"sharp": "^0.35.0",
|
|
132
|
-
"toilscript": "^0.1.
|
|
132
|
+
"toilscript": "^0.1.25",
|
|
133
133
|
"typescript-eslint": "^8.60.0",
|
|
134
134
|
"vite": "^8.0.14",
|
|
135
135
|
"vite-imagetools": "^10.0.0",
|
package/src/cli/create.ts
CHANGED
|
@@ -295,6 +295,7 @@ declare const Cookies: typeof import('toiljs/server/runtime/http/cookies').Cooki
|
|
|
295
295
|
type Cookies = import('toiljs/server/runtime/http/cookies').Cookies;
|
|
296
296
|
declare const SecureCookies: typeof import('toiljs/server/runtime/http/securecookies').SecureCookies;
|
|
297
297
|
type SecureCookies = import('toiljs/server/runtime/http/securecookies').SecureCookies;
|
|
298
|
+
declare const Time: typeof import('toiljs/server/runtime/time').Time;
|
|
298
299
|
// Email, rate-limit, 2FA, and auth globals (server/globals/*), hand-declared
|
|
299
300
|
// because their AssemblyScript source can't be type-aliased from tsc.
|
|
300
301
|
declare enum EmailStatus { Sent, Disabled, Budget, RecipientCapped, Deduped, TryLater, BadRecipient, ProviderError }
|
|
@@ -302,10 +303,12 @@ declare namespace EmailService { function send(to: string, subject: string, body
|
|
|
302
303
|
declare class RenderedEmail { subject: string; body: string; html: string; constructor(subject: string, body: string, html: string); }
|
|
303
304
|
declare class EmailTemplate { constructor(subject: string, body: string, html?: string); render(vars: Map<string, string>): RenderedEmail; send(to: string, vars: Map<string, string>, purpose?: string): EmailStatus; }
|
|
304
305
|
declare enum RateLimit { FixedWindow, SlidingWindow, TokenBucket }
|
|
306
|
+
declare namespace RateLimitService { function guard(routeId: i32, strategy: i32, limit: i32, window: i32): import('toiljs/server/runtime/response').Response | null; function guardKeyed(routeId: i32, strategy: i32, limit: i32, window: i32, key: string): import('toiljs/server/runtime/response').Response | null; }
|
|
305
307
|
declare class TwoFactorIssue { code: string; token: string; constructor(code: string, token: string); }
|
|
306
308
|
declare class TwoFactorChallenge { token: string; status: EmailStatus; constructor(token: string, status: EmailStatus); }
|
|
307
|
-
declare namespace TwoFactor { function setSecret(secret: Uint8Array): void; function issue(recipient: string, purpose: string, ttlSecs?: u64, digits?: i32): TwoFactorIssue; function send(recipient: string, purpose: string, ttlSecs?: u64, digits?: i32): TwoFactorChallenge; function verify(token: string, recipient: string, code: string): bool; }
|
|
308
|
-
declare namespace AuthService { const SESSION_COOKIE: string; const USER_COOKIE: string; const LOGIN_CONTEXT: string; const PUBLIC_KEY_LEN: i32; const SIGNATURE_LEN: i32; const DEFAULT_SESSION_TTL_SECS: u64; function setSecret(secret: Uint8Array): void; function hasSession(): bool; function getSessionBytes(): Uint8Array | null; function mintSession(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearSession(): Cookie; function userCookie(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearUserCookie(): Cookie; function buildLoginMessage(sub: string, aud: string, cid: Uint8Array, nonce: Uint8Array, iat: u64, exp: u64): Uint8Array; function verifyLogin(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): bool; }
|
|
309
|
+
declare namespace TwoFactor { const DEFAULT_TTL_SECS: u64; const DEFAULT_DIGITS: i32; function setSecret(secret: Uint8Array): void; function issue(recipient: string, purpose: string, ttlSecs?: u64, digits?: i32): TwoFactorIssue; function send(recipient: string, purpose: string, ttlSecs?: u64, digits?: i32): TwoFactorChallenge; function verify(token: string, recipient: string, code: string): bool; }
|
|
310
|
+
declare namespace AuthService { const SESSION_COOKIE: string; const USER_COOKIE: string; const LOGIN_CONTEXT: string; const PUBLIC_KEY_LEN: i32; const SIGNATURE_LEN: i32; const DEFAULT_SESSION_TTL_SECS: u64; function setSecret(secret: Uint8Array): void; function hasSession(): bool; function getSessionBytes(): Uint8Array | null; function getUser(): __ToilAuthUser | null; function mintSession(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearSession(): Cookie; function userCookie(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearUserCookie(): Cookie; function buildLoginMessage(sub: string, aud: string, cid: Uint8Array, nonce: Uint8Array, iat: u64, exp: u64): Uint8Array; function verifyLogin(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): bool; }
|
|
311
|
+
interface __ToilAuthUser {}
|
|
309
312
|
`;
|
|
310
313
|
|
|
311
314
|
/**
|
package/src/compiler/generate.ts
CHANGED
|
@@ -108,6 +108,7 @@ export const TOIL_SERVER_ENV_DTS =
|
|
|
108
108
|
`type Cookies = import('toiljs/server/runtime/http/cookies').Cookies;\n` +
|
|
109
109
|
`declare const SecureCookies: typeof import('toiljs/server/runtime/http/securecookies').SecureCookies;\n` +
|
|
110
110
|
`type SecureCookies = import('toiljs/server/runtime/http/securecookies').SecureCookies;\n` +
|
|
111
|
+
`declare const Time: typeof import('toiljs/server/runtime/time').Time;\n` +
|
|
111
112
|
`// Email, rate-limit, 2FA, and auth globals (server/globals/*), hand-declared\n` +
|
|
112
113
|
`// because their AssemblyScript source can't be type-aliased from tsc.\n` +
|
|
113
114
|
`declare enum EmailStatus { Sent, Disabled, Budget, RecipientCapped, Deduped, TryLater, BadRecipient, ProviderError }\n` +
|
|
@@ -115,10 +116,12 @@ export const TOIL_SERVER_ENV_DTS =
|
|
|
115
116
|
`declare class RenderedEmail { subject: string; body: string; html: string; constructor(subject: string, body: string, html: string); }\n` +
|
|
116
117
|
`declare class EmailTemplate { constructor(subject: string, body: string, html?: string); render(vars: Map<string, string>): RenderedEmail; send(to: string, vars: Map<string, string>, purpose?: string): EmailStatus; }\n` +
|
|
117
118
|
`declare enum RateLimit { FixedWindow, SlidingWindow, TokenBucket }\n` +
|
|
119
|
+
`declare namespace RateLimitService { function guard(routeId: i32, strategy: i32, limit: i32, window: i32): import('toiljs/server/runtime/response').Response | null; function guardKeyed(routeId: i32, strategy: i32, limit: i32, window: i32, key: string): import('toiljs/server/runtime/response').Response | null; }\n` +
|
|
118
120
|
`declare class TwoFactorIssue { code: string; token: string; constructor(code: string, token: string); }\n` +
|
|
119
121
|
`declare class TwoFactorChallenge { token: string; status: EmailStatus; constructor(token: string, status: EmailStatus); }\n` +
|
|
120
|
-
`declare namespace TwoFactor { function setSecret(secret: Uint8Array): void; function issue(recipient: string, purpose: string, ttlSecs?: u64, digits?: i32): TwoFactorIssue; function send(recipient: string, purpose: string, ttlSecs?: u64, digits?: i32): TwoFactorChallenge; function verify(token: string, recipient: string, code: string): bool; }\n` +
|
|
121
|
-
`declare namespace AuthService { const SESSION_COOKIE: string; const USER_COOKIE: string; const LOGIN_CONTEXT: string; const PUBLIC_KEY_LEN: i32; const SIGNATURE_LEN: i32; const DEFAULT_SESSION_TTL_SECS: u64; function setSecret(secret: Uint8Array): void; function hasSession(): bool; function getSessionBytes(): Uint8Array | null; function mintSession(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearSession(): Cookie; function userCookie(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearUserCookie(): Cookie; function buildLoginMessage(sub: string, aud: string, cid: Uint8Array, nonce: Uint8Array, iat: u64, exp: u64): Uint8Array; function verifyLogin(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): bool; }\n
|
|
122
|
+
`declare namespace TwoFactor { const DEFAULT_TTL_SECS: u64; const DEFAULT_DIGITS: i32; function setSecret(secret: Uint8Array): void; function issue(recipient: string, purpose: string, ttlSecs?: u64, digits?: i32): TwoFactorIssue; function send(recipient: string, purpose: string, ttlSecs?: u64, digits?: i32): TwoFactorChallenge; function verify(token: string, recipient: string, code: string): bool; }\n` +
|
|
123
|
+
`declare namespace AuthService { const SESSION_COOKIE: string; const USER_COOKIE: string; const LOGIN_CONTEXT: string; const PUBLIC_KEY_LEN: i32; const SIGNATURE_LEN: i32; const DEFAULT_SESSION_TTL_SECS: u64; function setSecret(secret: Uint8Array): void; function hasSession(): bool; function getSessionBytes(): Uint8Array | null; function getUser(): __ToilAuthUser | null; function mintSession(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearSession(): Cookie; function userCookie(userData: Uint8Array, ttlSecs?: u64): Cookie; function clearUserCookie(): Cookie; function buildLoginMessage(sub: string, aud: string, cid: Uint8Array, nonce: Uint8Array, iat: u64, exp: u64): Uint8Array; function verifyLogin(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): bool; }\n` +
|
|
124
|
+
`interface __ToilAuthUser {}\n`;
|
|
122
125
|
|
|
123
126
|
/**
|
|
124
127
|
* Returns a `./`-prefixed, **extensionless** POSIX module specifier from `.toil` to `abs`, for use
|
package/src/compiler/index.ts
CHANGED
|
@@ -143,7 +143,19 @@ async function buildServer(root: string): Promise<void> {
|
|
|
143
143
|
// Explicit entries (every server file) override the toilconfig entries; the target options
|
|
144
144
|
// (optimization, features, runtime) still come from the toilconfig's `release` target.
|
|
145
145
|
const files = serverEntryFiles(root);
|
|
146
|
-
|
|
146
|
+
// Suppress AS235 ("only variables/functions/enums become wasm exports"): a
|
|
147
|
+
// `@data`/`@rest` class is intentionally `export class` (so other server
|
|
148
|
+
// files import it), but never a wasm export — the warning is pure noise here.
|
|
149
|
+
const args = [
|
|
150
|
+
binJs,
|
|
151
|
+
...files,
|
|
152
|
+
'--target',
|
|
153
|
+
'release',
|
|
154
|
+
'--rpcModule',
|
|
155
|
+
'shared/server.ts',
|
|
156
|
+
'--disableWarning',
|
|
157
|
+
'235',
|
|
158
|
+
];
|
|
147
159
|
|
|
148
160
|
await new Promise<void>((resolve, reject) => {
|
|
149
161
|
const child = spawn(process.execPath, args, { cwd: root, stdio: 'inherit' });
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Request, Response, ToilHandler } from 'toiljs/server/runtime';
|
|
2
|
-
|
|
3
|
-
// AuthService is used with NO import (a global via toilscript --lib).
|
|
4
|
-
export class AuthTestHandler extends ToilHandler {
|
|
5
|
-
public handle(req: Request): Response {
|
|
6
|
-
const cid = new Uint8Array(16);
|
|
7
|
-
const nonce = new Uint8Array(32);
|
|
8
|
-
const msg = AuthService.buildLoginMessage('alice', 'toil-demo', cid, nonce, 1000, 2000);
|
|
9
|
-
// Dummy pk/sig -> verify must be false (also exercises the host import binding).
|
|
10
|
-
const pk = new Uint8Array(AuthService.PUBLIC_KEY_LEN);
|
|
11
|
-
const sig = new Uint8Array(AuthService.SIGNATURE_LEN);
|
|
12
|
-
const ok = AuthService.verifyLogin(pk, msg, sig);
|
|
13
|
-
return Response.text('msglen=' + msg.length.toString() + ' verify=' + (ok ? '1' : '0') + '\n');
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Method, Request, Response, ToilHandler } from 'toiljs/server/runtime';
|
|
2
|
-
import { DataReader } from 'data';
|
|
3
|
-
|
|
4
|
-
// Reads {sub, aud, cid, nonce, iat, exp, pk, sig} as a binary body, rebuilds
|
|
5
|
-
// the login message with the AS AuthService (server-authoritative encoding),
|
|
6
|
-
// and verifies the client signature. Proves the full client->edge chain.
|
|
7
|
-
export class AuthVerifyHandler extends ToilHandler {
|
|
8
|
-
public handle(req: Request): Response {
|
|
9
|
-
if (req.method != Method.POST) return Response.empty(405);
|
|
10
|
-
const r = new DataReader(req.body);
|
|
11
|
-
const sub = r.readString();
|
|
12
|
-
const aud = r.readString();
|
|
13
|
-
const cid = r.readBytes();
|
|
14
|
-
const nonce = r.readBytes();
|
|
15
|
-
const iat = r.readU64();
|
|
16
|
-
const exp = r.readU64();
|
|
17
|
-
const pk = r.readBytes();
|
|
18
|
-
const sig = r.readBytes();
|
|
19
|
-
const msg = AuthService.buildLoginMessage(sub, aud, cid, nonce, iat, exp);
|
|
20
|
-
const ok = AuthService.verifyLogin(pk, msg, sig);
|
|
21
|
-
return Response.text((ok ? '1' : '0') + '\n');
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Method, Request, Response, ToilHandler } from 'toiljs/server/runtime';
|
|
2
|
-
|
|
3
|
-
// Exercises the tenant-directed cache. Detection is via the host's
|
|
4
|
-
// `Toil-Cache` response header (MISS on first compute+store, HIT on reuse,
|
|
5
|
-
// DYNAMIC when not cached), so the body need not vary.
|
|
6
|
-
export class CacheHandler extends ToilHandler {
|
|
7
|
-
public handle(req: Request): Response {
|
|
8
|
-
if (req.method == Method.GET && req.path == '/cacheable') {
|
|
9
|
-
// edge-cache 5 min + browser Cache-Control max-age=60
|
|
10
|
-
return Response.json('{"cached":true}').cache(5, 60);
|
|
11
|
-
}
|
|
12
|
-
if (req.method == Method.GET && req.path == '/auth-ok') {
|
|
13
|
-
// edge-cache even when the request carries auth (allowAuth)
|
|
14
|
-
return Response.json('{"authok":true}').cache(5, 0, false, true);
|
|
15
|
-
}
|
|
16
|
-
if (req.method == Method.GET && req.path == '/uncacheable') {
|
|
17
|
-
return Response.json('{"cached":false}');
|
|
18
|
-
}
|
|
19
|
-
if (req.method == Method.POST && req.path == '/echo') {
|
|
20
|
-
// cache per (path, body): same body hits, different body misses
|
|
21
|
-
return Response.bytes(req.body).cache(5);
|
|
22
|
-
}
|
|
23
|
-
return Response.notFound();
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Response, RouteContext } from 'toiljs/server/runtime';
|
|
2
|
-
|
|
3
|
-
// @rest controller exercising the new compile-time @cache decorator.
|
|
4
|
-
@rest('deco')
|
|
5
|
-
class DecoCache {
|
|
6
|
-
// edge-cache 5 min + browser Cache-Control max-age=60, via the decorator
|
|
7
|
-
@get('/cached')
|
|
8
|
-
@cache(5, 60)
|
|
9
|
-
public cached(ctx: RouteContext): Response {
|
|
10
|
-
return Response.json('{"deco":"cached"}');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// no @cache -> never cached
|
|
14
|
-
@get('/plain')
|
|
15
|
-
public plain(ctx: RouteContext): Response {
|
|
16
|
-
return Response.json('{"deco":"plain"}');
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Request, Response, ToilHandler } from 'toiljs/server/runtime';
|
|
2
|
-
|
|
3
|
-
export class FastTrapHandler extends ToilHandler {
|
|
4
|
-
public handle(req: Request): Response {
|
|
5
|
-
unreachable(); // wasm `unreachable` -> instant trap, ~0 gas
|
|
6
|
-
return Response.text('x');
|
|
7
|
-
}
|
|
8
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Request, Response, ToilHandler } from 'toiljs/server/runtime';
|
|
2
|
-
|
|
3
|
-
// Module-level counter so the loop body has an observable side effect the
|
|
4
|
-
// optimizer cannot remove (and even a bare loop would still be gas-metered).
|
|
5
|
-
let counter: i64 = 0;
|
|
6
|
-
|
|
7
|
-
export class SpinHandler extends ToilHandler {
|
|
8
|
-
public handle(req: Request): Response {
|
|
9
|
-
// Infinite CPU burn on EVERY request. The edge's per-request gas
|
|
10
|
-
// budget (MAX_GAS_WASM_INIT) must trap this and 502 instead of
|
|
11
|
-
// freezing the worker.
|
|
12
|
-
while (true) {
|
|
13
|
-
counter = counter + 1;
|
|
14
|
-
}
|
|
15
|
-
// unreachable
|
|
16
|
-
return Response.text('unreachable\n');
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A hand-written edge-SSR `render` for the `/hello` route, authored against the
|
|
3
|
-
* generated typed `Slot` enum + `HASH`. It derives its data from the request
|
|
4
|
-
* and fills the holes; the host splices these values into the precompiled
|
|
5
|
-
* template. Registers itself with the `Ssr` router (compiler-injected in a real
|
|
6
|
-
* build; explicit here).
|
|
7
|
-
*/
|
|
8
|
-
import { Request } from 'toiljs/server/runtime';
|
|
9
|
-
import { HtmlBuilder, SlotValues, Ssr } from 'toiljs/server/runtime';
|
|
10
|
-
import { HASH, Slot } from './ssr/greeting.slots';
|
|
11
|
-
|
|
12
|
-
function renderGreeting(req: Request): SlotValues | null {
|
|
13
|
-
if (req.path != '/hello') return null;
|
|
14
|
-
const v = new SlotValues(HASH);
|
|
15
|
-
// A text hole: React-escaped (note the `&` and `<>` get entities).
|
|
16
|
-
v.setText(Slot.greeting, 'world & <friends>');
|
|
17
|
-
// A repeat region: three stamped rows.
|
|
18
|
-
const rows = new HtmlBuilder();
|
|
19
|
-
const items: string[] = ['a & b', '<c>', 'd'];
|
|
20
|
-
for (let i = 0; i < items.length; i++) {
|
|
21
|
-
rows.raw('<li>').text(items[i]).raw('</li>');
|
|
22
|
-
}
|
|
23
|
-
v.setRepeat(Slot.count, rows);
|
|
24
|
-
return v;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
Ssr.register(renderGreeting);
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Server } from 'toiljs/server/runtime';
|
|
2
|
-
import { revertOnError } from 'toiljs/server/runtime/abort/abort';
|
|
3
|
-
import { Request, Response, Rest, ToilHandler } from 'toiljs/server/runtime';
|
|
4
|
-
import './routes/Auth';
|
|
5
|
-
class H extends ToilHandler { public handle(req: Request): Response { const h = Rest.dispatch(req); return h != null ? h : Response.notFound(); } }
|
|
6
|
-
Server.handler = () => { return new H(); };
|
|
7
|
-
export * from 'toiljs/server/runtime/exports';
|
|
8
|
-
export function abort(m: string, f: string, l: u32, c: u32): void { revertOnError(m, f, l, c); }
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Server } from 'toiljs/server/runtime';
|
|
2
|
-
import { revertOnError } from 'toiljs/server/runtime/abort/abort';
|
|
3
|
-
import { AuthTestHandler } from './AuthTestHandler';
|
|
4
|
-
Server.handler = () => { return new AuthTestHandler(); };
|
|
5
|
-
export * from 'toiljs/server/runtime/exports';
|
|
6
|
-
export function abort(message: string, fileName: string, line: u32, column: u32): void {
|
|
7
|
-
revertOnError(message, fileName, line, column);
|
|
8
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Server } from 'toiljs/server/runtime';
|
|
2
|
-
import { revertOnError } from 'toiljs/server/runtime/abort/abort';
|
|
3
|
-
import { AuthVerifyHandler } from './AuthVerifyHandler';
|
|
4
|
-
Server.handler = () => { return new AuthVerifyHandler(); };
|
|
5
|
-
export * from 'toiljs/server/runtime/exports';
|
|
6
|
-
export function abort(message: string, fileName: string, line: u32, column: u32): void {
|
|
7
|
-
revertOnError(message, fileName, line, column);
|
|
8
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Server } from 'toiljs/server/runtime';
|
|
2
|
-
import { revertOnError } from 'toiljs/server/runtime/abort/abort';
|
|
3
|
-
import { CacheHandler } from './CacheHandler';
|
|
4
|
-
Server.handler = () => { return new CacheHandler(); };
|
|
5
|
-
export * from 'toiljs/server/runtime/exports';
|
|
6
|
-
export function abort(message: string, fileName: string, line: u32, column: u32): void {
|
|
7
|
-
revertOnError(message, fileName, line, column);
|
|
8
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Server } from 'toiljs/server/runtime';
|
|
2
|
-
import { revertOnError } from 'toiljs/server/runtime/abort/abort';
|
|
3
|
-
import { Request, Response, Rest, ToilHandler } from 'toiljs/server/runtime';
|
|
4
|
-
import './DecoCache';
|
|
5
|
-
|
|
6
|
-
class DecoHandler extends ToilHandler {
|
|
7
|
-
public handle(req: Request): Response {
|
|
8
|
-
const hit = Rest.dispatch(req);
|
|
9
|
-
if (hit != null) return hit;
|
|
10
|
-
return Response.notFound();
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
Server.handler = () => { return new DecoHandler(); };
|
|
15
|
-
export * from 'toiljs/server/runtime/exports';
|
|
16
|
-
export function abort(message: string, fileName: string, line: u32, column: u32): void {
|
|
17
|
-
revertOnError(message, fileName, line, column);
|
|
18
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Server } from 'toiljs/server/runtime';
|
|
2
|
-
import { revertOnError } from 'toiljs/server/runtime/abort/abort';
|
|
3
|
-
import { SpinHandler } from './SpinHandler';
|
|
4
|
-
|
|
5
|
-
Server.handler = () => {
|
|
6
|
-
return new SpinHandler();
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export * from 'toiljs/server/runtime/exports';
|
|
10
|
-
|
|
11
|
-
export function abort(message: string, fileName: string, line: u32, column: u32): void {
|
|
12
|
-
revertOnError(message, fileName, line, column);
|
|
13
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// AUTO-GENERATED by toil (edge SSR). Do not edit.
|
|
2
|
-
// Route: greeting. Slot ids match the deployed .slots manifest; HASH is the
|
|
3
|
-
// coherence hash the host checks against the template (deploy-skew guard).
|
|
4
|
-
//
|
|
5
|
-
// (For this example the values are hand-fixed; in a real build `template.ts`
|
|
6
|
-
// computes them from the route's rendered template.)
|
|
7
|
-
|
|
8
|
-
/** Stable hole ids for this route's template. */
|
|
9
|
-
export enum Slot {
|
|
10
|
-
greeting = 0,
|
|
11
|
-
count = 1,
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** Coherence hash (32 bytes) baked into the guest and echoed in every values
|
|
15
|
-
* envelope; the host rejects a response whose hash != the deployed template. */
|
|
16
|
-
export const HASH: StaticArray<u8> = [
|
|
17
|
-
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
18
|
-
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
19
|
-
];
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Edge-SSR example entry. Registers the `/hello` render (side-effect import),
|
|
3
|
-
* sets a no-op HTTP handler (so the `handle` export is well-formed), and
|
|
4
|
-
* surfaces the wasm exports — including `render(i32, i32) -> i64`.
|
|
5
|
-
*/
|
|
6
|
-
import { Server, ToilHandler } from 'toiljs/server/runtime';
|
|
7
|
-
import { revertOnError } from 'toiljs/server/runtime/abort/abort';
|
|
8
|
-
import './SsrGreetingRender';
|
|
9
|
-
|
|
10
|
-
Server.handler = () => {
|
|
11
|
-
return new ToilHandler();
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export * from 'toiljs/server/runtime/exports';
|
|
15
|
-
|
|
16
|
-
export function abort(message: string, fileName: string, line: u32, column: u32): void {
|
|
17
|
-
revertOnError(message, fileName, line, column);
|
|
18
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Server } from 'toiljs/server/runtime';
|
|
2
|
-
import { revertOnError } from 'toiljs/server/runtime/abort/abort';
|
|
3
|
-
import { FastTrapHandler } from './FastTrapHandler';
|
|
4
|
-
Server.handler = () => { return new FastTrapHandler(); };
|
|
5
|
-
export * from 'toiljs/server/runtime/exports';
|
|
6
|
-
export function abort(message: string, fileName: string, line: u32, column: u32): void {
|
|
7
|
-
revertOnError(message, fileName, line, column);
|
|
8
|
-
}
|