acrylblends 0.7.1 → 0.7.3
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 +2 -2
- package/dist/index.js +41 -1
- package/dist/index.js.map +1 -1
- package/docs/framework/README.md +2 -1
- package/docs/framework/UPSTREAM.md +1 -1
- package/docs/framework/api/summary.md +6 -3
- package/docs/framework/cordis/node-server.md +3 -0
- package/docs/framework/getting-started/deploy.md +133 -0
- package/docs/framework/getting-started/nodejs.md +37 -16
- package/docs/framework/guides/client-modules.md +209 -0
- package/docs/framework/guides/forms-and-sessions.md +1 -1
- package/docs/framework/guides/jsx-dom.md +6 -1
- package/docs/framework/guides/validation.md +87 -0
- package/docs/framework/helpers/streaming.md +119 -0
- package/docs/framework/helpers/testing.md +17 -0
- package/docs/framework/upstream.json +3 -3
- package/lib/browser/client.js +307 -0
- package/lib/browser/jsx-dom.js +1432 -0
- package/lib/index.js +1 -25
- package/lib/node-server/register.js +7 -49
- package/lib/node-server.d.ts +74 -7
- package/lib/node-server.js +264 -7
- package/lib/schema-validator.d.ts +40 -14
- package/lib/schema-validator.js +2 -2
- package/lib/src-0hI7M7cN.js +27 -0
- package/lib/transpile-CTnWof4z.js +90 -0
- package/package.json +1 -1
- package/plugins/acryl-inspect/upstream.json +1 -1
- package/plugins/acryl-node-server/src/adapter/server.ts +28 -5
- package/plugins/acryl-node-server/src/adapter/types.ts +14 -2
- package/plugins/acryl-node-server/src/client-modules.ts +293 -0
- package/plugins/acryl-node-server/src/register.ts +5 -62
- package/plugins/acryl-node-server/src/transpile.ts +120 -0
- package/plugins/acryl-node-server/upstream.json +4 -3
- package/plugins/acryl-router/src/browser/client.ts +3 -0
- package/plugins/acryl-router/src/browser/jsx-dom.ts +9 -0
- package/plugins/acryl-router/src/helpers/index.ts +1 -1
- package/plugins/acryl-router/src/helpers/schema-validator.ts +39 -12
- package/plugins/acryl-router/upstream.json +2 -2
- package/plugins/acryl-router-http/upstream.json +1 -1
- package/plugins/acryl-state/upstream.json +1 -1
- package/plugins/desktop/upstream.json +1 -1
- package/plugins/desktop-web/upstream.json +1 -1
- package/templates/agent/package.json +4 -3
- package/templates/blank/package.json +4 -3
- package/templates/todo/package.json +4 -3
- package/templates/todo/upstream.json +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One TypeScript module compiled to JavaScript with the app's own `typescript`: the transform behind `acrylblends/node-server/register`
|
|
3
|
+
* (server JSX on Node) and `clientModules` (browser modules). `typescript` is loaded from the app on first use, so the framework itself has
|
|
4
|
+
* no dependencies.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createRequire } from 'node:module'
|
|
8
|
+
import { existsSync } from 'node:fs'
|
|
9
|
+
import { dirname, join } from 'node:path'
|
|
10
|
+
|
|
11
|
+
/** The part of the TypeScript compiler API the framework uses. */
|
|
12
|
+
interface Compiler {
|
|
13
|
+
transpileModule(input: string, options: { fileName: string, compilerOptions: Record<string, unknown> }): { outputText: string }
|
|
14
|
+
findConfigFile(searchPath: string, fileExists: (path: string) => boolean): string | undefined
|
|
15
|
+
readConfigFile(path: string, readFile: (path: string) => string | undefined): { config?: unknown }
|
|
16
|
+
parseJsonConfigFileContent(json: unknown, host: unknown, basePath: string): { options: { jsxImportSource?: string } }
|
|
17
|
+
preProcessFile(text: string, readImportFiles: boolean, detectJavaScriptImports: boolean): { importedFiles: { fileName: string, pos: number }[] }
|
|
18
|
+
sys: { fileExists(path: string): boolean, readFile(path: string): string | undefined }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Thrown when the app has no `typescript` to compile a file with. */
|
|
22
|
+
export class MissingTypeScriptError extends Error {
|
|
23
|
+
constructor(file: string, what: string) {
|
|
24
|
+
super(`acrylblends: ${file} ${what}, and compiling it needs the typescript package in this app: npm install --save-dev typescript`)
|
|
25
|
+
this.name = 'MissingTypeScriptError'
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const compilers = new Map<string, Compiler>()
|
|
30
|
+
const importSources = new Map<string, string>()
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The app's own `typescript`, resolved from the file, so each app compiles with the version it pinned.
|
|
34
|
+
* @param file - the module to compile.
|
|
35
|
+
* @param what - what the file needs compiling for, for the error message.
|
|
36
|
+
* @returns the compiler.
|
|
37
|
+
* @throws MissingTypeScriptError when the app has no `typescript`.
|
|
38
|
+
*/
|
|
39
|
+
function compilerFor(file: string, what: string): Compiler {
|
|
40
|
+
// Only the app's own node_modules chain counts: Node's global folders (NODE_PATH, which a package manager's shims may set) are not the app.
|
|
41
|
+
let owner: string | undefined
|
|
42
|
+
for (let dir = dirname(file); owner === undefined; dir = dirname(dir)) {
|
|
43
|
+
if (existsSync(join(dir, 'node_modules', 'typescript', 'package.json'))) owner = dir
|
|
44
|
+
else if (dirname(dir) === dir) throw new MissingTypeScriptError(file, what)
|
|
45
|
+
}
|
|
46
|
+
const path = createRequire(join(owner, 'package.json')).resolve('typescript')
|
|
47
|
+
let compiler = compilers.get(path)
|
|
48
|
+
if (compiler === undefined) {
|
|
49
|
+
compiler = createRequire(path)(path) as Compiler
|
|
50
|
+
compilers.set(path, compiler)
|
|
51
|
+
}
|
|
52
|
+
return compiler
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** `jsxImportSource` of the tsconfig.json nearest to the file (following `extends`), read once per config. */
|
|
56
|
+
function importSourceFor(file: string, ts: Compiler, fallback: string): string {
|
|
57
|
+
const config = ts.findConfigFile(dirname(file), path => existsSync(path))
|
|
58
|
+
if (config === undefined) return fallback
|
|
59
|
+
let source = importSources.get(`${config}\0${fallback}`)
|
|
60
|
+
if (source === undefined) {
|
|
61
|
+
const { config: json } = ts.readConfigFile(config, path => ts.sys.readFile(path))
|
|
62
|
+
source = ts.parseJsonConfigFileContent(json ?? {}, ts.sys, dirname(config)).options.jsxImportSource ?? fallback
|
|
63
|
+
importSources.set(`${config}\0${fallback}`, source)
|
|
64
|
+
}
|
|
65
|
+
return source
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** How {@link compileModule} compiles one file. */
|
|
69
|
+
export interface CompileOptions {
|
|
70
|
+
/** The JSX runtime package; without it, the nearest tsconfig.json's `jsxImportSource`, then `defaultImportSource`. */
|
|
71
|
+
readonly importSource?: string | undefined
|
|
72
|
+
/** The runtime when neither `importSource` nor a tsconfig.json names one. */
|
|
73
|
+
readonly defaultImportSource: string
|
|
74
|
+
/** What the file needs compiling for, as the missing-typescript message says it. */
|
|
75
|
+
readonly what: string
|
|
76
|
+
/** Replaces an import specifier of the output (static, `export ... from` and dynamic); `undefined` keeps it. */
|
|
77
|
+
readonly rewrite?: ((specifier: string) => string | undefined) | undefined
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A TypeScript or JSX module compiled to an ES module, with its source map inline (a file's own `@jsxImportSource` pragma wins over the
|
|
82
|
+
* options).
|
|
83
|
+
* @param file - absolute path of the module, which also locates the app's `typescript` and tsconfig.json.
|
|
84
|
+
* @param text - its source.
|
|
85
|
+
* @param options - the JSX runtime and the specifier rewrite.
|
|
86
|
+
* @returns the JavaScript.
|
|
87
|
+
* @throws MissingTypeScriptError when the app has no `typescript`.
|
|
88
|
+
*/
|
|
89
|
+
export function compileModule(file: string, text: string, options: CompileOptions): string {
|
|
90
|
+
const ts = compilerFor(file, options.what)
|
|
91
|
+
const output = ts.transpileModule(text, {
|
|
92
|
+
fileName: file,
|
|
93
|
+
compilerOptions: {
|
|
94
|
+
module: 'esnext',
|
|
95
|
+
target: 'es2022',
|
|
96
|
+
jsx: 'react-jsx',
|
|
97
|
+
jsxImportSource: options.importSource ?? importSourceFor(file, ts, options.defaultImportSource),
|
|
98
|
+
allowImportingTsExtensions: true,
|
|
99
|
+
inlineSourceMap: true,
|
|
100
|
+
inlineSources: true,
|
|
101
|
+
},
|
|
102
|
+
}).outputText
|
|
103
|
+
return options.rewrite === undefined ? output : rewriteSpecifiers(ts, output, options.rewrite)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The output with each import specifier `rewrite` maps replaced; TypeScript's lexer finds them, so strings and comments are left alone. */
|
|
107
|
+
function rewriteSpecifiers(ts: Compiler, code: string, rewrite: (specifier: string) => string | undefined): string {
|
|
108
|
+
let result = ''
|
|
109
|
+
let last = 0
|
|
110
|
+
for (const { fileName, pos } of ts.preProcessFile(code, true, true).importedFiles.toSorted((a, b) => a.pos - b.pos)) {
|
|
111
|
+
// `pos` is the opening quote; a specifier written with escapes does not match its text and is left as it is.
|
|
112
|
+
const start = pos + 1
|
|
113
|
+
if (code.slice(start, start + fileName.length) !== fileName) continue
|
|
114
|
+
const replacement = rewrite(fileName)
|
|
115
|
+
if (replacement === undefined) continue
|
|
116
|
+
result += code.slice(last, start) + replacement
|
|
117
|
+
last = start + fileName.length
|
|
118
|
+
}
|
|
119
|
+
return result + code.slice(last)
|
|
120
|
+
}
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
"source": {
|
|
3
3
|
"repo": "acryldev/acryl",
|
|
4
4
|
"path": "plugins/acryl-node-server",
|
|
5
|
-
"commit": "
|
|
5
|
+
"commit": "e79bf3de59c700667ae617f5d367cc1fd2f8e037"
|
|
6
6
|
},
|
|
7
7
|
"vendored": [
|
|
8
8
|
"src",
|
|
9
9
|
"tests",
|
|
10
10
|
"LICENSE"
|
|
11
11
|
],
|
|
12
|
-
"rewrittenSpecifiers":
|
|
13
|
-
"digest": "sha256:
|
|
12
|
+
"rewrittenSpecifiers": 31,
|
|
13
|
+
"digest": "sha256:0a3278e006540d0dbecdfb6ada3ca865f3a9a78b649edd9454dab0c76281eda0",
|
|
14
14
|
"testsNotRun": {
|
|
15
|
+
"tests/client-modules.spec.ts": "links typescript from the plugin's own node_modules (ACRYL's workspace layout); clientModules and build-client are exercised here by the templates' tests and verify:scaffolder",
|
|
15
16
|
"tests/register.spec.ts": "links typescript from the plugin's own node_modules (ACRYL's workspace layout); the hook is exercised here by the templates' own .tsx tests and verify:scaffolder"
|
|
16
17
|
},
|
|
17
18
|
"notice": [
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The DOM renderer as one browser module: `acrylblends/jsx/dom`, its JSX runtimes and `client`, in a single file with no imports, so a page
|
|
3
|
+
* loads it without a bundler and the runtime and the hooks share one module. `clientModules` serves it and points those imports here.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export * from '../core/jsx/dom/index.ts'
|
|
7
|
+
export { jsx, jsxs } from '../core/jsx/dom/jsx-runtime.ts'
|
|
8
|
+
export { jsxDEV } from '../core/jsx/dom/jsx-dev-runtime.ts'
|
|
9
|
+
export { createRoot, hydrateRoot } from '../core/jsx/dom/client.ts'
|
|
@@ -13,5 +13,5 @@ export { testClient } from '../core/helper/testing/index.ts'
|
|
|
13
13
|
export { defaultPlugin, disableSSG, isSSGContext, onlySSG, redirectPlugin, ssgParams, toSSG, X_ACRYL_DISABLE_SSG_HEADER_KEY, type SSGPlugin, type ToSSGOptions } from '../core/helper/ssg/index.ts'
|
|
14
14
|
export { stream, streamSSE, streamText, SSEStreamingApi, type SSEMessage } from '../core/helper/streaming/index.ts'
|
|
15
15
|
export { validator, type ValidationFunction } from '../core/validator/index.ts'
|
|
16
|
-
export { schemaValidator, type StandardSchema } from './schema-validator.ts'
|
|
16
|
+
export { schemaValidator, type StandardIssue, type StandardResult, type StandardSchema, type StandardSchemaOptions, type StandardSchemaProps } from './schema-validator.ts'
|
|
17
17
|
export { createWSMessageEvent, defineWebSocketHelper, WSContext, type SendOptions, type UpgradeWebSocket, type WSContextInit, type WSEvents, type WSMessageReceive, type WSReadyState } from '../core/helper/websocket/index.ts'
|
|
@@ -1,27 +1,54 @@
|
|
|
1
1
|
import type { Env, MiddlewareHandler, ValidationTargets } from '../core/types.ts'
|
|
2
2
|
import { validator } from '../core/validator/index.ts'
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The Standard Schema v1 interface (https://standardschema.dev), declared here so the framework needs no dependency for it. Schemastery
|
|
6
|
+
* (what Cordis uses), Zod and Valibot implement it, and a hand-written schema object of the same shape is accepted as well. Only
|
|
7
|
+
* `validate` is called, so `version` and `vendor` may be left out of a hand-written schema.
|
|
8
|
+
*/
|
|
9
|
+
export interface StandardSchema<Input = unknown, Output = Input> {
|
|
10
|
+
readonly '~standard': StandardSchemaProps<Input, Output>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** The `~standard` properties of a {@link StandardSchema}. */
|
|
14
|
+
export interface StandardSchemaProps<Input = unknown, Output = Input> {
|
|
15
|
+
/** The version of the standard; every library sets it. */
|
|
16
|
+
readonly version?: 1 | undefined
|
|
17
|
+
/** The name of the schema library; every library sets it. */
|
|
18
|
+
readonly vendor?: string | undefined
|
|
19
|
+
/** Validates an unknown value. */
|
|
20
|
+
readonly validate: (value: unknown, options?: StandardSchemaOptions | undefined) => StandardResult<Output> | Promise<StandardResult<Output>>
|
|
21
|
+
/** The input and output types, for inference only. */
|
|
22
|
+
readonly types?: { readonly input: Input, readonly output: Output } | undefined
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Options a caller may pass to `validate`. */
|
|
26
|
+
export interface StandardSchemaOptions {
|
|
27
|
+
/** Library-specific parameters. */
|
|
28
|
+
readonly libraryOptions?: Record<string, unknown> | undefined
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** One validation issue: a message and where in the value it applies. */
|
|
32
|
+
export interface StandardIssue {
|
|
33
|
+
readonly message: string
|
|
34
|
+
readonly path?: readonly (PropertyKey | { readonly key: PropertyKey })[] | undefined
|
|
9
35
|
}
|
|
10
36
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
| { readonly
|
|
37
|
+
/** What `validate` answers: the parsed value, or the issues. */
|
|
38
|
+
export type StandardResult<Output> =
|
|
39
|
+
| { readonly value: Output, readonly issues?: undefined }
|
|
40
|
+
| { readonly issues: readonly StandardIssue[] }
|
|
14
41
|
|
|
15
|
-
const pathOf = (path:
|
|
16
|
-
(path ?? []).map(part => String(typeof part === 'object'
|
|
42
|
+
const pathOf = (path: StandardIssue['path']): string =>
|
|
43
|
+
(path ?? []).map(part => String(typeof part === 'object' ? part.key : part)).join('.')
|
|
17
44
|
|
|
18
45
|
/**
|
|
19
46
|
* The core `validator` driven by a Standard Schema. A failure answers 400 with `{ error, issues }`; success makes the parsed value the
|
|
20
|
-
* handler's `c.req.valid(target)`, typed by the schema.
|
|
47
|
+
* handler's `c.req.valid(target)`, typed by the schema's output.
|
|
21
48
|
*/
|
|
22
49
|
export function schemaValidator<Target extends keyof ValidationTargets, Out, E extends Env = Env, P extends string = string>(
|
|
23
50
|
target: Target,
|
|
24
|
-
schema: StandardSchema<Out>,
|
|
51
|
+
schema: StandardSchema<unknown, Out>,
|
|
25
52
|
): MiddlewareHandler<E, P, { in: { [K in Target]: Out }, out: { [K in Target]: Out } }> {
|
|
26
53
|
const middleware = validator(target, async (value, c) => {
|
|
27
54
|
const result = await schema['~standard'].validate(value)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"source": {
|
|
3
3
|
"repo": "acryldev/acryl",
|
|
4
4
|
"path": "plugins/acryl-router",
|
|
5
|
-
"commit": "
|
|
5
|
+
"commit": "e79bf3de59c700667ae617f5d367cc1fd2f8e037"
|
|
6
6
|
},
|
|
7
7
|
"vendored": [
|
|
8
8
|
"src",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"LICENSE"
|
|
11
11
|
],
|
|
12
12
|
"rewrittenSpecifiers": 6,
|
|
13
|
-
"digest": "sha256:
|
|
13
|
+
"digest": "sha256:1dd20cb7b5bb87e4368ce507dfe85de3e992cc90069a3b43e24af053b1186842",
|
|
14
14
|
"testsNotRun": {
|
|
15
15
|
"tests/loader.spec.ts": "loads the built lib/ entry through the real Loader; the library ships source and has no build"
|
|
16
16
|
},
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"apps/acryl-desktop/src/shell/desktop-user-data.ts": "user-data.ts",
|
|
6
6
|
"apps/acryl-desktop/src/shell/shutdown.ts": "shutdown.ts"
|
|
7
7
|
},
|
|
8
|
-
"commit": "
|
|
8
|
+
"commit": "e79bf3de59c700667ae617f5d367cc1fd2f8e037"
|
|
9
9
|
},
|
|
10
10
|
"rewrittenSpecifiers": 0,
|
|
11
11
|
"digest": "sha256:7dfe960a8bbb476c35540d2202a7aaa62b33492c2ac3e34314e2ecfca45f32f2"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"apps/acryl-desktop/src/shell/desktop-user-data.ts": "user-data.ts",
|
|
6
6
|
"apps/acryl-desktop/src/shell/shutdown.ts": "shutdown.ts"
|
|
7
7
|
},
|
|
8
|
-
"commit": "
|
|
8
|
+
"commit": "e79bf3de59c700667ae617f5d367cc1fd2f8e037"
|
|
9
9
|
},
|
|
10
10
|
"rewrittenSpecifiers": 0,
|
|
11
11
|
"digest": "sha256:7dfe960a8bbb476c35540d2202a7aaa62b33492c2ac3e34314e2ecfca45f32f2"
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
"save": "node src/plugins/save/cli.ts",
|
|
20
20
|
"upgrade": "node src/plugins/manifest/upgrade-cli.ts"
|
|
21
21
|
},
|
|
22
|
-
"dependencies": {
|
|
23
|
-
"devDependencies": {
|
|
24
|
-
"@types/node": "^24.0.0",
|
|
22
|
+
"dependencies": {
|
|
25
23
|
"typescript": "6.0.3"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^24.0.0"
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
"save": "node src/plugins/save/cli.ts",
|
|
20
20
|
"upgrade": "node src/plugins/manifest/upgrade-cli.ts"
|
|
21
21
|
},
|
|
22
|
-
"dependencies": {
|
|
23
|
-
"devDependencies": {
|
|
24
|
-
"@types/node": "^24.0.0",
|
|
22
|
+
"dependencies": {
|
|
25
23
|
"typescript": "6.0.3"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^24.0.0"
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
"save": "node src/plugins/save/cli.ts",
|
|
20
20
|
"upgrade": "node src/plugins/manifest/upgrade-cli.ts"
|
|
21
21
|
},
|
|
22
|
-
"dependencies": {
|
|
23
|
-
"devDependencies": {
|
|
24
|
-
"@types/node": "^24.0.0",
|
|
22
|
+
"dependencies": {
|
|
25
23
|
"typescript": "6.0.3"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^24.0.0"
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"examples/acryl-todo/src/index.ts": "src/todo.ts",
|
|
6
6
|
"examples/acryl-todo/src/page.ts": "src/page.ts"
|
|
7
7
|
},
|
|
8
|
-
"commit": "
|
|
8
|
+
"commit": "e79bf3de59c700667ae617f5d367cc1fd2f8e037"
|
|
9
9
|
},
|
|
10
10
|
"rewrittenSpecifiers": 4,
|
|
11
11
|
"digest": "sha256:9093704776563001f7f472f94cc5c2fdd79206342c1cb59c11fce62aa74df3ed"
|