toiljs 0.0.15 → 0.0.19
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/.babelrc +13 -13
- package/.gitattributes +2 -2
- package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
- package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -90
- package/.github/ISSUE_TEMPLATE/config.yml +8 -8
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
- package/.github/PULL_REQUEST_TEMPLATE.md +43 -43
- package/.github/changelog-config.json +45 -45
- package/.github/dependabot.yml +27 -27
- package/.github/workflows/ci.yml +191 -191
- package/.prettierrc.json +11 -11
- package/.vscode/settings.json +9 -9
- package/CHANGELOG.md +116 -5
- package/LICENSE +187 -187
- package/README.md +524 -315
- package/as-pect.asconfig.json +34 -34
- package/as-pect.config.js +65 -65
- package/assets/logo.svg +36 -36
- package/build/backend/.tsbuildinfo +1 -1
- package/build/backend/index.d.ts +1 -0
- package/build/backend/index.js +20 -1
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +1320 -696
- package/build/client/.tsbuildinfo +1 -1
- package/build/client/dev/devtools.d.ts +6 -0
- package/build/client/dev/devtools.js +479 -0
- package/build/client/dev/error-overlay.d.ts +9 -0
- package/build/client/dev/error-overlay.js +19 -4
- package/build/client/errors.d.ts +1 -0
- package/build/client/errors.js +3 -0
- package/build/client/index.d.ts +2 -0
- package/build/client/index.js +2 -0
- package/build/client/navigation/prefetch.d.ts +1 -0
- package/build/client/navigation/prefetch.js +35 -0
- package/build/client/routing/Router.js +1 -1
- package/build/client/routing/hooks.js +6 -2
- package/build/client/routing/loader.d.ts +23 -0
- package/build/client/routing/loader.js +53 -7
- package/build/client/routing/mount.js +4 -3
- package/build/client/rpc.d.ts +1 -0
- package/build/client/rpc.js +37 -0
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/config.d.ts +16 -0
- package/build/compiler/config.js +9 -0
- package/build/compiler/docs.js +78 -21
- package/build/compiler/generate.js +5 -4
- package/build/compiler/index.d.ts +3 -2
- package/build/compiler/index.js +2 -2
- package/build/compiler/plugin.js +228 -0
- package/build/compiler/prerender.d.ts +1 -0
- package/build/compiler/prerender.js +1 -1
- package/build/compiler/seo.d.ts +1 -1
- package/build/compiler/seo.js +20 -5
- package/build/compiler/ssg.js +39 -2
- package/build/compiler/vite.js +25 -0
- package/build/io/.tsbuildinfo +1 -1
- package/build/io/codec.d.ts +54 -0
- package/build/io/codec.js +143 -0
- package/build/io/index.d.ts +1 -2
- package/build/io/index.js +1 -2
- package/build/logger/.tsbuildinfo +1 -1
- package/build/shared/.tsbuildinfo +1 -1
- package/eslint.config.js +48 -48
- package/examples/basic/client/404.tsx +11 -11
- package/examples/basic/client/components/.gitkeep +1 -1
- package/examples/basic/client/global-error.tsx +13 -13
- package/examples/basic/client/layout.tsx +25 -25
- package/examples/basic/client/public/images/.gitkeep +1 -1
- package/examples/basic/client/public/images/logo.svg +36 -36
- package/examples/basic/client/public/robots.txt +2 -2
- package/examples/basic/client/routes/docs/[...slug].tsx +12 -12
- package/examples/basic/client/routes/features/error/error.tsx +16 -16
- package/examples/basic/client/routes/features/index.tsx +1 -1
- package/examples/basic/client/routes/features/template/b.tsx +14 -14
- package/examples/basic/client/routes/files/[[...slug]].tsx +21 -21
- package/examples/basic/client/routes/gallery/layout.tsx +13 -13
- package/examples/basic/client/routes/io.tsx +23 -24
- package/examples/basic/client/routes/loader-demo/loading.tsx +13 -13
- package/examples/basic/client/routes/rest.tsx +74 -0
- package/examples/basic/client/routes/rpc.tsx +43 -0
- package/examples/basic/client/routes/search.tsx +61 -61
- package/examples/basic/client/toil.tsx +5 -5
- package/package.json +167 -148
- package/presets/eslint.js +88 -88
- package/presets/no-uint8array-tostring.js +200 -200
- package/presets/prettier-plugin.js +51 -0
- package/presets/prettier.json +19 -18
- package/presets/tsconfig.json +37 -37
- package/server/runtime/README.md +97 -0
- package/server/runtime/abort/abort.ts +27 -0
- package/server/runtime/env/Server.ts +61 -0
- package/server/runtime/envelope.ts +191 -0
- package/server/runtime/exports/index.ts +52 -0
- package/server/runtime/handlers/ToilHandler.ts +34 -0
- package/server/runtime/index.ts +26 -0
- package/server/runtime/lang/Potential.ts +5 -0
- package/server/runtime/memory.ts +81 -0
- package/server/runtime/request.ts +55 -0
- package/server/runtime/response.ts +86 -0
- package/server/runtime/rest/Rest.ts +39 -0
- package/server/runtime/rest/RestHandler.ts +20 -0
- package/server/runtime/rest/RouteContext.ts +82 -0
- package/server/runtime/rest/match.ts +48 -0
- package/server/runtime/tsconfig.json +7 -0
- package/src/backend/index.ts +202 -160
- package/src/cli/create.ts +15 -5
- package/src/cli/diagnostics.ts +81 -0
- package/src/cli/doctor.ts +384 -7
- package/src/cli/index.ts +11 -2
- package/src/cli/proc.ts +50 -50
- package/src/cli/updates.ts +69 -69
- package/src/cli/validate.ts +31 -31
- package/src/client/channel/channel.ts +146 -146
- package/src/client/components/Form.tsx +65 -65
- package/src/client/components/Script.tsx +113 -113
- package/src/client/components/Slot.tsx +21 -21
- package/src/client/dev/devtools.tsx +1018 -0
- package/src/client/dev/error-overlay.tsx +30 -4
- package/src/client/errors.ts +11 -0
- package/src/client/head/head.ts +167 -167
- package/src/client/head/metadata.ts +112 -112
- package/src/client/index.ts +91 -89
- package/src/client/navigation/NavLink.tsx +86 -86
- package/src/client/navigation/navigation.ts +235 -235
- package/src/client/navigation/prefetch.ts +169 -130
- package/src/client/navigation/scroll.ts +53 -53
- package/src/client/routing/Router.tsx +8 -2
- package/src/client/routing/action.ts +122 -122
- package/src/client/routing/error-boundary.tsx +43 -43
- package/src/client/routing/hooks.ts +21 -6
- package/src/client/routing/loader.ts +325 -235
- package/src/client/routing/match.ts +47 -47
- package/src/client/routing/mount.tsx +54 -52
- package/src/client/routing/params-context.ts +10 -10
- package/src/client/routing/slot-context.ts +7 -7
- package/src/client/rpc.ts +64 -0
- package/src/client/search/search.ts +189 -189
- package/src/client/search/use-page-search.ts +73 -73
- package/src/client/types.ts +73 -73
- package/src/compiler/config.ts +221 -182
- package/src/compiler/docs.ts +285 -228
- package/src/compiler/generate.ts +395 -394
- package/src/compiler/index.ts +66 -57
- package/src/compiler/pages.ts +70 -70
- package/src/compiler/plugin.ts +258 -2
- package/src/compiler/prerender.ts +156 -156
- package/src/compiler/seo.ts +417 -390
- package/src/compiler/ssg.ts +171 -126
- package/src/compiler/vite.ts +34 -0
- package/src/io/FastMap.ts +151 -127
- package/src/io/FastSet.ts +15 -1
- package/src/io/codec.ts +217 -0
- package/src/io/index.ts +10 -11
- package/src/io/lengths.ts +14 -14
- package/src/io/types.ts +19 -18
- package/src/logger/index.ts +22 -22
- package/src/shared/index.ts +10 -10
- package/std/client/index.d.ts +15 -15
- package/std/client/package.json +3 -3
- package/test/assembly/example.spec.ts +17 -7
- package/test/channel.test.ts +21 -21
- package/test/doctor.test.ts +65 -0
- package/test/dom/Link.test.tsx +47 -47
- package/test/dom/NavLink.test.tsx +37 -37
- package/test/dom/error-overlay.test.tsx +44 -44
- package/test/dom/loader.test.tsx +121 -121
- package/test/dom/navigation.test.ts +59 -59
- package/test/dom/revalidate.test.tsx +38 -38
- package/test/dom/route-head.test.tsx +78 -78
- package/test/dom/router-loading.test.tsx +44 -44
- package/test/dom/scroll.test.ts +56 -56
- package/test/dom/use-metadata.test.tsx +58 -58
- package/test/errors.test.ts +21 -0
- package/test/io.test.ts +117 -93
- package/test/navlink.test.ts +28 -28
- package/test/placeholder.test.ts +9 -9
- package/test/prettier-plugin.test.ts +46 -0
- package/test/routes.test.ts +76 -76
- package/test/rpc.test.ts +50 -0
- package/test/seo.test.ts +175 -164
- package/test/slot-layouts.test.ts +69 -69
- package/test/ssg.test.ts +36 -36
- package/test/update.test.ts +44 -44
- package/test/validate.test.ts +42 -42
- package/tests/data-parity/generated-parity.ts +99 -0
- package/tests/data-parity/parity.ts +80 -0
- package/tests/data-parity/spec.ts +46 -0
- package/toil-routes.d.ts +7 -0
- package/tsconfig.backend.json +13 -13
- package/tsconfig.base.json +35 -35
- package/tsconfig.cli.json +13 -13
- package/tsconfig.client.json +14 -14
- package/tsconfig.compiler.json +13 -13
- package/tsconfig.io.json +12 -12
- package/tsconfig.json +22 -22
- package/tsconfig.logger.json +12 -12
- package/tsconfig.server.json +10 -10
- package/tsconfig.shared.json +12 -12
- package/vitest.config.ts +26 -26
- package/.idea/codeStyles/Project.xml +0 -54
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/prettier.xml +0 -7
- package/.idea/toiljs.iml +0 -8
- package/.idea/vcs.xml +0 -6
- package/.toil/entry.tsx +0 -9
- package/.toil/index.html +0 -12
- package/.toil/routes.ts +0 -9
- package/build/cli/configure.d.ts +0 -16
- package/build/cli/configure.js +0 -272
- package/build/cli/create.d.ts +0 -16
- package/build/cli/create.js +0 -420
- package/build/cli/diagnostics.d.ts +0 -55
- package/build/cli/diagnostics.js +0 -333
- package/build/cli/doctor.d.ts +0 -6
- package/build/cli/doctor.js +0 -249
- package/build/cli/features.d.ts +0 -25
- package/build/cli/features.js +0 -107
- package/build/cli/index.d.ts +0 -2
- package/build/cli/proc.d.ts +0 -6
- package/build/cli/proc.js +0 -31
- package/build/cli/ui.d.ts +0 -9
- package/build/cli/ui.js +0 -75
- package/build/cli/update.d.ts +0 -7
- package/build/cli/update.js +0 -117
- package/build/cli/updates.d.ts +0 -10
- package/build/cli/updates.js +0 -45
- package/build/cli/validate.d.ts +0 -4
- package/build/cli/validate.js +0 -19
- package/build/client/Link.d.ts +0 -8
- package/build/client/Link.js +0 -44
- package/build/client/NavLink.d.ts +0 -14
- package/build/client/NavLink.js +0 -37
- package/build/client/Router.d.ts +0 -7
- package/build/client/Router.js +0 -55
- package/build/client/channel.d.ts +0 -23
- package/build/client/channel.js +0 -94
- package/build/client/error-boundary.d.ts +0 -16
- package/build/client/error-boundary.js +0 -19
- package/build/client/head.d.ts +0 -26
- package/build/client/head.js +0 -87
- package/build/client/hooks.d.ts +0 -17
- package/build/client/hooks.js +0 -48
- package/build/client/lazy.d.ts +0 -16
- package/build/client/lazy.js +0 -53
- package/build/client/match.d.ts +0 -2
- package/build/client/match.js +0 -32
- package/build/client/mount.d.ts +0 -2
- package/build/client/mount.js +0 -13
- package/build/client/navigation.d.ts +0 -13
- package/build/client/navigation.js +0 -97
- package/build/client/params-context.d.ts +0 -2
- package/build/client/params-context.js +0 -2
- package/build/client/prefetch.d.ts +0 -11
- package/build/client/prefetch.js +0 -100
- package/build/client/runtime.d.ts +0 -31
- package/build/client/runtime.js +0 -112
- package/build/client/scroll.d.ts +0 -8
- package/build/client/scroll.js +0 -36
- package/build/io/BinaryReader.d.ts +0 -44
- package/build/io/BinaryReader.js +0 -244
- package/build/io/BinaryWriter.d.ts +0 -44
- package/build/io/BinaryWriter.js +0 -297
- package/build/server/release.wasm +0 -0
- package/build/server/release.wat +0 -9
- package/src/io/BinaryReader.ts +0 -340
- package/src/io/BinaryWriter.ts +0 -385
- package/src/server/index.ts +0 -10
- package/src/server/main.ts +0 -13
- package/src/server/tsconfig.json +0 -4
- package/toil-env.d.ts +0 -16
- package/toilconfig.json +0 -30
|
@@ -1,200 +1,200 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ESLint rule: disallow `.toString()` on Uint8Array (and branded byte types), which returns
|
|
3
|
-
* comma-separated decimals instead of hex.
|
|
4
|
-
*/
|
|
5
|
-
import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
|
|
6
|
-
import { SyntaxKind } from 'typescript';
|
|
7
|
-
|
|
8
|
-
function isUint8ArrayType(type, checker) {
|
|
9
|
-
const symbol = type.getSymbol();
|
|
10
|
-
if (symbol?.getName() === 'Uint8Array') {
|
|
11
|
-
return true;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const baseTypes = type.getBaseTypes?.();
|
|
15
|
-
if (baseTypes) {
|
|
16
|
-
for (const baseType of baseTypes) {
|
|
17
|
-
if (isUint8ArrayType(baseType, checker)) {
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (type.isIntersection()) {
|
|
24
|
-
for (const subType of type.types) {
|
|
25
|
-
if (isUint8ArrayType(subType, checker)) {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (type.isUnion()) {
|
|
32
|
-
return (
|
|
33
|
-
type.types.length > 0 &&
|
|
34
|
-
type.types.every((subType) => isUint8ArrayType(subType, checker))
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const constraint = type.getConstraint?.();
|
|
39
|
-
if (constraint && isUint8ArrayType(constraint, checker)) {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Types whose toString() is the dangerous default behavior we want to catch.
|
|
48
|
-
* If toString is declared on any type NOT in this set, it has been
|
|
49
|
-
* intentionally overridden and we should leave it alone.
|
|
50
|
-
*/
|
|
51
|
-
const DEFAULT_TOSTRING_OWNERS = new Set([
|
|
52
|
-
'Object',
|
|
53
|
-
'Uint8Array',
|
|
54
|
-
'Int8Array',
|
|
55
|
-
'Uint8ClampedArray',
|
|
56
|
-
'Int16Array',
|
|
57
|
-
'Uint16Array',
|
|
58
|
-
'Int32Array',
|
|
59
|
-
'Uint32Array',
|
|
60
|
-
'Float32Array',
|
|
61
|
-
'Float64Array',
|
|
62
|
-
'BigInt64Array',
|
|
63
|
-
'BigUint64Array',
|
|
64
|
-
]);
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Given a declaration node, walk up the AST parents to find the enclosing
|
|
68
|
-
* class or interface name. More reliable than checker.getTypeAtLocation(decl.parent),
|
|
69
|
-
* which can return odd results for .d.ts files.
|
|
70
|
-
*/
|
|
71
|
-
function getEnclosingClassName(decl) {
|
|
72
|
-
let current = decl.parent;
|
|
73
|
-
while (current) {
|
|
74
|
-
if (
|
|
75
|
-
current.kind === SyntaxKind.ClassDeclaration ||
|
|
76
|
-
current.kind === SyntaxKind.ClassExpression ||
|
|
77
|
-
current.kind === SyntaxKind.InterfaceDeclaration
|
|
78
|
-
) {
|
|
79
|
-
if (current.name) {
|
|
80
|
-
return current.name.text;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
current = current.parent;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Checks whether the resolved toString() on this type is a custom override
|
|
92
|
-
* rather than the default Uint8Array/Object prototype version.
|
|
93
|
-
*/
|
|
94
|
-
function hasCustomToString(type, checker) {
|
|
95
|
-
const toStringSymbol = type.getProperty('toString');
|
|
96
|
-
if (!toStringSymbol) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const declarations = toStringSymbol.getDeclarations();
|
|
101
|
-
if (!declarations || declarations.length === 0) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
for (const decl of declarations) {
|
|
106
|
-
const ownerName = getEnclosingClassName(decl);
|
|
107
|
-
if (ownerName && !DEFAULT_TOSTRING_OWNERS.has(ownerName)) {
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const apparentType = checker.getApparentType(type);
|
|
113
|
-
if (apparentType !== type) {
|
|
114
|
-
const apparentToString = apparentType.getProperty('toString');
|
|
115
|
-
if (apparentToString && apparentToString !== toStringSymbol) {
|
|
116
|
-
const apparentDecls = apparentToString.getDeclarations();
|
|
117
|
-
if (apparentDecls) {
|
|
118
|
-
for (const decl of apparentDecls) {
|
|
119
|
-
const ownerName = getEnclosingClassName(decl);
|
|
120
|
-
if (ownerName && !DEFAULT_TOSTRING_OWNERS.has(ownerName)) {
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const createRule = ESLintUtils.RuleCreator(
|
|
132
|
-
(name) => `https://github.com/dacely-cloud/toiljs/tree/main/presets#${name}`,
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
const rule = createRule({
|
|
136
|
-
name: 'no-uint8array-tostring',
|
|
137
|
-
meta: {
|
|
138
|
-
type: 'problem',
|
|
139
|
-
docs: {
|
|
140
|
-
description:
|
|
141
|
-
'Disallow .toString() on Uint8Array and branded types (Script, Bytes32, etc.) which produces comma-separated decimals instead of hex',
|
|
142
|
-
},
|
|
143
|
-
messages: {
|
|
144
|
-
noUint8ArrayToString:
|
|
145
|
-
'{{typeName}}.toString() returns comma-separated decimals (e.g. "0,32,70,107"), not a hex string. ' +
|
|
146
|
-
'Use Buffer.from(arr).toString("hex") or toHex() instead.',
|
|
147
|
-
},
|
|
148
|
-
schema: [],
|
|
149
|
-
},
|
|
150
|
-
defaultOptions: [],
|
|
151
|
-
create(context) {
|
|
152
|
-
const services = ESLintUtils.getParserServices(context);
|
|
153
|
-
const checker = services.program.getTypeChecker();
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
CallExpression(node) {
|
|
157
|
-
if (
|
|
158
|
-
node.callee.type !== AST_NODE_TYPES.MemberExpression ||
|
|
159
|
-
node.callee.property.type !== AST_NODE_TYPES.Identifier ||
|
|
160
|
-
node.callee.property.name !== 'toString' ||
|
|
161
|
-
node.arguments.length > 0
|
|
162
|
-
) {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const objectNode = node.callee.object;
|
|
167
|
-
const tsNode = services.esTreeNodeToTSNodeMap.get(objectNode);
|
|
168
|
-
const type = checker.getTypeAtLocation(tsNode);
|
|
169
|
-
|
|
170
|
-
if (!isUint8ArrayType(type, checker)) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (hasCustomToString(type, checker)) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const typeName = checker.typeToString(type);
|
|
179
|
-
context.report({
|
|
180
|
-
node,
|
|
181
|
-
messageId: 'noUint8ArrayToString',
|
|
182
|
-
data: { typeName },
|
|
183
|
-
});
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
const plugin = {
|
|
190
|
-
meta: {
|
|
191
|
-
name: 'eslint-plugin-no-uint8array-tostring',
|
|
192
|
-
version: '1.0.0',
|
|
193
|
-
},
|
|
194
|
-
rules: {
|
|
195
|
-
'no-uint8array-tostring': rule,
|
|
196
|
-
},
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
export default plugin;
|
|
200
|
-
export { rule };
|
|
1
|
+
/**
|
|
2
|
+
* ESLint rule: disallow `.toString()` on Uint8Array (and branded byte types), which returns
|
|
3
|
+
* comma-separated decimals instead of hex.
|
|
4
|
+
*/
|
|
5
|
+
import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
|
|
6
|
+
import { SyntaxKind } from 'typescript';
|
|
7
|
+
|
|
8
|
+
function isUint8ArrayType(type, checker) {
|
|
9
|
+
const symbol = type.getSymbol();
|
|
10
|
+
if (symbol?.getName() === 'Uint8Array') {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const baseTypes = type.getBaseTypes?.();
|
|
15
|
+
if (baseTypes) {
|
|
16
|
+
for (const baseType of baseTypes) {
|
|
17
|
+
if (isUint8ArrayType(baseType, checker)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (type.isIntersection()) {
|
|
24
|
+
for (const subType of type.types) {
|
|
25
|
+
if (isUint8ArrayType(subType, checker)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (type.isUnion()) {
|
|
32
|
+
return (
|
|
33
|
+
type.types.length > 0 &&
|
|
34
|
+
type.types.every((subType) => isUint8ArrayType(subType, checker))
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const constraint = type.getConstraint?.();
|
|
39
|
+
if (constraint && isUint8ArrayType(constraint, checker)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Types whose toString() is the dangerous default behavior we want to catch.
|
|
48
|
+
* If toString is declared on any type NOT in this set, it has been
|
|
49
|
+
* intentionally overridden and we should leave it alone.
|
|
50
|
+
*/
|
|
51
|
+
const DEFAULT_TOSTRING_OWNERS = new Set([
|
|
52
|
+
'Object',
|
|
53
|
+
'Uint8Array',
|
|
54
|
+
'Int8Array',
|
|
55
|
+
'Uint8ClampedArray',
|
|
56
|
+
'Int16Array',
|
|
57
|
+
'Uint16Array',
|
|
58
|
+
'Int32Array',
|
|
59
|
+
'Uint32Array',
|
|
60
|
+
'Float32Array',
|
|
61
|
+
'Float64Array',
|
|
62
|
+
'BigInt64Array',
|
|
63
|
+
'BigUint64Array',
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Given a declaration node, walk up the AST parents to find the enclosing
|
|
68
|
+
* class or interface name. More reliable than checker.getTypeAtLocation(decl.parent),
|
|
69
|
+
* which can return odd results for .d.ts files.
|
|
70
|
+
*/
|
|
71
|
+
function getEnclosingClassName(decl) {
|
|
72
|
+
let current = decl.parent;
|
|
73
|
+
while (current) {
|
|
74
|
+
if (
|
|
75
|
+
current.kind === SyntaxKind.ClassDeclaration ||
|
|
76
|
+
current.kind === SyntaxKind.ClassExpression ||
|
|
77
|
+
current.kind === SyntaxKind.InterfaceDeclaration
|
|
78
|
+
) {
|
|
79
|
+
if (current.name) {
|
|
80
|
+
return current.name.text;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
current = current.parent;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Checks whether the resolved toString() on this type is a custom override
|
|
92
|
+
* rather than the default Uint8Array/Object prototype version.
|
|
93
|
+
*/
|
|
94
|
+
function hasCustomToString(type, checker) {
|
|
95
|
+
const toStringSymbol = type.getProperty('toString');
|
|
96
|
+
if (!toStringSymbol) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const declarations = toStringSymbol.getDeclarations();
|
|
101
|
+
if (!declarations || declarations.length === 0) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const decl of declarations) {
|
|
106
|
+
const ownerName = getEnclosingClassName(decl);
|
|
107
|
+
if (ownerName && !DEFAULT_TOSTRING_OWNERS.has(ownerName)) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const apparentType = checker.getApparentType(type);
|
|
113
|
+
if (apparentType !== type) {
|
|
114
|
+
const apparentToString = apparentType.getProperty('toString');
|
|
115
|
+
if (apparentToString && apparentToString !== toStringSymbol) {
|
|
116
|
+
const apparentDecls = apparentToString.getDeclarations();
|
|
117
|
+
if (apparentDecls) {
|
|
118
|
+
for (const decl of apparentDecls) {
|
|
119
|
+
const ownerName = getEnclosingClassName(decl);
|
|
120
|
+
if (ownerName && !DEFAULT_TOSTRING_OWNERS.has(ownerName)) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const createRule = ESLintUtils.RuleCreator(
|
|
132
|
+
(name) => `https://github.com/dacely-cloud/toiljs/tree/main/presets#${name}`,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const rule = createRule({
|
|
136
|
+
name: 'no-uint8array-tostring',
|
|
137
|
+
meta: {
|
|
138
|
+
type: 'problem',
|
|
139
|
+
docs: {
|
|
140
|
+
description:
|
|
141
|
+
'Disallow .toString() on Uint8Array and branded types (Script, Bytes32, etc.) which produces comma-separated decimals instead of hex',
|
|
142
|
+
},
|
|
143
|
+
messages: {
|
|
144
|
+
noUint8ArrayToString:
|
|
145
|
+
'{{typeName}}.toString() returns comma-separated decimals (e.g. "0,32,70,107"), not a hex string. ' +
|
|
146
|
+
'Use Buffer.from(arr).toString("hex") or toHex() instead.',
|
|
147
|
+
},
|
|
148
|
+
schema: [],
|
|
149
|
+
},
|
|
150
|
+
defaultOptions: [],
|
|
151
|
+
create(context) {
|
|
152
|
+
const services = ESLintUtils.getParserServices(context);
|
|
153
|
+
const checker = services.program.getTypeChecker();
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
CallExpression(node) {
|
|
157
|
+
if (
|
|
158
|
+
node.callee.type !== AST_NODE_TYPES.MemberExpression ||
|
|
159
|
+
node.callee.property.type !== AST_NODE_TYPES.Identifier ||
|
|
160
|
+
node.callee.property.name !== 'toString' ||
|
|
161
|
+
node.arguments.length > 0
|
|
162
|
+
) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const objectNode = node.callee.object;
|
|
167
|
+
const tsNode = services.esTreeNodeToTSNodeMap.get(objectNode);
|
|
168
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
169
|
+
|
|
170
|
+
if (!isUint8ArrayType(type, checker)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (hasCustomToString(type, checker)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const typeName = checker.typeToString(type);
|
|
179
|
+
context.report({
|
|
180
|
+
node,
|
|
181
|
+
messageId: 'noUint8ArrayToString',
|
|
182
|
+
data: { typeName },
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const plugin = {
|
|
190
|
+
meta: {
|
|
191
|
+
name: 'eslint-plugin-no-uint8array-tostring',
|
|
192
|
+
version: '1.0.0',
|
|
193
|
+
},
|
|
194
|
+
rules: {
|
|
195
|
+
'no-uint8array-tostring': rule,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export default plugin;
|
|
200
|
+
export { rule };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prettier plugin that lets prettier format toilscript server code, which uses native
|
|
3
|
+
* decorators on free functions (`@main`, `@remote function ...`). Those are valid
|
|
4
|
+
* toilscript but not valid ECMAScript/TypeScript grammar, so prettier's parser rejects
|
|
5
|
+
* them outright.
|
|
6
|
+
*
|
|
7
|
+
* The fix is a parse-time/print-time round-trip: in `preprocess` each function decorator
|
|
8
|
+
* is rewritten to a marker block comment (which the TS parser accepts), and the estree
|
|
9
|
+
* printer's `printComment` renders that marker back as the original `@decorator`. Class
|
|
10
|
+
* and method decorators are already valid grammar and pass through untouched.
|
|
11
|
+
*/
|
|
12
|
+
import * as tsPlugin from 'prettier/plugins/typescript';
|
|
13
|
+
import * as estreePlugin from 'prettier/plugins/estree';
|
|
14
|
+
|
|
15
|
+
const baseTs = tsPlugin.parsers.typescript;
|
|
16
|
+
const baseEstree = estreePlugin.printers.estree;
|
|
17
|
+
|
|
18
|
+
const MARKER = '::toil-decorator ';
|
|
19
|
+
// One-or-more bare decorators (`@name`, no args) immediately before a function declaration.
|
|
20
|
+
const FN_DECORATORS =
|
|
21
|
+
/((?:@[A-Za-z_$][\w$]*[ \t]*\r?\n[ \t]*)+)((?:export[ \t]+)?(?:default[ \t]+)?(?:async[ \t]+)?function\b)/g;
|
|
22
|
+
const ONE_DECORATOR = /@([A-Za-z_$][\w$]*)([ \t]*\r?\n[ \t]*)/g;
|
|
23
|
+
|
|
24
|
+
function preprocess(text, options) {
|
|
25
|
+
const pre = baseTs.preprocess ? baseTs.preprocess(text, options) : text;
|
|
26
|
+
return pre.replace(FN_DECORATORS, (_match, decorators, fn) => {
|
|
27
|
+
const masked = decorators.replace(
|
|
28
|
+
ONE_DECORATOR,
|
|
29
|
+
(_d, name, gap) => `/*${MARKER}${name}*/${gap}`,
|
|
30
|
+
);
|
|
31
|
+
return masked + fn;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const parsers = {
|
|
36
|
+
typescript: { ...baseTs, preprocess },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const printers = {
|
|
40
|
+
estree: {
|
|
41
|
+
...baseEstree,
|
|
42
|
+
printComment(path, options) {
|
|
43
|
+
const comment = path.node ?? path.getValue();
|
|
44
|
+
const value = comment?.value;
|
|
45
|
+
if (typeof value === 'string' && value.startsWith(MARKER)) {
|
|
46
|
+
return '@' + value.slice(MARKER.length).trim();
|
|
47
|
+
}
|
|
48
|
+
return baseEstree.printComment(path, options);
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
package/presets/prettier.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
{
|
|
2
|
+
"plugins": ["toiljs/prettier-plugin"],
|
|
3
|
+
"printWidth": 120,
|
|
4
|
+
"tabWidth": 4,
|
|
5
|
+
"useTabs": false,
|
|
6
|
+
"semi": true,
|
|
7
|
+
"singleQuote": true,
|
|
8
|
+
"trailingComma": "none",
|
|
9
|
+
"bracketSpacing": true,
|
|
10
|
+
"bracketSameLine": true,
|
|
11
|
+
"arrowParens": "always",
|
|
12
|
+
"htmlWhitespaceSensitivity": "css",
|
|
13
|
+
"overrides": [
|
|
14
|
+
{
|
|
15
|
+
"files": "*.html",
|
|
16
|
+
"options": { "parser": "html" }
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
package/presets/tsconfig.json
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
// toiljs shared client tsconfig, opinionated, strict.
|
|
2
|
-
// Extend it from your project: { "extends": "toiljs/tsconfig", "include": ["client", ".toil"] }
|
|
3
|
-
{
|
|
4
|
-
"compilerOptions": {
|
|
5
|
-
"strict": true,
|
|
6
|
-
"noImplicitAny": true,
|
|
7
|
-
"strictNullChecks": true,
|
|
8
|
-
"strictFunctionTypes": true,
|
|
9
|
-
"strictBindCallApply": true,
|
|
10
|
-
"strictPropertyInitialization": true,
|
|
11
|
-
"noImplicitThis": true,
|
|
12
|
-
"useUnknownInCatchVariables": true,
|
|
13
|
-
"alwaysStrict": true,
|
|
14
|
-
"noUnusedLocals": true,
|
|
15
|
-
"noUnusedParameters": true,
|
|
16
|
-
"exactOptionalPropertyTypes": false,
|
|
17
|
-
"noImplicitReturns": false,
|
|
18
|
-
"noFallthroughCasesInSwitch": true,
|
|
19
|
-
"noUncheckedIndexedAccess": false,
|
|
20
|
-
"noImplicitOverride": true,
|
|
21
|
-
"noPropertyAccessFromIndexSignature": false,
|
|
22
|
-
"module": "ES2020",
|
|
23
|
-
"target": "ES2020",
|
|
24
|
-
"moduleResolution": "Bundler",
|
|
25
|
-
"jsx": "react-jsx",
|
|
26
|
-
"lib": ["ESNext", "DOM", "DOM.Iterable", "DOM.AsyncIterable"],
|
|
27
|
-
"isolatedModules": true,
|
|
28
|
-
"verbatimModuleSyntax": false,
|
|
29
|
-
"allowImportingTsExtensions": true,
|
|
30
|
-
"esModuleInterop": true,
|
|
31
|
-
"resolveJsonModule": true,
|
|
32
|
-
"forceConsistentCasingInFileNames": true,
|
|
33
|
-
"skipLibCheck": true,
|
|
34
|
-
"moduleDetection": "force",
|
|
35
|
-
"noEmit": true
|
|
36
|
-
}
|
|
37
|
-
}
|
|
1
|
+
// toiljs shared client tsconfig, opinionated, strict.
|
|
2
|
+
// Extend it from your project: { "extends": "toiljs/tsconfig", "include": ["client", ".toil"] }
|
|
3
|
+
{
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"strict": true,
|
|
6
|
+
"noImplicitAny": true,
|
|
7
|
+
"strictNullChecks": true,
|
|
8
|
+
"strictFunctionTypes": true,
|
|
9
|
+
"strictBindCallApply": true,
|
|
10
|
+
"strictPropertyInitialization": true,
|
|
11
|
+
"noImplicitThis": true,
|
|
12
|
+
"useUnknownInCatchVariables": true,
|
|
13
|
+
"alwaysStrict": true,
|
|
14
|
+
"noUnusedLocals": true,
|
|
15
|
+
"noUnusedParameters": true,
|
|
16
|
+
"exactOptionalPropertyTypes": false,
|
|
17
|
+
"noImplicitReturns": false,
|
|
18
|
+
"noFallthroughCasesInSwitch": true,
|
|
19
|
+
"noUncheckedIndexedAccess": false,
|
|
20
|
+
"noImplicitOverride": true,
|
|
21
|
+
"noPropertyAccessFromIndexSignature": false,
|
|
22
|
+
"module": "ES2020",
|
|
23
|
+
"target": "ES2020",
|
|
24
|
+
"moduleResolution": "Bundler",
|
|
25
|
+
"jsx": "react-jsx",
|
|
26
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable", "DOM.AsyncIterable"],
|
|
27
|
+
"isolatedModules": true,
|
|
28
|
+
"verbatimModuleSyntax": false,
|
|
29
|
+
"allowImportingTsExtensions": true,
|
|
30
|
+
"esModuleInterop": true,
|
|
31
|
+
"resolveJsonModule": true,
|
|
32
|
+
"forceConsistentCasingInFileNames": true,
|
|
33
|
+
"skipLibCheck": true,
|
|
34
|
+
"moduleDetection": "force",
|
|
35
|
+
"noEmit": true
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# toiljs server runtime
|
|
2
|
+
|
|
3
|
+
In-tree SDK that bridges a toilscript handler to the toil-backend
|
|
4
|
+
edge's wasm ABI.
|
|
5
|
+
|
|
6
|
+
## What it does
|
|
7
|
+
|
|
8
|
+
The edge calls `handle(req_ofs: i32, req_len: i32) -> i64` on every
|
|
9
|
+
request. This runtime gives you:
|
|
10
|
+
|
|
11
|
+
- `Request` / `Response` AssemblyScript types
|
|
12
|
+
- A byte-for-byte envelope codec matching
|
|
13
|
+
`toil-backend/src/http/envelope.rs`
|
|
14
|
+
- A `ToilHandler` base class you extend, plus a `Server` singleton you
|
|
15
|
+
assign `Server.handler = () => new MyHandler()`. The `handle` wasm
|
|
16
|
+
export (re-exported from `toiljs/server/runtime/exports`) decodes the
|
|
17
|
+
request, runs your handler, encodes the response, and returns the
|
|
18
|
+
packed i64 the host expects.
|
|
19
|
+
|
|
20
|
+
## Wire contract
|
|
21
|
+
|
|
22
|
+
Source of truth: `toil-backend/src/http/envelope.rs`.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
request envelope (LE, no padding):
|
|
26
|
+
u8 method 0=GET 1=POST 2=PUT 3=DELETE 4=PATCH 5=HEAD 6=OPTIONS
|
|
27
|
+
u16 path_len
|
|
28
|
+
[u8] path
|
|
29
|
+
u16 n_headers
|
|
30
|
+
for each header: u16 name_len, u16 val_len, [u8] name, [u8] val
|
|
31
|
+
u32 body_len
|
|
32
|
+
[u8] body
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The response envelope is the same shape with the first `u8 method +
|
|
36
|
+
u16 path_len + path` replaced by `u16 status`.
|
|
37
|
+
|
|
38
|
+
The handler must return a packed `i64`:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
(resp_ofs << 32) | resp_len
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The host reads `resp_len` bytes starting at `resp_ofs` in linear
|
|
45
|
+
memory and decodes them as a response envelope.
|
|
46
|
+
|
|
47
|
+
## Memory layout
|
|
48
|
+
|
|
49
|
+
- `[0, req_len)` — request envelope, written by the host before
|
|
50
|
+
`handle` is called.
|
|
51
|
+
- `[65536, 65536 + resp_len)` — response envelope, written by
|
|
52
|
+
`dispatch` (the response base is the second 64 KiB page so the
|
|
53
|
+
request and response never overlap).
|
|
54
|
+
|
|
55
|
+
The edge enforces a 1024-page (64 MiB) linear memory cap via
|
|
56
|
+
`LimitingTunables`, so leaving the first page for the request is
|
|
57
|
+
fine.
|
|
58
|
+
|
|
59
|
+
## Example
|
|
60
|
+
|
|
61
|
+
A user app extends `ToilHandler` and wires it up in `server/main.ts`.
|
|
62
|
+
The runtime is consumed as the `toiljs/server/runtime` library export:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
// server/HelloHandler.ts
|
|
66
|
+
import { ToilHandler, Request, Response } from 'toiljs/server/runtime';
|
|
67
|
+
|
|
68
|
+
export class HelloHandler extends ToilHandler {
|
|
69
|
+
public handle(req: Request): Response {
|
|
70
|
+
if (req.path == '/') return Response.text('hello\n');
|
|
71
|
+
if (req.path == '/json') return Response.json('{"ok":true}');
|
|
72
|
+
return Response.notFound();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// server/main.ts
|
|
79
|
+
import { Server } from 'toiljs/server/runtime';
|
|
80
|
+
import { revertOnError } from 'toiljs/server/runtime/abort/abort';
|
|
81
|
+
import { HelloHandler } from './HelloHandler';
|
|
82
|
+
|
|
83
|
+
Server.handler = () => new HelloHandler();
|
|
84
|
+
|
|
85
|
+
// Surface the wasm `handle(i32, i32) -> i64` entry the edge calls.
|
|
86
|
+
export * from 'toiljs/server/runtime/exports';
|
|
87
|
+
|
|
88
|
+
// Forward AS runtime panics to the host's env::abort import.
|
|
89
|
+
export function abort(message: string, fileName: string, line: u32, column: u32): void {
|
|
90
|
+
revertOnError(message, fileName, line, column);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Compile with `toilscript --target release`, drop the resulting
|
|
95
|
+
`build/server/release.wasm` at `<toil-backend>/hosts/<hostname>.wasm`,
|
|
96
|
+
and the edge will route requests with that `Host:` header to it. A
|
|
97
|
+
complete app lives in `examples/basic`.
|