@aventara/client 0.1.0-pilot.2 → 0.1.0-pilot.4
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 +11 -13
- package/dist/cli/command.parser.d.ts +1 -13
- package/dist/cli/generation-success.renderer.d.ts +0 -4
- package/dist/cli/terminal.prompter.d.ts +0 -6
- package/dist/cli/warning.renderer.d.ts +0 -6
- package/dist/cli.d.ts +0 -16
- package/dist/config/client-config.interface.d.ts +10 -20
- package/dist/config/config.loader.d.ts +0 -21
- package/dist/config/config.resolver.d.ts +2 -17
- package/dist/config/env.cascade.d.ts +0 -30
- package/dist/config/module-style.resolver.d.ts +0 -25
- package/dist/config/tsconfig.locator.d.ts +0 -24
- package/dist/contract/contract.acceptance.d.ts +0 -39
- package/dist/contract/contract.fetcher.d.ts +0 -29
- package/dist/contract/contract.loader.d.ts +0 -7
- package/dist/emit/banner.emitter.d.ts +0 -18
- package/dist/emit/banner.emitter.js +1 -1
- package/dist/emit/client-surface.emitter.d.ts +0 -23
- package/dist/emit/client-tree.emitter.d.ts +0 -20
- package/dist/emit/contract-carrier.emitter.d.ts +0 -7
- package/dist/emit/derivation.emitter.d.ts +0 -28
- package/dist/emit/emitted-tree.interface.d.ts +0 -53
- package/dist/emit/enum.emitter.d.ts +0 -20
- package/dist/emit/module-specifier.scanner.d.ts +0 -15
- package/dist/emit/module-style.interface.d.ts +0 -15
- package/dist/emit/name.deriver.d.ts +0 -71
- package/dist/emit/named-type.emitter.d.ts +0 -20
- package/dist/emit/runtime.emitter.d.ts +0 -50
- package/dist/emit/runtime.emitter.js +12 -19
- package/dist/emit/scalar.codec.d.ts +0 -39
- package/dist/emit/transaction.emitter.d.ts +0 -6
- package/dist/emit/transaction.emitter.js +3 -5
- package/dist/generate.d.ts +0 -33
- package/dist/index.d.ts +1 -5
- package/dist/init/client-config.template.d.ts +0 -8
- package/dist/init/client-init.orchestrator.js +7 -0
- package/dist/init/client-init.planner.d.ts +0 -1
- package/dist/init/client-init.planner.js +10 -0
- package/dist/init/client-init.questions.d.ts +0 -8
- package/dist/output/output.validator.d.ts +16 -33
- package/dist/output/output.validator.js +68 -16
- package/dist/output/output.writer.d.ts +1 -107
- package/dist/output/output.writer.js +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ client is the product: it imports nothing from this package, or from `@aventara/
|
|
|
6
6
|
|
|
7
7
|
Every argument and result type in the generated client is core's own derivation — `@aventara/core`'s published
|
|
8
8
|
declarations, copied into the tree and instantiated over the deployment's ClientContract — so the client agrees with the
|
|
9
|
-
server by construction
|
|
9
|
+
server by construction.
|
|
10
10
|
|
|
11
11
|
## Setting up a frontend: `avclient init`
|
|
12
12
|
|
|
@@ -91,8 +91,9 @@ supported yet.
|
|
|
91
91
|
- **A failed run leaves the previous output exactly as it was.** The whole tree is written to a staging directory inside
|
|
92
92
|
`generateAt`, validated there as one program, and only then moved into place. A run killed part-way is repaired by the
|
|
93
93
|
next run that writes, before it writes anything. A first run that fails removes the directories it created.
|
|
94
|
-
- **The validate step is a real type check** when the optional peer `typescript` (5.5 to 6)
|
|
95
|
-
|
|
94
|
+
- **The validate step is a real type check** when your project has the optional peer `typescript` (5.5 to 6) — it is
|
|
95
|
+
resolved from your project, so `npx @aventara/client@pilot init` type-checks too — under a consumer's strictest plausible
|
|
96
|
+
settings and with nothing outside the tree resolvable. Without it — or under
|
|
96
97
|
TypeScript 7, which has no classic compiler API to check with — the check degrades to a parse with Node's own
|
|
97
98
|
TypeScript parser, and says so loudly in one warning; it is never skipped, and the client is still written. The peer
|
|
98
99
|
range has no upper bound, so a frontend on TypeScript 7 installs it; your own `tsc` checks the tree when it compiles.
|
|
@@ -209,11 +210,9 @@ chose. The `.d.ts` files are core's copied declarations under `generated/derivat
|
|
|
209
210
|
`UserUniqueWhere`, `UserOrderBy`, `UserCreateData`, `UserUpdateData`, `UserSelect`, `UserInclude`), each only when
|
|
210
211
|
its operation is advertised.
|
|
211
212
|
|
|
212
|
-
The tree is self-contained: every import in it names a file in it
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
reserved word, a name the runtime owns, an enum sharing a Resource's name) are renamed, with one warning each; the wire
|
|
216
|
-
name is kept.
|
|
213
|
+
The tree is self-contained: every import in it names a file in it, so it compiles with nothing else installed. Names a TypeScript
|
|
214
|
+
declaration cannot carry as-is (a reserved word, a name the runtime owns, an enum sharing a Resource's name) are
|
|
215
|
+
renamed, with one warning each; the wire name is kept.
|
|
217
216
|
|
|
218
217
|
## What a generated client is bound to
|
|
219
218
|
|
|
@@ -236,11 +235,10 @@ doing so re-binds nothing — the ClientContract hash still decides whether that
|
|
|
236
235
|
|
|
237
236
|
## Type-checking cost in a consumer
|
|
238
237
|
|
|
239
|
-
A consumer pays for the calls it type-checks, not for the schema
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
program, the 500,000 line near 150 Resources.
|
|
238
|
+
A consumer pays for the calls it type-checks, not for the schema, under `skipLibCheck: true` (the common default):
|
|
239
|
+
the named types (`generated/types.d.ts`) and core's derivation are declaration files checked only where read. Under
|
|
240
|
+
`skipLibCheck: false` every named type is resolved where it is declared, so the cost grows with the number of
|
|
241
|
+
Resources.
|
|
244
242
|
|
|
245
243
|
## Before 1.0
|
|
246
244
|
|
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `avclient` command line: one command, its one flag, and help. Everything the
|
|
3
|
-
* generator needs is in `framework.client.ts` and the `.env` cascade, so
|
|
4
|
-
* `generate` takes no flag that would duplicate a config member — a second source
|
|
5
|
-
* of the same fact. `--yes` is not one: it answers the one question the generator
|
|
6
|
-
* asks.
|
|
7
|
-
*/
|
|
8
1
|
export declare const USAGE = "avclient \u2014 generate a typed Aventara client from a deployed ClientContract.\n\nUsage:\n avclient generate [--yes] Read framework.client.ts (or .js, .mjs, .cjs, .mts,\n .cts) in the current directory, fetch\n <entrypoint>/_contract, and write AvClient.ts and\n generated/ into its generateAt directory, its imports\n spelled as the project's tsconfig.json says.\n avclient init [options] Set up this frontend: write framework.client.ts, the\n .env entry, the avclient:generate script and the\n @aventara/client devDependency; install; generate.\n --entrypoint <url> The server's entrypoint [http://localhost:3000/api].\n --env-var <NAME> Read it from this variable [AVENTARA_API_URL].\n --no-env-var Write it into framework.client.ts as a literal.\n --generate-at <dir> Where the client goes [./src/api].\n --package-manager <npm|pnpm> [the lockfile's, else the launching one, else npm]\n --skip-install Print the install instead of running it.\n --skip-generate Do not generate now.\n -y, --yes Accept every default, and replace differing content.\n avclient --help Print this and exit 0.\n avclient <command> --help Print that command's usage and exit 0.\n avclient --version, -v Print this generator's version and exit 0.\n\nOptions:\n -y, --yes Overwrite or remove content in AvClient.ts and generated/ that the\n generator did not produce, without asking. Without it, such content\n is listed and you are asked; where nobody can answer (stdin is not a\n terminal, as in CI), the run is refused and nothing is touched.\n\nThe generator owns AvClient.ts and generated/ in generateAt, and nothing else\nthere: your own files beside them are never read, moved or removed. Generated\nfiles are replaced on every run; do not edit them.\n\nBefore framework.client.ts is evaluated, the .env cascade is read from the\ncurrent directory, highest precedence first: the process environment,\n.env.<mode>.local, .env.<mode>, .env.local, .env \u2014 where mode is NODE_ENV, or\n\"development\".\n";
|
|
9
|
-
/** `avclient generate --help` (pilot.1): that command's usage alone. */
|
|
10
2
|
export declare const GENERATE_USAGE = "Usage: avclient generate [--yes]\n\nRead framework.client.ts (or .js, .mjs, .cjs, .mts, .cts) in the current directory,\nfetch <entrypoint>/_contract, and write AvClient.ts and generated/ into its\ngenerateAt directory, its imports spelled as the project's tsconfig.json says.\n\nOptions:\n -y, --yes Overwrite or remove content in AvClient.ts and generated/ that the\n generator did not produce, without asking. Without it, such content\n is listed and you are asked; where nobody can answer (stdin is not a\n terminal, as in CI), the run is refused and nothing is touched.\n\nThe generator owns AvClient.ts and generated/ in generateAt, and nothing else\nthere: your own files beside them are never read, moved or removed.\n\nBefore the config is evaluated, the .env cascade is read from the current\ndirectory, highest precedence first: the process environment, .env.<mode>.local,\n.env.<mode>, .env.local, .env \u2014 where mode is NODE_ENV, or \"development\".\n";
|
|
11
|
-
/** `avclient init --help` (pilot.1): that command's usage alone. */
|
|
12
3
|
export declare const INIT_USAGE = "Usage: avclient init [options]\n\nSet up this frontend: write framework.client.ts, the .env entry, the avclient:generate\nscript and the @aventara/client devDependency; install; generate.\n\nOptions:\n --entrypoint <url> The server's entrypoint [http://localhost:3000/api].\n --env-var <NAME> Read it from this variable [AVENTARA_API_URL].\n --no-env-var Write it into framework.client.ts as a literal.\n --generate-at <dir> Where the client goes [./src/api].\n --package-manager <npm|pnpm> [the lockfile's, else the launching one, else npm]\n --skip-install Print the install instead of running it.\n --skip-generate Do not generate now.\n -y, --yes Accept every default, and replace differing content.\n\nOn a terminal every unanswered question is asked; anywhere else, pass its flag\nor --yes, or the run stops before writing anything.\n";
|
|
13
|
-
/** `avclient init`'s command line. */
|
|
14
4
|
export type ClientInitCommand = {
|
|
15
5
|
readonly command: "init";
|
|
16
6
|
readonly given: Readonly<Partial<Record<"entrypoint" | "envVar" | "generateAt" | "packageManager", string>>>;
|
|
@@ -20,9 +10,7 @@ export type ClientInitCommand = {
|
|
|
20
10
|
readonly yes: boolean;
|
|
21
11
|
};
|
|
22
12
|
/** What the command line asked for. */
|
|
23
|
-
export type CliCommand =
|
|
24
|
-
/** `--help`; with `topic`, `avclient <topic> --help` (pilot.1). */
|
|
25
|
-
{
|
|
13
|
+
export type CliCommand = {
|
|
26
14
|
readonly command: "help";
|
|
27
15
|
readonly topic?: "generate" | "init";
|
|
28
16
|
} | {
|
|
@@ -13,10 +13,6 @@ export interface GenerationSuccessReport {
|
|
|
13
13
|
readonly stderr: string;
|
|
14
14
|
}
|
|
15
15
|
export declare function renderGenerationSuccess(result: ClientGenerated): GenerationSuccessReport;
|
|
16
|
-
/**
|
|
17
|
-
* Nothing written: the deployment's ClientContract and these bytes are what is
|
|
18
|
-
* there.
|
|
19
|
-
*/
|
|
20
16
|
export declare function renderUpToDate(result: ClientUpToDate): GenerationSuccessReport;
|
|
21
17
|
/**
|
|
22
18
|
* What the person running the generator is told before being asked: every path
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The terminal's questions — `generate`'s yes/no and `init`'s wizard — over one
|
|
3
|
-
* `node:readline` interface whose lines are **queued**: a line typed (or piped
|
|
4
|
-
* into a pseudo-terminal) before its question is kept for it, where a fresh
|
|
5
|
-
* interface per question, or `readline/promises`' `question`, drops it.
|
|
6
|
-
*/
|
|
7
1
|
export type TerminalPrompter = {
|
|
8
2
|
readonly ask: (prompt: string) => Promise<string>;
|
|
9
3
|
readonly confirm: (question: string) => Promise<boolean>;
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* How one warning reaches the person running the generator: its own stderr line,
|
|
3
|
-
* under the CLI's one prefix. Warnings carry no prefix of their own, so the line
|
|
4
|
-
* says "warning" once. Shared by a success and a refusal, so a warning reads the
|
|
5
|
-
* same either way.
|
|
6
|
-
*/
|
|
7
1
|
/** The prefix every warning line opens with. */
|
|
8
2
|
export declare const WARNING_LINE_PREFIX = "avclient: warning: ";
|
|
9
3
|
/** One newline-terminated line per warning, in the order given; empty for none. */
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,21 +1,5 @@
|
|
|
1
1
|
import { type CliIo } from "./cli/generate.command.js";
|
|
2
2
|
export type { CliIo } from "./cli/generate.command.js";
|
|
3
|
-
/**
|
|
4
|
-
* It is also the one place that ASKS: when content the generator did not produce
|
|
5
|
-
* stands in `AvClient.ts` or `generated/`, the paths are listed and the person is
|
|
6
|
-
* asked whether to overwrite them. `--yes` answers for them. Where nobody can
|
|
7
|
-
* answer — stdin is not a terminal — it never asks and never overrides: it
|
|
8
|
-
* refuses, naming `--yes`. Cancelled or refused, nothing was touched, the run's
|
|
9
|
-
* warnings are printed before the sentence, and the exit code is 1. A killed run's
|
|
10
|
-
* leftovers are looked at before the question, so the answer — and `--yes` —
|
|
11
|
-
* covers what it hid as well.
|
|
12
|
-
*/
|
|
13
3
|
/** Runs one command line; resolves to the exit code. Never throws. */
|
|
14
4
|
export declare function runCli(argv: readonly string[], io: CliIo): Promise<number>;
|
|
15
|
-
/**
|
|
16
|
-
* Runs `avclient` over this process — its arguments, its terminal, its exit
|
|
17
|
-
* code. Called by the bin's entry (`avclient.bin.ts`) once the Node guard has
|
|
18
|
-
* admitted this Node; importing this module runs nothing, which is what lets
|
|
19
|
-
* `runCli` be tested in process.
|
|
20
|
-
*/
|
|
21
5
|
export declare function runFromProcess(): Promise<void>;
|
|
@@ -1,33 +1,31 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The generator's configuration, in its two states.
|
|
3
|
-
*
|
|
4
|
-
* `ClientConfigInput` is what a `framework.client.ts` default-exports through
|
|
5
|
-
* `defineClientConfig`. It is passive data: an environment variable appears as an
|
|
6
|
-
* `EnvReference`, a NAME to look up, so the file can be evaluated without the
|
|
7
|
-
* environment having been loaded into the global and resolution stays a pure
|
|
8
|
-
* function of the input and an `EnvRecord`.
|
|
9
|
-
*/
|
|
10
1
|
/** A deferred read of one environment variable from the resolved cascade. */
|
|
11
2
|
export interface EnvReference {
|
|
3
|
+
/** Always `"env"`. */
|
|
12
4
|
readonly kind: "env";
|
|
5
|
+
/** The environment variable's name. */
|
|
13
6
|
readonly name: string;
|
|
14
7
|
}
|
|
15
8
|
/** A value written literally, or read from the resolved environment. */
|
|
16
9
|
export type ConfigValue = string | EnvReference;
|
|
10
|
+
/**
|
|
11
|
+
* What a `framework.client.ts` default-exports through `defineClientConfig`.
|
|
12
|
+
* Plain data: an environment variable appears as an `EnvReference` (`env("NAME")`),
|
|
13
|
+
* a name to look up, so the file can be evaluated before the environment is loaded.
|
|
14
|
+
*/
|
|
17
15
|
export interface ClientConfigInput {
|
|
18
16
|
/** The full deployed framework entrypoint: origin plus mount path. */
|
|
19
17
|
readonly entrypoint: ConfigValue;
|
|
20
18
|
/**
|
|
21
19
|
* The directory the client is generated into, relative to the config file's
|
|
22
|
-
* directory. It is
|
|
23
|
-
* in it, and never touches anything else there.
|
|
20
|
+
* directory. It is shared: the generator writes only `AvClient.ts` and
|
|
21
|
+
* `generated/` in it, and never touches anything else there.
|
|
24
22
|
*/
|
|
25
23
|
readonly generateAt: ConfigValue;
|
|
26
24
|
/**
|
|
27
25
|
* The `tsconfig.json` the generated client is written for, relative to the
|
|
28
26
|
* config file's directory — only for a layout the discovery gets wrong (a
|
|
29
27
|
* monorepo, a non-standard name). Absent, the nearest `tsconfig.json` above
|
|
30
|
-
* `generateAt`
|
|
28
|
+
* `generateAt` is used.
|
|
31
29
|
*/
|
|
32
30
|
readonly tsconfigFile?: string;
|
|
33
31
|
}
|
|
@@ -35,17 +33,9 @@ export interface ClientConfigInput {
|
|
|
35
33
|
export type AbsolutePath = string & {
|
|
36
34
|
readonly __absolutePath: true;
|
|
37
35
|
};
|
|
38
|
-
/**
|
|
39
|
-
* Root is the empty string, not `"/"`, so every route is `path + "/<route>"` with
|
|
40
|
-
* no special case. Only resolution makes one.
|
|
41
|
-
*/
|
|
42
36
|
export type EntrypointPath = ("" | `/${string}`) & {
|
|
43
37
|
readonly __entrypointPath: true;
|
|
44
38
|
};
|
|
45
|
-
/**
|
|
46
|
-
* The deployed entrypoint, normalised once at resolution so every consumer reads
|
|
47
|
-
* one canonical form. Joining a route onto it is concatenation.
|
|
48
|
-
*/
|
|
49
39
|
export interface ClientEntrypoint {
|
|
50
40
|
/**
|
|
51
41
|
* The deployment the entrypoint addresses: scheme, credentials, host and port.
|
|
@@ -1,25 +1,4 @@
|
|
|
1
1
|
import type { ClientConfigInput } from "./client-config.interface.js";
|
|
2
|
-
/**
|
|
3
|
-
* Finds and evaluates the client config, `framework.client.<ext>`, the way
|
|
4
|
-
* Prisma 7 loads `prisma.config.ts` (`@prisma/config` 7.10's
|
|
5
|
-
* `loadConfigFromFile`): through `c12`, which evaluates it with `jiti`
|
|
6
|
-
* (`interopDefault`, no module cache), with no `.env` loading, no rc file, no
|
|
7
|
-
* remote or `extends` layers and no `package.json` key. So any of
|
|
8
|
-
* {@link CLIENT_CONFIG_FILE_EXTENSIONS} — Prisma's own list — in ES module or
|
|
9
|
-
* CommonJS syntax, whatever the project's `"type"`, TypeScript included, with
|
|
10
|
-
* no compiler of the project's needed (pilot.1's `.mts` rule is gone: jiti, not
|
|
11
|
-
* Node, decides how the file is read).
|
|
12
|
-
*
|
|
13
|
-
* One file, looked up in one directory — no search up the tree — and when more
|
|
14
|
-
* than one is present the run refuses rather than choose: two files would be
|
|
15
|
-
* two sources of the same fact. What the file imports — `@aventara/client` for
|
|
16
|
-
* `defineClientConfig` and `env` — resolves from the consumer's project, as any
|
|
17
|
-
* import of theirs would.
|
|
18
|
-
*
|
|
19
|
-
* The evaluated value is checked against `ClientConfigInput`'s shape before it
|
|
20
|
-
* is trusted: the config is the consumer's code, and a typo there should be a
|
|
21
|
-
* sentence naming the member, not a `TypeError` from deep inside resolution.
|
|
22
|
-
*/
|
|
23
2
|
/** Prisma's `SUPPORTED_EXTENSIONS`, in its order. */
|
|
24
3
|
export declare const CLIENT_CONFIG_FILE_EXTENSIONS: readonly [".js", ".ts", ".mjs", ".cjs", ".mts", ".cts"];
|
|
25
4
|
/** The config's name, before its extension. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ClientConfigInput, ClientEntrypoint, EnvReference, ResolvedClientConfig } from "./client-config.interface.js";
|
|
2
2
|
import type { EnvCascadeResolution } from "./env.cascade.js";
|
|
3
|
-
/**
|
|
3
|
+
/** Gives a `framework.client.ts` its type: returns `config` unchanged. */
|
|
4
4
|
export declare function defineClientConfig(config: ClientConfigInput): ClientConfigInput;
|
|
5
|
-
/** Names an environment variable to be read from the resolved
|
|
5
|
+
/** Names an environment variable to be read from the resolved environment. */
|
|
6
6
|
export declare function env(name: string): EnvReference;
|
|
7
7
|
/**
|
|
8
8
|
* The configuration cannot be resolved. The message is the whole diagnosis — the
|
|
@@ -23,22 +23,7 @@ export interface ClientConfigResolutionInput {
|
|
|
23
23
|
*/
|
|
24
24
|
export declare function resolveClientConfig(input: ClientConfigResolutionInput): ResolvedClientConfig;
|
|
25
25
|
/**
|
|
26
|
-
* An entrypoint value resolved to its canonical form. The URL half is this
|
|
27
|
-
* function's: whitespace (which the URL parser would trim or drop unseen), a value
|
|
28
|
-
* that is not an absolute URL, a scheme other than http(s), credentials, a query
|
|
29
|
-
* or fragment (even an empty one, which the parser forgets), and a `..` segment
|
|
30
|
-
* (which the parser would resolve away) are refused here, reading the value as
|
|
31
|
-
* written.
|
|
32
|
-
*
|
|
33
|
-
* Only its slash spelling is coerced — `api`, `/api/` and `//api//` are one
|
|
34
|
-
* intent, `/api` — and root is `""`.
|
|
35
|
-
*
|
|
36
26
|
* @throws ClientConfigError in one sentence that does not echo the value.
|
|
37
27
|
*/
|
|
38
28
|
export declare function resolveEntrypoint(value: string): ClientEntrypoint;
|
|
39
|
-
/**
|
|
40
|
-
* The entrypoint as one absolute URL with no trailing slash — the form the emitted
|
|
41
|
-
* transport joins its routes to, and the default a generated client embeds. Root
|
|
42
|
-
* is the bare origin.
|
|
43
|
-
*/
|
|
44
29
|
export declare function entrypointHref(entrypoint: ClientEntrypoint): string;
|
|
@@ -1,39 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Precedence, highest first: the existing process environment >
|
|
3
|
-
* `.env.<mode>.local` > `.env.<mode>` > `.env.local` > `.env`.
|
|
4
|
-
*
|
|
5
|
-
* # Why a returned record, and not `process.loadEnvFile`
|
|
6
|
-
*
|
|
7
|
-
* Measured on Node v24.20.0, `process.loadEnvFile` is first-write-wins and never
|
|
8
|
-
* overrides an existing value — the right semantics — but it is the wrong
|
|
9
|
-
* mechanism here, for three reasons that were measured rather than argued:
|
|
10
|
-
*
|
|
11
|
-
* 1. It writes into the live `process.env`. A resolution that mutates the global
|
|
12
|
-
* leaks across tests and across invocations in one process; a suite that passes
|
|
13
|
-
* alone and fails in sequence is exactly that leak.
|
|
14
|
-
* 2. It reports an unreadable file (`EACCES`) as `ENOENT`. Catching `ENOENT` from
|
|
15
|
-
* it would treat a `.env` the user cannot read as a `.env` that is not there —
|
|
16
|
-
* a silent wrong answer.
|
|
17
|
-
* 3. It silently drops lines that are not assignments. So does `util.parseEnv`;
|
|
18
|
-
* neither has a notion of a malformed file.
|
|
19
|
-
*
|
|
20
|
-
* So each file is read with `readFileSync` (whose error code is the truth), parsed
|
|
21
|
-
* with `util.parseEnv` (the same parser `loadEnvFile` uses — pinned by a parity
|
|
22
|
-
* test), checked for lines the parser would discard, and folded first-write-wins
|
|
23
|
-
* over the candidates in priority order. Nothing global is read or written: the
|
|
24
|
-
* process environment is an INPUT.
|
|
25
|
-
*/
|
|
26
1
|
/** A resolved environment: names to values, never `undefined`. */
|
|
27
2
|
export type EnvRecord = Readonly<Record<string, string>>;
|
|
28
3
|
/** The process environment as Node types it: values may be `undefined`. */
|
|
29
4
|
export type ProcessEnvInput = Readonly<Record<string, string | undefined>>;
|
|
30
5
|
/** The mode used when neither an explicit mode nor `NODE_ENV` supplies one. */
|
|
31
6
|
export declare const DEFAULT_ENV_MODE = "development";
|
|
32
|
-
/**
|
|
33
|
-
* Reads one candidate file. Returns `undefined` only when the file does not
|
|
34
|
-
* exist; any other failure throws. The seam exists so resolution can be tested
|
|
35
|
-
* without a filesystem — the default reads from disk.
|
|
36
|
-
*/
|
|
37
7
|
export type EnvFileReader = (filePath: string) => string | undefined;
|
|
38
8
|
export interface EnvCascadeInput {
|
|
39
9
|
/** The directory the five candidates are looked up in. */
|
|
@@ -1,31 +1,6 @@
|
|
|
1
1
|
import type { TsConfigJsonResolved } from "get-tsconfig";
|
|
2
2
|
import type { ClientModuleStyle, ImportFileExtension, ModuleFormat } from "../emit/module-style.interface.js";
|
|
3
3
|
import { type LocatedTsconfig } from "./tsconfig.locator.js";
|
|
4
|
-
/**
|
|
5
|
-
* How the generated client is spelled for the project that compiles it: inferred
|
|
6
|
-
* from the project's `tsconfig.json` (`tsconfig.locator.ts`) and, under
|
|
7
|
-
* `node16`/`nodenext`, the nearest `package.json`, by Prisma 7.10's
|
|
8
|
-
* `prisma-client` rules, mirrored from its CLI with the generated extension fixed
|
|
9
|
-
* at `ts`:
|
|
10
|
-
*
|
|
11
|
-
* importFileExtension
|
|
12
|
-
* 1. `allowImportingTsExtensions` or `rewriteRelativeImportExtensions` → `ts`;
|
|
13
|
-
* 2. `module` is `commonjs`, or `moduleResolution` is `bundler` (either case) →
|
|
14
|
-
* `""`;
|
|
15
|
-
* 3. otherwise → `js` (Prisma's `ts → js`).
|
|
16
|
-
*
|
|
17
|
-
* moduleFormat
|
|
18
|
-
* 1. `module` is `commonjs` → `cjs`;
|
|
19
|
-
* 2. `module` is `node16` or `nodenext` → the nearest `package.json` above
|
|
20
|
-
* `generateAt`: `"type": "module"` → `esm`; no `package.json`, an unparseable
|
|
21
|
-
* one, or any other `type` → `cjs`;
|
|
22
|
-
* 3. any other `module` → `esm`; no `module` → `esm` (Prisma's fallback, whose
|
|
23
|
-
* `cjs` arm needs a generated `.cts`).
|
|
24
|
-
*
|
|
25
|
-
* Prisma falls back to the generated extension when there is no tsconfig; this
|
|
26
|
-
* generator refuses instead: its client is TypeScript, and a project without a
|
|
27
|
-
* tsconfig is a JavaScript project, not supported yet.
|
|
28
|
-
*/
|
|
29
4
|
/** What the run follows, and where it read it from. */
|
|
30
5
|
export interface ResolvedModuleStyle extends ClientModuleStyle {
|
|
31
6
|
/** The tsconfig the style was inferred from. */
|
|
@@ -1,28 +1,4 @@
|
|
|
1
1
|
import { type TsConfigJsonResolved } from "get-tsconfig";
|
|
2
|
-
/**
|
|
3
|
-
* Which `tsconfig.json` the generated client is spelled for
|
|
4
|
-
* (`module-style.resolver.ts`): the project's own, found the way Prisma 7's
|
|
5
|
-
* `prisma-client` generator finds it — `get-tsconfig`'s `getTsconfig`, the very
|
|
6
|
-
* library and version Prisma 7.10 bundles, searching from the output directory
|
|
7
|
-
* up, `extends` chains merged — with one step Prisma does not take.
|
|
8
|
-
*
|
|
9
|
-
* # A solution-style root (Vite's layout)
|
|
10
|
-
*
|
|
11
|
-
* `npm create vite` writes a `tsconfig.json` that compiles nothing — `"files":
|
|
12
|
-
* []` and `references` to `tsconfig.app.json` and `tsconfig.node.json` — so the
|
|
13
|
-
* nearest config names no compiler option at all. When the config found does
|
|
14
|
-
* not include the generated entry file and has `references`, the referenced
|
|
15
|
-
* project that does include it is the one used, the first in `references`
|
|
16
|
-
* order — how TypeScript's own language service picks a file's project. With
|
|
17
|
-
* none including it, the root stays the answer, as it is Prisma's.
|
|
18
|
-
*
|
|
19
|
-
* # `tsconfigFile`
|
|
20
|
-
*
|
|
21
|
-
* The escape hatch for a layout this discovery gets wrong (a monorepo, a
|
|
22
|
-
* non-standard name): the config's `tsconfigFile`, resolved against the config
|
|
23
|
-
* file's directory, used as named — no search, no reference step. A missing or
|
|
24
|
-
* unreadable one is refused, naming the path it resolved to.
|
|
25
|
-
*/
|
|
26
2
|
/** The tsconfig a run follows: its path and its resolved content (`extends` merged). */
|
|
27
3
|
export interface LocatedTsconfig {
|
|
28
4
|
readonly path: string;
|
|
@@ -1,47 +1,8 @@
|
|
|
1
1
|
import { type ClientContract } from "@aventara/core";
|
|
2
|
-
/**
|
|
3
|
-
* # The structure check is core's, never a second implementation
|
|
4
|
-
*
|
|
5
|
-
* What a ClientContract IS belongs to core, so the structure step is core's
|
|
6
|
-
* `validateClientContractStructure` and nothing here restates a member, a kind or
|
|
7
|
-
* a vocabulary. Before it, a body with a valid envelope and a malformed member —
|
|
8
|
-
* `resources: { Spell: 5 }` — reached core's canonicalizer, which reads members
|
|
9
|
-
* without validating them, and escaped as a plain `TypeError` and a stack.
|
|
10
|
-
*
|
|
11
|
-
* # Protocol support is judged first, from a read rather than a rule
|
|
12
|
-
*
|
|
13
|
-
* A body from a protocol this generator does not speak may have a different
|
|
14
|
-
* structure, and "unsupported" is then the true and useful answer. So the
|
|
15
|
-
* advertised version is READ — leniently, never judged — and judged against the
|
|
16
|
-
* supported set before any structure is. A body whose version cannot be read at
|
|
17
|
-
* all goes on to the structure step, which names what is wrong with it.
|
|
18
|
-
*
|
|
19
|
-
* # A value, not a throw site
|
|
20
|
-
*
|
|
21
|
-
* # The hash is core's, never a second implementation
|
|
22
|
-
*
|
|
23
|
-
* The advertised `protocol.hash` is recomputed with `computeClientContractHash` —
|
|
24
|
-
* the function the compiler stamps it with — and compared. A hash computed here
|
|
25
|
-
* would be a second source of the Contract's identity.
|
|
26
|
-
*
|
|
27
|
-
* The ClientContract type admits values RFC 8785 cannot encode, and a valid JSON
|
|
28
|
-
* body produces two of them: `1e999` parses to `Infinity`, and `"\ud800"` parses
|
|
29
|
-
* to a lone surrogate. Core rejects both with `CanonicalJsonError`. No server can
|
|
30
|
-
* have stamped a hash over such a body — its own canonicalizer would have refused
|
|
31
|
-
* — so it is refused at THIS step, as `hash-mismatch` with its own sentence and
|
|
32
|
-
* core's path, never let escape as a stack. ONLY that class is caught: anything
|
|
33
|
-
* else thrown while hashing is not a known property of the input, and keeps its
|
|
34
|
-
* stack as a defect.
|
|
35
|
-
*/
|
|
36
|
-
/** The protocol versions this generator speaks. */
|
|
37
2
|
export declare const SUPPORTED_PROTOCOL_VERSIONS: ReadonlySet<number>;
|
|
38
3
|
export type ContractRejectionReason = "protocol-unsupported" | "hash-mismatch" | "structure-invalid";
|
|
39
4
|
export type ContractAccepted = {
|
|
40
5
|
readonly accepted: true;
|
|
41
|
-
/**
|
|
42
|
-
* Trusted to the depth this step checks: core's structure check and the hash. It
|
|
43
|
-
* is the erased ClientContract: no capability is interpreted here.
|
|
44
|
-
*/
|
|
45
6
|
readonly contract: ClientContract;
|
|
46
7
|
};
|
|
47
8
|
export type ContractRejected = {
|
|
@@ -1,18 +1,4 @@
|
|
|
1
1
|
import type { ClientEntrypoint } from "../config/client-config.interface.js";
|
|
2
|
-
/**
|
|
3
|
-
* # Transport, and only transport
|
|
4
|
-
*
|
|
5
|
-
* This file's success is "a JSON value arrived". It does not judge the value: a
|
|
6
|
-
* body that is a JSON array is still a successful fetch, and refusing it is
|
|
7
|
-
* `contract.acceptance.ts`'s job. Every way of failing to produce a JSON value is
|
|
8
|
-
* a `ContractTransportError`, a different class from `ContractProtocolError`,
|
|
9
|
-
* because the two have different remedies — reach the deployment, versus
|
|
10
|
-
* regenerate or upgrade against what it served — and a message that blurs them
|
|
11
|
-
* sends the developer to the wrong one.
|
|
12
|
-
*
|
|
13
|
-
* `fetch` is injected, so the transport is tested without a server; the default is
|
|
14
|
-
* the platform's.
|
|
15
|
-
*/
|
|
16
2
|
/** The part of a `Response` the fetcher reads. */
|
|
17
3
|
export type ContractResponse = Pick<Response, "ok" | "status" | "text">;
|
|
18
4
|
/** The `fetch` the fetcher calls. `globalThis.fetch` satisfies it. */
|
|
@@ -20,10 +6,6 @@ export type ContractFetch = (url: URL, init: {
|
|
|
20
6
|
readonly method: "GET";
|
|
21
7
|
readonly headers?: Readonly<Record<string, string>>;
|
|
22
8
|
}) => Promise<ContractResponse>;
|
|
23
|
-
/**
|
|
24
|
-
* What a conditional GET answers when the served ClientContract is the one named:
|
|
25
|
-
* no body, nothing to judge.
|
|
26
|
-
*/
|
|
27
9
|
export declare const CONTRACT_NOT_MODIFIED: unique symbol;
|
|
28
10
|
export type ContractTransportFailureReason =
|
|
29
11
|
/** No response at all: DNS, refused connection, TLS, abort. */
|
|
@@ -39,21 +21,10 @@ export declare class ContractTransportError extends Error {
|
|
|
39
21
|
readonly name = "ContractTransportError";
|
|
40
22
|
constructor(reason: ContractTransportFailureReason, message: string, options?: ErrorOptions);
|
|
41
23
|
}
|
|
42
|
-
/**
|
|
43
|
-
* `<entrypoint>/_contract`: the canonical mount path and the route, concatenated.
|
|
44
|
-
* The path's canonical form is resolution's — root is `""` — so the join repairs
|
|
45
|
-
* nothing and special-cases nothing. URL resolution would be wrong here: `new
|
|
46
|
-
* URL("_contract", "https://h/api")` replaces `api`.
|
|
47
|
-
*/
|
|
48
24
|
export declare function contractUrlOf(entrypoint: ClientEntrypoint): URL;
|
|
49
25
|
/** The URL as it may be printed: credentials in the entrypoint never are. */
|
|
50
26
|
export declare function displayUrl(url: URL): string;
|
|
51
27
|
/**
|
|
52
|
-
* GETs the ClientContract and returns the parsed body, unjudged — or, when
|
|
53
|
-
* `ifNoneMatch` (a quoted contract hash, the deployment's entity tag) is sent and
|
|
54
|
-
* the deployment answers `304`, {@link CONTRACT_NOT_MODIFIED}. A `304` to a
|
|
55
|
-
* request that sent none is no answer, like any other non-2xx.
|
|
56
|
-
*
|
|
57
28
|
* @throws ContractTransportError when no JSON value arrives.
|
|
58
29
|
*/
|
|
59
30
|
export declare function fetchClientContractBody(entrypoint: ClientEntrypoint, fetch?: ContractFetch, ifNoneMatch?: string): Promise<unknown>;
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import type { ClientContract } from "@aventara/core";
|
|
2
2
|
import type { ClientEntrypoint } from "../config/client-config.interface.js";
|
|
3
3
|
import { type ContractFetch } from "./contract.fetcher.js";
|
|
4
|
-
/**
|
|
5
|
-
* Where the pipeline stops on a rejection, so it is where the `ContractRejected`
|
|
6
|
-
* value becomes a thrown `ContractProtocolError`.
|
|
7
|
-
*/
|
|
8
4
|
export interface ClientContractLoadInput {
|
|
9
|
-
/** The client config's resolved entrypoint. */
|
|
10
5
|
readonly entrypoint: ClientEntrypoint;
|
|
11
|
-
/** Injected for tests; the platform `fetch` otherwise. */
|
|
12
6
|
readonly fetch?: ContractFetch;
|
|
13
7
|
}
|
|
14
8
|
/**
|
|
@@ -16,7 +10,6 @@ export interface ClientContractLoadInput {
|
|
|
16
10
|
* @throws ContractProtocolError when what arrived is refused.
|
|
17
11
|
*/
|
|
18
12
|
export declare function loadClientContract(input: ClientContractLoadInput): Promise<ClientContract>;
|
|
19
|
-
/** A ClientContract loaded against one the caller already holds. */
|
|
20
13
|
export interface ClientContractSince {
|
|
21
14
|
readonly contract: ClientContract;
|
|
22
15
|
/** The deployment answered `304`: `contract` is the one the caller held. */
|
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The banner line that says who owns a file. It is what the output writer reads to
|
|
3
|
-
* decide that a directory is a previous generation it may replace whole rather
|
|
4
|
-
* than someone's files it would delete — so it is its own constant, and must not
|
|
5
|
-
* change between generator versions: an output written by an older generator has
|
|
6
|
-
* to stay recognisable to a newer one.
|
|
7
|
-
*/
|
|
8
1
|
export declare const GENERATED_OWNERSHIP_LINE = "/* !!! Generated by @aventara/client. Do not edit. !!! */";
|
|
9
|
-
/**
|
|
10
|
-
* The ownership banner every emitted file opens with.
|
|
11
|
-
*
|
|
12
|
-
* The Biome suppressions are EMITTED rather than configured, so the output stays
|
|
13
|
-
* correct wherever a consumer drops it, including a repository whose formatter
|
|
14
|
-
* nobody here chose.
|
|
15
|
-
*
|
|
16
|
-
* Nothing in it can change between two runs over one contract: no timestamp, no
|
|
17
|
-
* generator version, no host, no path — any of them would break the exit gate's
|
|
18
|
-
* byte-equality on the first re-run. And no driver prose.
|
|
19
|
-
*/
|
|
20
2
|
export declare const GENERATED_BANNER: readonly string[];
|
|
21
3
|
/** `source` with the banner before it and one blank line between. */
|
|
22
4
|
export declare function withGeneratedBanner(source: string): string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const GENERATED_OWNERSHIP_LINE = "/* !!! Generated by @aventara/client. Do not edit. !!! */";
|
|
2
2
|
export const GENERATED_BANNER = [
|
|
3
|
-
"// biome-ignore-all format: generated output
|
|
3
|
+
"// biome-ignore-all format: generated output",
|
|
4
4
|
"// biome-ignore-all lint: generated output",
|
|
5
5
|
GENERATED_OWNERSHIP_LINE,
|
|
6
6
|
"/* Regenerate with `avclient generate`. */",
|
|
@@ -2,27 +2,4 @@ import type { ClientContract } from "@aventara/core";
|
|
|
2
2
|
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
3
3
|
import { type ClientModuleStyle } from "./module-style.interface.js";
|
|
4
4
|
import { type EmittedNames } from "./name.deriver.js";
|
|
5
|
-
/**
|
|
6
|
-
* `generated/client.ts` — the typed surface: the class `AvClient`, its options
|
|
7
|
-
* `AvClientOptions` and the per-call `CallOptions`, and the ready instance
|
|
8
|
-
* `avClient`, this module's default export.
|
|
9
|
-
*
|
|
10
|
-
* Every argument and result type is an instantiation of core's own derivation,
|
|
11
|
-
* copied under `generated/derivation/`: the call grammar over the carrier's
|
|
12
|
-
* contract, in the client's forms. Nothing here re-spells a rule. A call resolves
|
|
13
|
-
* to the data, a first-style miss to `null`.
|
|
14
|
-
*
|
|
15
|
-
* Each surface is one mapped alias over the one contract. A Resource is reached by
|
|
16
|
-
* its contract name, except where that name is one of the client's own members,
|
|
17
|
-
* which renames the property only. `tx` and `transaction` exist iff the contract
|
|
18
|
-
* advertises `interactive` transactions, in the type as in the runtime.
|
|
19
|
-
*
|
|
20
|
-
* Also declared here, for `types.d.ts` alone, the two helpers the named types are
|
|
21
|
-
* aliases of: `ResourceRecord` and `ResourceArgument`.
|
|
22
|
-
*
|
|
23
|
-
* At runtime the class builds one frozen object per Resource from the advertised
|
|
24
|
-
* operations — each variant a function that runs the operation through the
|
|
25
|
-
* transport, reading the fetch when it is called — and the default entrypoint is
|
|
26
|
-
* the generated one unless the options name another.
|
|
27
|
-
*/
|
|
28
5
|
export declare function emitClientSurfaceModule(contract: ClientContract, names: EmittedNames, style: ClientModuleStyle): EmittedModule;
|
|
@@ -3,26 +3,6 @@ import type { ClientEntrypoint } from "../config/client-config.interface.js";
|
|
|
3
3
|
import { type ClientEmission } from "./emitted-tree.interface.js";
|
|
4
4
|
import { type ClientModuleStyle } from "./module-style.interface.js";
|
|
5
5
|
/**
|
|
6
|
-
* The banner, the encoding and the file order are applied HERE, once, over every
|
|
7
|
-
* module — so no emitter can forget the banner, and byte-equality is a property of
|
|
8
|
-
* this function rather than of each emitter's care:
|
|
9
|
-
*
|
|
10
|
-
* - every module opens with the generated banner;
|
|
11
|
-
* - every file is UTF-8;
|
|
12
|
-
* - files come in UTF-16 code-unit order of their path, each path once.
|
|
13
|
-
*
|
|
14
|
-
* The name derivation's rename warnings ride on the result beside the tree; this
|
|
15
|
-
* function prints nothing.
|
|
16
|
-
*
|
|
17
|
-
* The names and enums read the enum registry, registry NAMES and `protocol`.
|
|
18
|
-
* Core's derivation is copied under `generated/derivation/` as its published
|
|
19
|
-
* declarations: read from the installed `@aventara/core`, never from the Contract.
|
|
20
|
-
* The contract itself reaches the tree as three projections of the one accepted
|
|
21
|
-
* contract: the carrier `generated/contract.ts`, the runtime's decode table and
|
|
22
|
-
* advertised operations `generated/runtime/descriptor.ts`, and `metadata.ts`'s
|
|
23
|
-
* hash, version and default entrypoint — the entrypoint being the run's other
|
|
24
|
-
* input.
|
|
25
|
-
*
|
|
26
6
|
* @throws GeneratedNameError when a name cannot be emitted, even renamed.
|
|
27
7
|
*/
|
|
28
8
|
export declare function emitClientTree(contract: ClientContract, entrypoint: ClientEntrypoint, style: ClientModuleStyle): ClientEmission;
|
|
@@ -2,11 +2,4 @@ import { type ClientContract } from "@aventara/core";
|
|
|
2
2
|
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
3
3
|
/** `generated/contract.ts`, before the banner. */
|
|
4
4
|
export declare function emitContractCarrierModule(contract: ClientContract): EmittedModule;
|
|
5
|
-
/**
|
|
6
|
-
* The ClientContract a carrier file holds — its whole text, banner included — or
|
|
7
|
-
* `undefined` when it is not one this generator wrote over a contract that still
|
|
8
|
-
* verifies: a foreign prefix or suffix, bytes that are not JSON, a body that is
|
|
9
|
-
* not a ClientContract or whose hash does not re-verify, or bytes that are not
|
|
10
|
-
* that contract's canonical form. Never trusted otherwise.
|
|
11
|
-
*/
|
|
12
5
|
export declare function parseContractCarrier(text: string): Promise<ClientContract | undefined>;
|
|
@@ -1,32 +1,4 @@
|
|
|
1
1
|
import type { EmittedModule } from "./emitted-tree.interface.js";
|
|
2
|
-
/**
|
|
3
|
-
* The derivation, transported. A generated client's argument and result types are
|
|
4
|
-
* core's own `OperationArgumentsFor`, `OperationResultFor` and the call grammar
|
|
5
|
-
* (`OperationCall`, …) — never a second spelling. They reach the emitted tree as
|
|
6
|
-
* core's PUBLISHED DECLARATIONS, copied under `generated/derivation/` at their
|
|
7
|
-
* path relative to core's `dist`:
|
|
8
|
-
*
|
|
9
|
-
* - declarations, not sources: the sources' closure carries value imports (the
|
|
10
|
-
* canonicaliser, the wire grammars), the declarations' carries none; a `.d.ts`
|
|
11
|
-
* cannot carry runtime, so a bundler never sees one;
|
|
12
|
-
* - under their own directory, so core's `runtime/decimal.d.ts` cannot shadow the
|
|
13
|
-
* emitted `generated/runtime/decimal.ts`;
|
|
14
|
-
* - byte for byte, except the trailing `//# sourceMappingURL=…` line, which names
|
|
15
|
-
* a map the tree does not hold.
|
|
16
|
-
*
|
|
17
|
-
* The closure is walked here, at generation time, from {@link DERIVATION_ROOTS}
|
|
18
|
-
* over whichever `@aventara/core` this package resolves — so the output is a
|
|
19
|
-
* function of the core version the generator depends on, as the rest of it is a
|
|
20
|
-
* function of the generator. A closure that names a package, carries a
|
|
21
|
-
* triple-slash reference, or leaves core's declarations is refused: it cannot be
|
|
22
|
-
* transported, and core's own gate (`emittable-closure.gate.spec.ts`) exists so
|
|
23
|
-
* that it never is.
|
|
24
|
-
*/
|
|
25
|
-
/**
|
|
26
|
-
* The modules whose declarations the generated client copies — the roots of core's
|
|
27
|
-
* `emittable-closure.gate.spec.ts`, mirrored (core cannot export a test constant,
|
|
28
|
-
* and this package cannot read core's tests).
|
|
29
|
-
*/
|
|
30
2
|
export declare const DERIVATION_ROOTS: readonly string[];
|
|
31
3
|
/**
|
|
32
4
|
* Reads one declaration file by its path relative to core's `dist`
|