@aventara/client 0.1.0-pilot.1 → 0.1.0-pilot.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 +44 -9
- package/dist/avclient.bin.js +0 -10
- package/dist/cli/command.parser.d.ts +15 -10
- package/dist/cli/command.parser.js +13 -19
- package/dist/cli/generate.command.js +0 -6
- package/dist/cli/generation-failure.renderer.js +0 -14
- package/dist/cli/generation-success.renderer.d.ts +4 -1
- package/dist/cli/generation-success.renderer.js +0 -13
- package/dist/cli/terminal.prompter.d.ts +1 -2
- package/dist/cli/warning.renderer.d.ts +2 -2
- package/dist/cli/warning.renderer.js +0 -8
- package/dist/cli.d.ts +8 -16
- package/dist/cli.js +5 -25
- package/dist/config/client-config.interface.d.ts +18 -16
- package/dist/config/client-config.interface.js +0 -13
- package/dist/config/config.loader.d.ts +34 -22
- package/dist/config/config.loader.js +49 -52
- package/dist/config/config.resolver.d.ts +13 -19
- package/dist/config/config.resolver.js +9 -48
- package/dist/config/env.cascade.d.ts +12 -14
- package/dist/config/env.cascade.js +0 -19
- package/dist/config/module-style.resolver.d.ts +52 -0
- package/dist/config/module-style.resolver.js +75 -0
- package/dist/config/tsconfig.locator.d.ts +45 -0
- package/dist/config/tsconfig.locator.js +52 -0
- package/dist/contract/contract.acceptance.d.ts +12 -26
- package/dist/contract/contract.acceptance.js +0 -54
- package/dist/contract/contract.fetcher.d.ts +12 -17
- package/dist/contract/contract.fetcher.js +0 -24
- package/dist/contract/contract.loader.d.ts +4 -5
- package/dist/contract/contract.loader.js +0 -10
- package/dist/emit/banner.emitter.d.ts +11 -12
- package/dist/emit/banner.emitter.js +0 -26
- package/dist/emit/client-surface.emitter.d.ts +17 -21
- package/dist/emit/client-surface.emitter.js +29 -55
- package/dist/emit/client-tree.emitter.d.ts +11 -20
- package/dist/emit/client-tree.emitter.js +12 -54
- package/dist/emit/contract-carrier.emitter.d.ts +5 -6
- package/dist/emit/contract-carrier.emitter.js +0 -28
- package/dist/emit/derivation.emitter.d.ts +7 -7
- package/dist/emit/derivation.emitter.js +2 -161
- package/dist/emit/descriptor.emitter.js +2 -28
- package/dist/emit/emitted-tree.interface.d.ts +40 -17
- package/dist/emit/emitted-tree.interface.js +6 -16
- package/dist/emit/enum.emitter.d.ts +4 -4
- package/dist/emit/enum.emitter.js +0 -24
- package/dist/emit/module-specifier.scanner.d.ts +25 -0
- package/dist/emit/module-specifier.scanner.js +160 -0
- package/dist/emit/module-style.interface.d.ts +58 -0
- package/dist/emit/module-style.interface.js +8 -0
- package/dist/emit/name.deriver.d.ts +33 -61
- package/dist/emit/name.deriver.js +0 -134
- package/dist/emit/named-type.emitter.d.ts +14 -21
- package/dist/emit/named-type.emitter.js +3 -30
- package/dist/emit/runtime.emitter.d.ts +23 -50
- package/dist/emit/runtime.emitter.js +68 -159
- package/dist/emit/scalar.codec.d.ts +20 -33
- package/dist/emit/scalar.codec.js +13 -69
- package/dist/emit/transaction.emitter.d.ts +6 -14
- package/dist/emit/transaction.emitter.js +24 -33
- package/dist/generate.d.ts +20 -34
- package/dist/generate.js +14 -22
- package/dist/index.js +0 -5
- package/dist/init/client-config.template.d.ts +6 -4
- package/dist/init/client-config.template.js +10 -13
- package/dist/init/client-init.errors.js +0 -3
- package/dist/init/client-init.orchestrator.js +8 -9
- package/dist/init/client-init.planner.d.ts +1 -9
- package/dist/init/client-init.planner.js +16 -24
- package/dist/init/client-init.questions.d.ts +8 -12
- package/dist/init/client-init.questions.js +0 -11
- package/dist/init/client-project.inspector.d.ts +6 -0
- package/dist/init/client-project.inspector.js +2 -2
- package/dist/node-version.guard.js +0 -12
- package/dist/output/output.validator.d.ts +49 -27
- package/dist/output/output.validator.js +113 -74
- package/dist/output/output.writer.d.ts +59 -52
- package/dist/output/output.writer.js +72 -134
- package/package.json +6 -4
|
@@ -1,36 +1,11 @@
|
|
|
1
1
|
import { isOperationVariantAvailable, } from "@aventara/core";
|
|
2
2
|
import { ownPropertyKey } from "./name.deriver.js";
|
|
3
|
-
/**
|
|
4
|
-
* `generated/runtime/descriptor.ts` — what the RUNTIME needs of the ClientContract,
|
|
5
|
-
* as data (P1, P4). A projection of the same accepted contract the carrier
|
|
6
|
-
* (`generated/contract.ts`) holds, derived in the same run, so it is not a second
|
|
7
|
-
* source: `descriptor.emitter.spec.ts` regenerates it from the parsed carrier and
|
|
8
|
-
* compares.
|
|
9
|
-
*
|
|
10
|
-
* - **The decode table (P1).** Encoding is decidable by a value's runtime class;
|
|
11
|
-
* decoding is not — a wire `"12"` is a `string`, a `bigint` or a `decimal`
|
|
12
|
-
* depending only on the field (M13). So per Resource the table names each
|
|
13
|
-
* result field whose wire string is revived (`bigint`, `decimal`, `datetime`,
|
|
14
|
-
* `bytes`, §6.2) and each relation with its target and whether it is to-many,
|
|
15
|
-
* which the decode walk recurses into (S5 added `many`: a to-one record may
|
|
16
|
-
* itself hold a `data` and a `count` field, so the shape alone cannot say). Every other key passes through untouched; `json` is never
|
|
17
|
-
* revived.
|
|
18
|
-
* - **The advertised operations (P4).** Read here through core's
|
|
19
|
-
* `isOperationVariantAvailable`, emitted as `[resource, family, variant]`; the
|
|
20
|
-
* type grammar reads the carrier's `operations` keys. They agree while present
|
|
21
|
-
* ⇔ advertised (Phase 10 A3), and a gate asserts it over both pilots.
|
|
22
|
-
*
|
|
23
|
-
* Every registry is walked in UTF-16 code-unit order, so the key order a
|
|
24
|
-
* contract arrives in never reaches the bytes (C-843).
|
|
25
|
-
*/
|
|
26
|
-
/** The scalars whose wire form a decoder revives into another runtime class (§6.2). */
|
|
27
3
|
const REVIVED_SCALARS = new Set([
|
|
28
4
|
"bigint",
|
|
29
5
|
"bytes",
|
|
30
6
|
"datetime",
|
|
31
7
|
"decimal",
|
|
32
8
|
]);
|
|
33
|
-
/** `generated/runtime/descriptor.ts`, before the banner. */
|
|
34
9
|
export function emitDescriptorModule(contract) {
|
|
35
10
|
const resources = codeUnitOrder(Object.keys(contract.resources));
|
|
36
11
|
const table = resources.map((name) => {
|
|
@@ -55,9 +30,9 @@ export function emitDescriptorModule(contract) {
|
|
|
55
30
|
return {
|
|
56
31
|
path: "runtime/descriptor.ts",
|
|
57
32
|
source: "/**\n" +
|
|
58
|
-
" * A result field the decoder revives from its wire string
|
|
33
|
+
" * A result field the decoder revives from its wire string, or a relation\n" +
|
|
59
34
|
" * it recurses into, read as the relation's target Resource — `many` saying the\n" +
|
|
60
|
-
" * value is a list, `{ data, count }` or `{ count }` rather than one record
|
|
35
|
+
" * value is a list, `{ data, count }` or `{ count }` rather than one record.\n" +
|
|
61
36
|
" */\n" +
|
|
62
37
|
"export type FieldDecoding =\n" +
|
|
63
38
|
'\t| "bigint"\n' +
|
|
@@ -79,7 +54,6 @@ export function emitDescriptorModule(contract) {
|
|
|
79
54
|
`])[] = [${advertised.length === 0 ? "" : `\n${advertised.join("")}`}];\n`,
|
|
80
55
|
};
|
|
81
56
|
}
|
|
82
|
-
/** The table entry a field needs, as source, or `undefined` when it passes through. */
|
|
83
57
|
function decodingOf(field) {
|
|
84
58
|
if (field === undefined) {
|
|
85
59
|
return undefined;
|
|
@@ -1,37 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The generator's output as a VALUE before it is a filesystem effect
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* every emitter.
|
|
2
|
+
* The generator's output as a VALUE before it is a filesystem effect.
|
|
3
|
+
* Byte-equality is therefore testable without touching disk, and temp → validate →
|
|
4
|
+
* replace is a property of the writer alone rather than of every emitter.
|
|
6
5
|
*/
|
|
6
|
+
import type { ClientModuleStyle } from "./module-style.interface.js";
|
|
7
7
|
/**
|
|
8
|
-
* The output layout
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The output layout: the consumer names a directory, `generateAt`, which is SHARED
|
|
9
|
+
* — they may keep their own files there. The generator owns exactly two entries in
|
|
10
|
+
* it:
|
|
11
11
|
*
|
|
12
12
|
* - `AvClient.ts`, the entry point a consumer imports;
|
|
13
13
|
* - `generated/`, every other module, owned and replaced whole.
|
|
14
|
+
*
|
|
15
|
+
* The tree is TypeScript source that the developer's own toolchain compiles like
|
|
16
|
+
* their own files — `tsc`, Next.js (Turbopack or webpack), Vite, `tsx`, Node's
|
|
17
|
+
* type stripping. pilot.1's tree always spelled its internal imports NodeNext's
|
|
18
|
+
* way (`./generated/client.js`), which a bundler that does not map `.js` back to
|
|
19
|
+
* `.ts` (Turbopack) and plain Node could not follow. Now those specifiers are the
|
|
20
|
+
* project's: extensionless, `.js` or `.ts`, as its `tsconfig.json` decides
|
|
21
|
+
* (`module-style.interface.ts`, Prisma 7's rules). A consumer imports the entry as
|
|
22
|
+
* they import their own files — `./api/AvClient`, `./api/AvClient.js` or
|
|
23
|
+
* `./api/AvClient.ts`.
|
|
14
24
|
*/
|
|
15
25
|
/** The entry point, at the root of `generateAt`. */
|
|
16
26
|
export declare const CLIENT_ENTRY_FILE = "AvClient.ts";
|
|
27
|
+
/** The files the generator owns at the root of `generateAt`. */
|
|
28
|
+
export declare const CLIENT_ENTRY_FILES: readonly ["AvClient.ts"];
|
|
29
|
+
/** One of the entry point's files. */
|
|
30
|
+
export type ClientEntryFile = (typeof CLIENT_ENTRY_FILES)[number];
|
|
31
|
+
/**
|
|
32
|
+
* Entry files an unreleased build of this generator wrote — JavaScript beside
|
|
33
|
+
* declarations, never published. Each is the generator's only while it carries
|
|
34
|
+
* the ownership line: a generation removes it then, so no stale `AvClient.mjs`
|
|
35
|
+
* is left beside the `AvClient.ts` it was replaced by. One without the line is
|
|
36
|
+
* the developer's file, and nothing reads, refuses on or removes it. (pilot.0's
|
|
37
|
+
* and pilot.1's entry is `AvClient.ts` itself, replaced in place.)
|
|
38
|
+
*/
|
|
39
|
+
export declare const LEGACY_CLIENT_ENTRY_FILES: readonly string[];
|
|
17
40
|
/** The directory holding every other emitted module, under `generateAt`. */
|
|
18
41
|
export declare const GENERATED_DIRECTORY = "generated";
|
|
19
42
|
/**
|
|
20
43
|
* One of core's published declaration files, copied under `generated/derivation/`
|
|
21
|
-
* at its path relative to core's `dist
|
|
44
|
+
* at its path relative to core's `dist`.
|
|
22
45
|
*/
|
|
23
46
|
export type DerivationModulePath = `derivation/${string}.d.ts`;
|
|
24
47
|
/**
|
|
25
|
-
* The modules under `generated/`: the emitted sources, the carrier
|
|
26
|
-
* (`contract.ts`, Q4), the typed surface (`client.ts`) and named types
|
|
27
|
-
* (`types.d.ts`, S4 — a declaration file, architect 2026-10-05), and core's declarations under `derivation/`. §15.4's
|
|
28
|
-
* `runtime/transaction.ts` and `runtime/fingerprint.ts` exist iff transactions
|
|
29
|
-
* are interactive (S6, P3); `resources/` and `runtime/projection.ts` are not
|
|
30
|
-
* emitted (plan §7).
|
|
31
|
-
*
|
|
32
48
|
* Relative to `generated/`, POSIX-separated.
|
|
33
49
|
*/
|
|
34
50
|
export type GeneratedModulePath = DerivationModulePath | "client.ts" | "contract.ts" | "enums.ts" | "metadata.ts" | "runtime/codec.ts" | "runtime/decimal.ts" | "runtime/descriptor.ts" | "runtime/errors.ts" | "runtime/fingerprint.ts" | "runtime/transaction.ts" | "runtime/transport.ts" | "types.d.ts";
|
|
51
|
+
/**
|
|
52
|
+
* The carrier: the ClientContract the tree was generated against, under
|
|
53
|
+
* `generated/` — the module a rerun reads back to ask for a `304`.
|
|
54
|
+
*/
|
|
55
|
+
export declare const CONTRACT_CARRIER_MODULE = "contract.ts";
|
|
35
56
|
/** A path in the emitted tree: relative to `generateAt`, POSIX-separated. */
|
|
36
57
|
export type EmittedFilePath = typeof CLIENT_ENTRY_FILE | `${typeof GENERATED_DIRECTORY}/${GeneratedModulePath}`;
|
|
37
58
|
/** One module's TypeScript source under `generated/`, before the banner and before encoding. */
|
|
@@ -46,8 +67,8 @@ export interface EmittedFile {
|
|
|
46
67
|
}
|
|
47
68
|
/**
|
|
48
69
|
* Every file one generation emits, in UTF-16 code-unit order of `path`, each path
|
|
49
|
-
* once — `AvClient.ts` first, then `generated/**`. Replaced, never merged
|
|
50
|
-
*
|
|
70
|
+
* once — `AvClient.ts` first, then `generated/**`. Replaced, never merged:
|
|
71
|
+
* `generated/` whole, `AvClient.ts` as one file.
|
|
51
72
|
*/
|
|
52
73
|
export type EmittedTree = readonly EmittedFile[];
|
|
53
74
|
/**
|
|
@@ -57,5 +78,7 @@ export type EmittedTree = readonly EmittedFile[];
|
|
|
57
78
|
*/
|
|
58
79
|
export interface ClientEmission {
|
|
59
80
|
readonly tree: EmittedTree;
|
|
81
|
+
/** The project's spelling the tree was emitted in, and is validated in. */
|
|
82
|
+
readonly style: ClientModuleStyle;
|
|
60
83
|
readonly warnings: readonly string[];
|
|
61
84
|
}
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The generator's output as a VALUE before it is a filesystem effect (plan §7,
|
|
3
|
-
* group 3). Byte-equality is therefore testable without touching disk, and
|
|
4
|
-
* temp → validate → replace (S7) is a property of the writer alone rather than of
|
|
5
|
-
* every emitter.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* The output layout (architect, 2026-10-04): the consumer names a directory,
|
|
9
|
-
* `generateAt`, which is SHARED — they may keep their own files there. The
|
|
10
|
-
* generator owns exactly two entries in it:
|
|
11
|
-
*
|
|
12
|
-
* - `AvClient.ts`, the entry point a consumer imports;
|
|
13
|
-
* - `generated/`, every other module, owned and replaced whole.
|
|
14
|
-
*/
|
|
15
|
-
/** The entry point, at the root of `generateAt`. */
|
|
16
1
|
export const CLIENT_ENTRY_FILE = "AvClient.ts";
|
|
17
|
-
|
|
2
|
+
export const CLIENT_ENTRY_FILES = [CLIENT_ENTRY_FILE];
|
|
3
|
+
export const LEGACY_CLIENT_ENTRY_FILES = [
|
|
4
|
+
"AvClient.d.mts",
|
|
5
|
+
"AvClient.mjs",
|
|
6
|
+
];
|
|
18
7
|
export const GENERATED_DIRECTORY = "generated";
|
|
8
|
+
export const CONTRACT_CARRIER_MODULE = "contract.ts";
|
|
@@ -3,7 +3,7 @@ import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
|
3
3
|
import { type EmittedNames } from "./name.deriver.js";
|
|
4
4
|
/**
|
|
5
5
|
* `enums.ts`: per enum, a string-literal union type and a same-named `as const`
|
|
6
|
-
* object
|
|
6
|
+
* object:
|
|
7
7
|
*
|
|
8
8
|
* ```ts
|
|
9
9
|
* export type Role = "ADMIN" | "USER";
|
|
@@ -17,8 +17,8 @@ import { type EmittedNames } from "./name.deriver.js";
|
|
|
17
17
|
* renamed.
|
|
18
18
|
*
|
|
19
19
|
* Enums come in the order `names` gives them — UTF-16 code units of the contract
|
|
20
|
-
* name — so the key order a contract arrives in never reaches the bytes
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* name — so the key order a contract arrives in never reaches the bytes. An enum's
|
|
21
|
+
* VALUES keep their declared order, in the type and in the object: that order is
|
|
22
|
+
* meaning, and canonical form keeps it too.
|
|
23
23
|
*/
|
|
24
24
|
export declare function emitEnumsModule(contract: Pick<ClientContract, "enums">, names: EmittedNames): EmittedModule;
|
|
@@ -1,24 +1,4 @@
|
|
|
1
1
|
import { ownPropertyKey } from "./name.deriver.js";
|
|
2
|
-
/**
|
|
3
|
-
* `enums.ts`: per enum, a string-literal union type and a same-named `as const`
|
|
4
|
-
* object (§15.4's "enum types"; architect decision, 2026-10-04):
|
|
5
|
-
*
|
|
6
|
-
* ```ts
|
|
7
|
-
* export type Role = "ADMIN" | "USER";
|
|
8
|
-
* export const Role = { ADMIN: "ADMIN", USER: "USER" } as const;
|
|
9
|
-
* ```
|
|
10
|
-
*
|
|
11
|
-
* The type is what every later emitter references; the object gives a consumer a
|
|
12
|
-
* value to name a member by and to enumerate. Both are declared under the enum's
|
|
13
|
-
* emitted identifier, which differs from its contract name only when the name was
|
|
14
|
-
* renamed (`name.deriver.ts`). The values are the wire values and are never
|
|
15
|
-
* renamed.
|
|
16
|
-
*
|
|
17
|
-
* Enums come in the order `names` gives them — UTF-16 code units of the contract
|
|
18
|
-
* name — so the key order a contract arrives in never reaches the bytes (C-843).
|
|
19
|
-
* An enum's VALUES keep their declared order, in the type and in the object: that
|
|
20
|
-
* order is meaning, and canonical form keeps it too.
|
|
21
|
-
*/
|
|
22
2
|
export function emitEnumsModule(contract, names) {
|
|
23
3
|
const declarations = names.enums.map(({ contractName, identifier }) => {
|
|
24
4
|
const values = contract.enums[contractName]?.values ?? [];
|
|
@@ -33,10 +13,6 @@ export function emitEnumsModule(contract, names) {
|
|
|
33
13
|
});
|
|
34
14
|
return {
|
|
35
15
|
path: "enums.ts",
|
|
36
|
-
// Without an import or export, a consumer whose config treats such a file
|
|
37
|
-
// as a script (`moduleResolution: "bundler"` outside a `"type": "module"`
|
|
38
|
-
// package, measured) fails `AvClient.ts`'s `export *` with TS2306 — so a
|
|
39
|
-
// contract without enums still emits a module.
|
|
40
16
|
source: declarations.length === 0 ? "export {};\n" : declarations.join("\n"),
|
|
41
17
|
};
|
|
42
18
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which modules a file names, read without `typescript`: the derivation's copy
|
|
3
|
+
* walks core's declaration closure by it (`derivation.emitter.ts`), and the output
|
|
4
|
+
* validator checks by it that every module the emitted tree names is a file of the
|
|
5
|
+
* tree, spelled the project's way — with or without a compiler.
|
|
6
|
+
*/
|
|
7
|
+
export interface ScannedModule {
|
|
8
|
+
/**
|
|
9
|
+
* Every module specifier: a `from` clause's, a side-effect `import`'s, and an
|
|
10
|
+
* `import()` type query's.
|
|
11
|
+
*/
|
|
12
|
+
readonly specifiers: readonly string[];
|
|
13
|
+
/** Whether the file carries a `/// <reference …/>` directive. */
|
|
14
|
+
readonly references: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The specifiers one module names — a declaration file or JavaScript — read by a
|
|
18
|
+
* small lexer rather than by `typescript` (an optional peer, absent from a
|
|
19
|
+
* consumer's install): comments, string literals, template literals and
|
|
20
|
+
* regular-expression literals are skipped as units, so text inside them is never
|
|
21
|
+
* mistaken for an import. Agrees with TypeScript's own pre-processor over core's
|
|
22
|
+
* closure (`derivation.emitter.spec.ts`) and over every emitted file
|
|
23
|
+
* (`module-specifier.scanner.spec.ts`).
|
|
24
|
+
*/
|
|
25
|
+
export declare function scanModuleSpecifiers(text: string): ScannedModule;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
const SPECIFIER_KEYWORD = /^(?:from|import)$/;
|
|
2
|
+
const EXPRESSION_KEYWORDS = new Set([
|
|
3
|
+
"case",
|
|
4
|
+
"delete",
|
|
5
|
+
"do",
|
|
6
|
+
"else",
|
|
7
|
+
"in",
|
|
8
|
+
"instanceof",
|
|
9
|
+
"new",
|
|
10
|
+
"of",
|
|
11
|
+
"return",
|
|
12
|
+
"throw",
|
|
13
|
+
"typeof",
|
|
14
|
+
"void",
|
|
15
|
+
"yield",
|
|
16
|
+
]);
|
|
17
|
+
export function scanModuleSpecifiers(text) {
|
|
18
|
+
const tokens = [];
|
|
19
|
+
let references = false;
|
|
20
|
+
let index = 0;
|
|
21
|
+
const readQuoted = (quote) => {
|
|
22
|
+
let value = "";
|
|
23
|
+
index += 1;
|
|
24
|
+
while (index < text.length && text[index] !== quote) {
|
|
25
|
+
if (text[index] === "\\") {
|
|
26
|
+
value += text[index + 1] ?? "";
|
|
27
|
+
index += 2;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
value += text[index];
|
|
31
|
+
index += 1;
|
|
32
|
+
}
|
|
33
|
+
index += 1;
|
|
34
|
+
return value;
|
|
35
|
+
};
|
|
36
|
+
const scanCode = (inPlaceholder) => {
|
|
37
|
+
let depth = 0;
|
|
38
|
+
while (index < text.length) {
|
|
39
|
+
const character = text[index];
|
|
40
|
+
const next = text[index + 1];
|
|
41
|
+
if (character === "/" && next === "/") {
|
|
42
|
+
const end = text.indexOf("\n", index);
|
|
43
|
+
const line = text.slice(index, end === -1 ? text.length : end);
|
|
44
|
+
if (/^\/\/\/\s*<reference\b/.test(line)) {
|
|
45
|
+
references = true;
|
|
46
|
+
}
|
|
47
|
+
index = end === -1 ? text.length : end + 1;
|
|
48
|
+
}
|
|
49
|
+
else if (character === "/" && next === "*") {
|
|
50
|
+
const end = text.indexOf("*/", index + 2);
|
|
51
|
+
index = end === -1 ? text.length : end + 2;
|
|
52
|
+
}
|
|
53
|
+
else if (character === "/" && opensRegularExpression(tokens.at(-1))) {
|
|
54
|
+
skipRegularExpression();
|
|
55
|
+
}
|
|
56
|
+
else if (character === '"' || character === "'") {
|
|
57
|
+
tokens.push({ kind: "string", value: readQuoted(character) });
|
|
58
|
+
}
|
|
59
|
+
else if (character === "`") {
|
|
60
|
+
scanTemplate();
|
|
61
|
+
}
|
|
62
|
+
else if (/[A-Za-z_$]/.test(character)) {
|
|
63
|
+
const start = index;
|
|
64
|
+
while (index < text.length && /[\w$]/.test(text[index])) {
|
|
65
|
+
index += 1;
|
|
66
|
+
}
|
|
67
|
+
tokens.push({ kind: "word", value: text.slice(start, index) });
|
|
68
|
+
}
|
|
69
|
+
else if (/\s/.test(character)) {
|
|
70
|
+
index += 1;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
if (inPlaceholder && character === "{") {
|
|
74
|
+
depth += 1;
|
|
75
|
+
}
|
|
76
|
+
else if (inPlaceholder && character === "}") {
|
|
77
|
+
if (depth === 0) {
|
|
78
|
+
index += 1;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
depth -= 1;
|
|
82
|
+
}
|
|
83
|
+
tokens.push({ kind: "punctuation", value: character });
|
|
84
|
+
index += 1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const skipRegularExpression = () => {
|
|
89
|
+
index += 1;
|
|
90
|
+
let inClass = false;
|
|
91
|
+
while (index < text.length && text[index] !== "\n") {
|
|
92
|
+
const character = text[index];
|
|
93
|
+
if (character === "\\") {
|
|
94
|
+
index += 2;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
index += 1;
|
|
98
|
+
if (character === "[") {
|
|
99
|
+
inClass = true;
|
|
100
|
+
}
|
|
101
|
+
else if (character === "]") {
|
|
102
|
+
inClass = false;
|
|
103
|
+
}
|
|
104
|
+
else if (character === "/" && !inClass) {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
while (index < text.length && /[a-z]/.test(text[index])) {
|
|
109
|
+
index += 1;
|
|
110
|
+
}
|
|
111
|
+
tokens.push({ kind: "punctuation", value: "/" });
|
|
112
|
+
};
|
|
113
|
+
const scanTemplate = () => {
|
|
114
|
+
index += 1;
|
|
115
|
+
while (index < text.length && text[index] !== "`") {
|
|
116
|
+
if (text[index] === "\\") {
|
|
117
|
+
index += 2;
|
|
118
|
+
}
|
|
119
|
+
else if (text[index] === "$" && text[index + 1] === "{") {
|
|
120
|
+
index += 2;
|
|
121
|
+
scanCode(true);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
index += 1;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
index += 1;
|
|
128
|
+
tokens.push({ kind: "punctuation", value: "`" });
|
|
129
|
+
};
|
|
130
|
+
scanCode(false);
|
|
131
|
+
const specifiers = [];
|
|
132
|
+
tokens.forEach((token, at) => {
|
|
133
|
+
const following = tokens[at + 1];
|
|
134
|
+
if (token.kind !== "word") {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (SPECIFIER_KEYWORD.test(token.value) && following?.kind === "string") {
|
|
138
|
+
specifiers.push(following.value);
|
|
139
|
+
}
|
|
140
|
+
else if (token.value === "import" &&
|
|
141
|
+
following?.kind === "punctuation" &&
|
|
142
|
+
following.value === "(" &&
|
|
143
|
+
tokens[at + 2]?.kind === "string") {
|
|
144
|
+
specifiers.push(tokens[at + 2].value);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return { specifiers, references };
|
|
148
|
+
}
|
|
149
|
+
function opensRegularExpression(previous) {
|
|
150
|
+
if (previous === undefined) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (previous.kind === "word") {
|
|
154
|
+
return EXPRESSION_KEYWORDS.has(previous.value);
|
|
155
|
+
}
|
|
156
|
+
return (previous.kind === "punctuation" &&
|
|
157
|
+
previous.value !== ")" &&
|
|
158
|
+
previous.value !== "]" &&
|
|
159
|
+
previous.value !== "`");
|
|
160
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How the generated client's modules are spelled for the project that compiles
|
|
3
|
+
* them. The client is TypeScript source — `.ts` files — and the developer's own
|
|
4
|
+
* toolchain compiles it like their own files, so the relative specifiers by which
|
|
5
|
+
* its modules name each other, and the module format they are checked in, follow
|
|
6
|
+
* that project's rules. The names and the rules are Prisma 7's `prisma-client`
|
|
7
|
+
* generator's (`importFileExtension`, `moduleFormat`), inferred from the project's
|
|
8
|
+
* `tsconfig.json` (`config/module-style.resolver.ts`).
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The extension a relative import inside the generated client is written with:
|
|
12
|
+
*
|
|
13
|
+
* - `""` — extensionless, `./generated/client`: a bundler (`moduleResolution:
|
|
14
|
+
* "bundler"`) or CommonJS (`module: "commonjs"`) project, where the developer's
|
|
15
|
+
* own imports carry none;
|
|
16
|
+
* - `"js"` — `./generated/client.js`: Node's ES module resolution
|
|
17
|
+
* (`node16`/`nodenext`), where the import names the file `tsc` will emit;
|
|
18
|
+
* - `"ts"` — `./generated/client.ts`: a project that imports TypeScript files by
|
|
19
|
+
* their own name (`allowImportingTsExtensions`, `rewriteRelativeImportExtensions`)
|
|
20
|
+
* — Node's type stripping, Deno, Bun.
|
|
21
|
+
*
|
|
22
|
+
* Prisma's list is wider (`mts`, `cts`, `mjs`, `cjs`): those pair with a
|
|
23
|
+
* generated `.mts` or `.cts`, and this generator emits `.ts` only.
|
|
24
|
+
*/
|
|
25
|
+
export type ImportFileExtension = "" | "js" | "ts";
|
|
26
|
+
/**
|
|
27
|
+
* The module format the project compiles the generated `.ts` files to: what
|
|
28
|
+
* its `tsconfig.json` says (`module`), or, under `node16`/`nodenext`, what the
|
|
29
|
+
* nearest `package.json`'s `"type"` says. The emitted bytes are the same in
|
|
30
|
+
* either — they use no construct one format lacks — but the output validator
|
|
31
|
+
* type-checks them in this one, as the project will.
|
|
32
|
+
*/
|
|
33
|
+
export type ModuleFormat = "esm" | "cjs";
|
|
34
|
+
/** One project's spelling of the generated client, resolved once per run. */
|
|
35
|
+
export interface ClientModuleStyle {
|
|
36
|
+
readonly importFileExtension: ImportFileExtension;
|
|
37
|
+
readonly moduleFormat: ModuleFormat;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A relative module path without an extension — `./contract`,
|
|
41
|
+
* `../metadata`, `./derivation/operations/operation-call` — as an emitter names
|
|
42
|
+
* the module it imports.
|
|
43
|
+
*/
|
|
44
|
+
export type ExtensionlessModulePath = `./${string}` | `../${string}`;
|
|
45
|
+
/**
|
|
46
|
+
* The specifier a generated module imports `module` by, in this style. One rule
|
|
47
|
+
* for every target, `.ts` source and `.d.ts` declaration alike: TypeScript
|
|
48
|
+
* resolves an extensionless, a `.js` and a `.ts` specifier to either (measured
|
|
49
|
+
* under `nodenext` and `bundler`, TypeScript 5.9, 6 and 7).
|
|
50
|
+
*/
|
|
51
|
+
export declare function importSpecifier(module: ExtensionlessModulePath, style: Pick<ClientModuleStyle, "importFileExtension">): string;
|
|
52
|
+
/**
|
|
53
|
+
* Writes the quoted specifier an emitted `import`/`export … from` names a
|
|
54
|
+
* module by: `importSpecifier`, as a string literal of the generated source.
|
|
55
|
+
*/
|
|
56
|
+
export type ModuleSpecifierWriter = (module: ExtensionlessModulePath) => string;
|
|
57
|
+
/** The {@link ModuleSpecifierWriter} of one style. */
|
|
58
|
+
export declare function moduleSpecifierWriter(style: Pick<ClientModuleStyle, "importFileExtension">): ModuleSpecifierWriter;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function importSpecifier(module, style) {
|
|
2
|
+
return style.importFileExtension === ""
|
|
3
|
+
? module
|
|
4
|
+
: `${module}.${style.importFileExtension}`;
|
|
5
|
+
}
|
|
6
|
+
export function moduleSpecifierWriter(style) {
|
|
7
|
+
return (module) => JSON.stringify(importSpecifier(module, style));
|
|
8
|
+
}
|
|
@@ -2,15 +2,10 @@ import { type ClientContract, type OperationFamily } from "@aventara/core";
|
|
|
2
2
|
/**
|
|
3
3
|
* Name derivation and the rename ladder for the emitted tree.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* rename is the TypeScript identifier's alone. The name the server uses — the
|
|
10
|
-
* wire name a Resource is addressed by — is kept beside it unchanged
|
|
11
|
-
* (`EmittedName.contractName`), so a later emitter that sends a request reads that,
|
|
12
|
-
* never the identifier. Every rename is one warning line on the result; generation
|
|
13
|
-
* does not print.
|
|
5
|
+
* The name the server uses — the wire name a Resource is addressed by — is kept
|
|
6
|
+
* beside it unchanged (`EmittedName.contractName`), so a later emitter that sends
|
|
7
|
+
* a request reads that, never the identifier. Every rename is one warning line on
|
|
8
|
+
* the result; generation does not print.
|
|
14
9
|
*
|
|
15
10
|
* A name is unusable as-is when it is not an identifier, is a reserved word, is a
|
|
16
11
|
* predefined type's name, is a name the generated runtime owns, or — an enum only
|
|
@@ -20,66 +15,44 @@ import { type ClientContract, type OperationFamily } from "@aventara/core";
|
|
|
20
15
|
* - Resource `X`: `XModel` → `ResourceX` → `X_` → `_X` → `_X_` → refused;
|
|
21
16
|
* - enum `X`: `XEnum` → `EnumX` → `X_` → refused.
|
|
22
17
|
*
|
|
23
|
-
* Only a name whose whole ladder is unusable or taken is refused, in one sentence
|
|
24
|
-
* and a name no rename can repair, which is refused without trying a rung: the
|
|
18
|
+
* Only a name whose whole ladder is unusable or taken is refused, in one sentence
|
|
19
|
+
* — and a name no rename can repair, which is refused without trying a rung: the
|
|
25
20
|
* empty name. Every rung of its ladder is a bare affix (`Model`, `Enum`, `_`) that
|
|
26
21
|
* names nothing on the server, so "renaming" it would invent an identifier rather
|
|
27
|
-
* than repair one
|
|
22
|
+
* than repair one.
|
|
28
23
|
*
|
|
29
24
|
* # Determinism
|
|
30
25
|
*
|
|
31
26
|
* The same Contract always yields the same names, whatever order its keys arrive
|
|
32
|
-
* in: every name usable as-is is claimed first (so a usable name is never
|
|
33
|
-
* by another's rename), then Resources are renamed, then enums, each
|
|
34
|
-
* UTF-16 code-unit order of its contract names. An enum sharing a
|
|
35
|
-
* walks its ladder and the Resource keeps the name.
|
|
27
|
+
* in: every name usable as-is is claimed first (so a usable name is never
|
|
28
|
+
* displaced by another's rename), then Resources are renamed, then enums, each
|
|
29
|
+
* registry in UTF-16 code-unit order of its contract names. An enum sharing a
|
|
30
|
+
* Resource's name walks its ladder and the Resource keeps the name.
|
|
36
31
|
*
|
|
37
32
|
* # One namespace
|
|
38
33
|
*
|
|
39
|
-
*
|
|
40
|
-
* each other and with the runtime's own exports. §15.6 names a Resource's type by
|
|
41
|
-
* the Resource's name (`Spell`), so that namespace is claimed now, before the
|
|
42
|
-
* Resource types are emitted. Names are case-sensitive.
|
|
43
|
-
*
|
|
44
|
-
* # Derived names (Phase 12-rest Q10; the ladder rule, a plan ruling)
|
|
45
|
-
*
|
|
46
|
-
* §15.6 derives named types from a Resource (`SpellWhere`, `SpellOrderBy`, …;
|
|
47
|
-
* {@link RESOURCE_NAMED_TYPES}), each only when the operation it reads is
|
|
48
|
-
* advertised. A Resource's base name is taken at the first rung — its own name
|
|
49
|
-
* first, when usable — where the base AND every name derived from it are free of
|
|
50
|
-
* every other emitted name: `User` beside a Resource `UserWhere` walks to
|
|
51
|
-
* `UserModel`, one warning. Derived names a Resource claims are claimed for good,
|
|
52
|
-
* so a later Resource or enum never takes one. The generated modules that declare
|
|
53
|
-
* these names use no global type a Resource could shadow (`types.d.ts` imports two
|
|
54
|
-
* helpers, which are reserved).
|
|
34
|
+
* Names are case-sensitive.
|
|
55
35
|
*
|
|
56
|
-
*
|
|
36
|
+
* A Resource's base name is taken at the first rung — its own name first, when
|
|
37
|
+
* usable — where the base AND every name derived from it are free of every other
|
|
38
|
+
* emitted name: `User` beside a Resource `UserWhere` walks to `UserModel`, one
|
|
39
|
+
* warning. Derived names a Resource claims are claimed for good, so a later
|
|
40
|
+
* Resource or enum never takes one. The generated modules that declare these names
|
|
41
|
+
* use no global type a Resource could shadow (`types.d.ts` imports two helpers,
|
|
42
|
+
* which are reserved).
|
|
57
43
|
*
|
|
58
|
-
* A Resource is reached as a property of the client, named by its contract name.
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
44
|
+
* A Resource is reached as a property of the client, named by its contract name. A
|
|
45
|
+
* contract name equal to one of the client's own members ({@link
|
|
46
|
+
* CLIENT_MEMBER_NAMES}) walks the Resource ladder for its PROPERTY only, one
|
|
47
|
+
* warning; the wire name, and the TypeScript type names, are unaffected.
|
|
62
48
|
*
|
|
63
49
|
* Reads registry KEYS and which operations are advertised. No capability member.
|
|
64
50
|
*/
|
|
65
51
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* `avClient` singleton, `AvClient`, `AvClientOptions`, `CallOptions`, `Fetch`,
|
|
69
|
-
* `Decimal`, `FrameworkError`, `NotFoundError`, `TransportError`,
|
|
70
|
-
* `OperationCode`/`ValidationCode` and `Operation<T>`, plus `Cause` and
|
|
71
|
-
* `ValidationIssue` (public by the architect's decision of 2026-10-04) — every
|
|
72
|
-
* `FrameworkError` subclass Q2 adds, read from the runtime emitter that declares
|
|
73
|
-
* them, and the two helpers `types.d.ts` imports to declare the named types
|
|
74
|
-
* (`ResourceArgument`, `ResourceRecord`). A Contract name equal to one of them
|
|
75
|
-
* walks its rename ladder. In UTF-16 code-unit order.
|
|
52
|
+
* A Contract name equal to one of them walks its rename ladder. In UTF-16
|
|
53
|
+
* code-unit order.
|
|
76
54
|
*/
|
|
77
55
|
export declare const RESERVED_EMITTED_NAMES: readonly string[];
|
|
78
|
-
/**
|
|
79
|
-
* One of §15.6's named Resource types (Q10 = a): the suffix it adds to the
|
|
80
|
-
* Resource's name, the operation it reads — it exists only when that operation is
|
|
81
|
-
* advertised — and the argument it names, or none for the default record.
|
|
82
|
-
*/
|
|
83
56
|
export interface ResourceNamedType {
|
|
84
57
|
readonly suffix: string;
|
|
85
58
|
readonly family: OperationFamily;
|
|
@@ -87,18 +60,17 @@ export interface ResourceNamedType {
|
|
|
87
60
|
/** The argument it names; absent for `X`, the default record. */
|
|
88
61
|
readonly argument?: "where" | "orderBy" | "data" | "select" | "include";
|
|
89
62
|
}
|
|
90
|
-
/** §15.6's set exactly (Q10 = a), in §15.6's order. */
|
|
91
63
|
export declare const RESOURCE_NAMED_TYPES: readonly ResourceNamedType[];
|
|
92
64
|
/** The named types a Resource with `operations` gets: those whose operation it advertises. */
|
|
93
65
|
export declare function namedTypesOf(operations: unknown): readonly ResourceNamedType[];
|
|
94
66
|
/**
|
|
95
|
-
* The client's own members, which a Resource property must not shadow
|
|
96
|
-
* `
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
67
|
+
* The client's own members, which a Resource property must not shadow: `tx` and
|
|
68
|
+
* `transaction` (reserved whether or not the contract advertises transactions, so
|
|
69
|
+
* a property never moves with `transactions`), `then` — a client with a `then` is
|
|
70
|
+
* a thenable, and `await` would call it — and every name `Object.prototype`
|
|
71
|
+
* carries in ES2022, `constructor` among them. Fixed here, not read from the
|
|
72
|
+
* running Node, so the output never moves with the generator's runtime. In UTF-16
|
|
73
|
+
* code-unit order.
|
|
102
74
|
*/
|
|
103
75
|
export declare const CLIENT_MEMBER_NAMES: readonly string[];
|
|
104
76
|
/** One Contract name and the TypeScript identifier the tree declares it under. */
|
|
@@ -108,7 +80,7 @@ export interface EmittedName {
|
|
|
108
80
|
/** The identifier the emitted TypeScript declares; `contractName` unless renamed. */
|
|
109
81
|
readonly identifier: string;
|
|
110
82
|
}
|
|
111
|
-
/** A Resource's property on the client: its contract name unless a member owns it
|
|
83
|
+
/** A Resource's property on the client: its contract name unless a member owns it. */
|
|
112
84
|
export interface EmittedProperty {
|
|
113
85
|
/** The name the server uses. Never renamed. */
|
|
114
86
|
readonly contractName: string;
|