@pithy-sh/vite 0.1.1 → 0.1.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/dist/clientEnvDeclaration.d.ts +102 -0
- package/dist/clientEnvDeclaration.d.ts.map +1 -0
- package/dist/clientEnvDeclaration.js +299 -0
- package/dist/devOrigin.d.ts +53 -0
- package/dist/devOrigin.d.ts.map +1 -0
- package/dist/devOrigin.js +45 -0
- package/dist/plugin.d.ts +105 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +96 -0
- package/dist/testPlugin.d.ts +54 -0
- package/dist/testPlugin.d.ts.map +1 -0
- package/dist/testPlugin.js +58 -0
- package/dist/virtualModule.d.ts +28 -0
- package/dist/virtualModule.d.ts.map +1 -0
- package/dist/virtualModule.js +108 -0
- package/dist/workerConfig.d.ts +58 -0
- package/dist/workerConfig.d.ts.map +1 -0
- package/dist/workerConfig.js +117 -0
- package/package.json +12 -6
- package/src/clientEnvDeclaration.ts +46 -9
- package/src/testPlugin.ts +28 -37
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **`client-env.d.ts` is generated from the four declared client projections (#398).**
|
|
3
|
+
*
|
|
4
|
+
* `@pithy-sh/ui-react`'s `templates/client-env.d.ts` is the ambient declaration for the four
|
|
5
|
+
* `virtual:pithy/*` modules, copied into an adopter's Worker by `pithy ui add react`. It used to be
|
|
6
|
+
* hand-written, three packages away from the `client:` projections that produce the values it
|
|
7
|
+
* describes, and #392 held the two together with a spawned compiler because there was no faithful
|
|
8
|
+
* source to generate from: `Capability.client` is typed `(context) => ClientProjection`, so each
|
|
9
|
+
* capability's real shape existed only as an inferred literal inside a closure.
|
|
10
|
+
*
|
|
11
|
+
* #395 removed that. The four capabilities now declare their projection types, and the declaration is
|
|
12
|
+
* the source of truth rather than a restatement of the literal. So there is exactly one statement of
|
|
13
|
+
* each shape, and this module copies it — **a gate that watches two things agree is strictly worse
|
|
14
|
+
* than one thing.**
|
|
15
|
+
*
|
|
16
|
+
* ## Why the declared type is copied as text rather than re-printed from a type
|
|
17
|
+
*
|
|
18
|
+
* Because the doc comments are half of what the file is worth. A declaration emitted from a resolved
|
|
19
|
+
* type carries the shape and loses every sentence attached to it, and those sentences are what tell a
|
|
20
|
+
* screen that `action` must be rendered and never retyped, or that a Paddle client token is
|
|
21
|
+
* publishable by design. Copying the type node's own source text keeps the unions, the `| null` arms
|
|
22
|
+
* and the prose exactly as the capability wrote them.
|
|
23
|
+
*
|
|
24
|
+
* The one thing that is *not* copied is what is not a shape at all — see {@link PREAMBLE} and
|
|
25
|
+
* {@link ENABLED_EXPORT}, both of which are policy and are written as fixed text.
|
|
26
|
+
*
|
|
27
|
+
* ## Nothing an adopter runs
|
|
28
|
+
*
|
|
29
|
+
* The kit generates at its own build time and commits the artifact. `pithy ui add react` still copies
|
|
30
|
+
* a plain static `.d.ts` into a Worker, and a scaffolded project typechecks with no new step.
|
|
31
|
+
*/
|
|
32
|
+
/** One `virtual:pithy/*` module, and the declared type that is its whole shape. */
|
|
33
|
+
export interface DeclaredModule {
|
|
34
|
+
/** The capability name — the segment after `virtual:pithy/`. */
|
|
35
|
+
readonly module: string;
|
|
36
|
+
/** The specifier the projection is read from. Resolved through the package's own exports map. */
|
|
37
|
+
readonly specifier: string;
|
|
38
|
+
/** The exported type alias that declares what a browser receives. */
|
|
39
|
+
readonly type: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The modules the declaration covers, in the order they are emitted.
|
|
43
|
+
*
|
|
44
|
+
* This list is the whole reach of the generated file: a further capability projecting to a browser is
|
|
45
|
+
* a further entry here, and until it is one, nothing declares it. The specifiers resolve because each
|
|
46
|
+
* capability is a devDependency of `@pithy-sh/vite` — a build-time dependency, like the compiler. An
|
|
47
|
+
* adopter installs none of them to use the plugin, and never runs this module.
|
|
48
|
+
*/
|
|
49
|
+
export declare const DECLARED_MODULES: readonly DeclaredModule[];
|
|
50
|
+
/**
|
|
51
|
+
* The source text of every top-level `export type X = …;` in a module, keyed by name.
|
|
52
|
+
*
|
|
53
|
+
* The value is the type node's own text, verbatim: every doc comment, every union arm, every `| null`
|
|
54
|
+
* exactly as written. It is found by scanning for the `;` that closes the alias at bracket depth zero,
|
|
55
|
+
* over code characters only.
|
|
56
|
+
*/
|
|
57
|
+
export declare function parseTypeAliases(source: string): Map<string, string>;
|
|
58
|
+
/**
|
|
59
|
+
* Replace every reference to a sibling type alias with that alias's own text, re-indented to sit where
|
|
60
|
+
* the reference did.
|
|
61
|
+
*
|
|
62
|
+
* `PaymentsClientProduct` is the only one today, and it is named rather than inlined at the source for
|
|
63
|
+
* a reason #395 recorded — the annotation goes on the `.map` callback, where a fresh literal is still
|
|
64
|
+
* checked in both directions. The declaration an adopter reads has no such seam to hang a name on, so
|
|
65
|
+
* the shape is written where it is used.
|
|
66
|
+
*/
|
|
67
|
+
export declare function inlineAliases(text: string, aliases: ReadonlyMap<string, string>, seen?: readonly string[]): string;
|
|
68
|
+
/** One `declare module "virtual:pithy/<module>"` block, fixed text and declared shape together. */
|
|
69
|
+
export declare function renderModule(module: string, declaredType: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* The whole file, from each module's projection source.
|
|
72
|
+
*
|
|
73
|
+
* Pure, and the module list is an argument, so the test can hand it a projection this repository does
|
|
74
|
+
* not contain and read what came out. A generator asserted only against the real four would pass just
|
|
75
|
+
* as well if it ignored them and printed a constant.
|
|
76
|
+
*/
|
|
77
|
+
export declare function renderClientEnv(sources: ReadonlyMap<string, string>, modules?: readonly DeclaredModule[]): string;
|
|
78
|
+
/**
|
|
79
|
+
* Read the four projections' sources through their own packages' exports maps.
|
|
80
|
+
*
|
|
81
|
+
* **Resolved, then mirrored back to `src` — Jim, #476.** Until every package shipped a build,
|
|
82
|
+
* `@pithy-sh/auth/src/client/projection` resolved to the TypeScript itself. It resolves to
|
|
83
|
+
* `dist/client/projection.js` now, where the types are stripped and `AuthClientProjection` does not
|
|
84
|
+
* exist, so the resolved path is walked back to the module it was emitted from.
|
|
85
|
+
*
|
|
86
|
+
* The declaration beside it, `dist/client/projection.d.ts`, is the other candidate and was tried first.
|
|
87
|
+
* It is arguably the more truthful input — it is the type an adopter can actually see — but it makes
|
|
88
|
+
* this generator's build wait on five capability packages' builds, and `@pithy-sh/vite` depends on none
|
|
89
|
+
* of them. That is five cross-package `dependsOn` edges in `turbo.jsonc` that a sixth capability's
|
|
90
|
+
* projection would silently need and silently not have: the generator would read a stale declaration,
|
|
91
|
+
* emit a stale template, and say nothing. Source has no such ordering, `turbo.jsonc` already names
|
|
92
|
+
* every `packages/<name>/src/client/projection.ts` as this build's input, and the authored text is
|
|
93
|
+
* what the file's own header promises an adopter is emitted verbatim.
|
|
94
|
+
*
|
|
95
|
+
* `dist/x.js` → `src/x.ts` is not a guess about the layout: `tsdown` and `tsc -p tsconfig.build.json`
|
|
96
|
+
* both mirror `src/` into `dist/`, so the two are the same path under a different root, and
|
|
97
|
+
* `packaging.test.ts` fails on any published module missing either half.
|
|
98
|
+
*/
|
|
99
|
+
export declare function readProjectionSources(): Promise<Map<string, string>>;
|
|
100
|
+
/** The declaration as it should be on disk. What `bun run generate` writes and the gate compares. */
|
|
101
|
+
export declare function generateClientEnv(): Promise<string>;
|
|
102
|
+
//# sourceMappingURL=clientEnvDeclaration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clientEnvDeclaration.d.ts","sourceRoot":"","sources":["../src/clientEnvDeclaration.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,mFAAmF;AACnF,MAAM,WAAW,cAAc;IAC7B,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iGAAiG;IACjG,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,cAAc,EAMrD,CAAC;AAuGF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBpE;AAQD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,IAAI,GAAE,SAAS,MAAM,EAAO,GAC3B,MAAM,CAiBR;AA2CD,mGAAmG;AACnG,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAUzE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,OAAO,GAAE,SAAS,cAAc,EAAqB,GACpD,MAAM,CAYR;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAS1E;AAED,qGAAqG;AACrG,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAEzD"}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
//#region src/clientEnvDeclaration.ts
|
|
4
|
+
/**
|
|
5
|
+
* The modules the declaration covers, in the order they are emitted.
|
|
6
|
+
*
|
|
7
|
+
* This list is the whole reach of the generated file: a further capability projecting to a browser is
|
|
8
|
+
* a further entry here, and until it is one, nothing declares it. The specifiers resolve because each
|
|
9
|
+
* capability is a devDependency of `@pithy-sh/vite` — a build-time dependency, like the compiler. An
|
|
10
|
+
* adopter installs none of them to use the plugin, and never runs this module.
|
|
11
|
+
*/
|
|
12
|
+
const DECLARED_MODULES = [
|
|
13
|
+
{
|
|
14
|
+
module: "auth",
|
|
15
|
+
specifier: "@pithy-sh/auth/src/client/projection",
|
|
16
|
+
type: "AuthClientProjection"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
module: "i18n",
|
|
20
|
+
specifier: "@pithy-sh/i18n/src/client/projection",
|
|
21
|
+
type: "I18nClientProjection"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
module: "payments",
|
|
25
|
+
specifier: "@pithy-sh/payments/src/client/projection",
|
|
26
|
+
type: "PaymentsClientProjection"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
module: "support",
|
|
30
|
+
specifier: "@pithy-sh/support/src/client/projection",
|
|
31
|
+
type: "SupportClientProjection"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
module: "turnstile",
|
|
35
|
+
specifier: "@pithy-sh/turnstile/src/client/projection",
|
|
36
|
+
type: "TurnstileClientProjection"
|
|
37
|
+
}
|
|
38
|
+
];
|
|
39
|
+
/**
|
|
40
|
+
* **Fixed text, item one: the preamble.**
|
|
41
|
+
*
|
|
42
|
+
* The `vite/client` reference and the paragraph explaining why the default export is a union are
|
|
43
|
+
* policy about how the modules are consumed, not a projection of anything, so there is nothing to
|
|
44
|
+
* derive them from. They are written here.
|
|
45
|
+
*/
|
|
46
|
+
const PREAMBLE = `/// <reference types="vite/client" />
|
|
47
|
+
|
|
48
|
+
// The client-safe projection of this worker's composed capabilities, served by @pithy-sh/vite.
|
|
49
|
+
//
|
|
50
|
+
// Each module is a DEFAULT export whose type is a union discriminated on \`enabled\`. That shape is
|
|
51
|
+
// deliberate. A capability that is not composed projects \`{ enabled: false }\` and nothing else, so a
|
|
52
|
+
// NAMED import of any other key would be a missing export and the build would fail — on exactly the
|
|
53
|
+
// case this mechanism exists to make survivable. Importing the default and narrowing cannot fail:
|
|
54
|
+
//
|
|
55
|
+
// import turnstile from "virtual:pithy/turnstile";
|
|
56
|
+
// if (!turnstile.enabled) return null; // narrowed: sitekey, mode and token exist below this line
|
|
57
|
+
//
|
|
58
|
+
// The \`virtual:pithy/*\` modules are never written to disk. These declarations describe modules the
|
|
59
|
+
// Vite plugin serves, built from the Worker's own pithy.config.ts.
|
|
60
|
+
//
|
|
61
|
+
// **Generated, and copied here as it was emitted (#398).** Each declaration below is a capability's
|
|
62
|
+
// declared client projection — \`src/client/projection.ts\` in @pithy-sh/auth, @pithy-sh/payments,
|
|
63
|
+
// @pithy-sh/support and @pithy-sh/turnstile — written out by @pithy-sh/vite's
|
|
64
|
+
// \`src/clientEnvDeclaration.ts\` at kit build time. In the kit that is one statement of each shape and
|
|
65
|
+
// nothing to keep in step: a field a projection stops emitting is a compile error where it is
|
|
66
|
+
// projected, and this file moves in the same commit.
|
|
67
|
+
//
|
|
68
|
+
// In your repository it is yours, like every other seeded file. Editing it changes what your compiler
|
|
69
|
+
// believes and nothing about what the plugin serves.
|
|
70
|
+
`;
|
|
71
|
+
/**
|
|
72
|
+
* **Fixed text, item two: the one named export.**
|
|
73
|
+
*
|
|
74
|
+
* `enabled` is declared alone on purpose, and the refusal is the feature. A capability nobody composed
|
|
75
|
+
* serves `{ enabled: false }` and nothing else, so `import { sitekey } from "virtual:pithy/turnstile"`
|
|
76
|
+
* has to fail the build — on exactly the case the union exists to make survivable. Deriving the named
|
|
77
|
+
* exports from the projection's keys would declare every one of them and take that refusal away.
|
|
78
|
+
*/
|
|
79
|
+
const ENABLED_EXPORT = ` /**
|
|
80
|
+
* Whether this capability is composed and serving on this worker. Also the union's discriminant.
|
|
81
|
+
*
|
|
82
|
+
* The only named export, deliberately. Every other key is reached through the default export and a
|
|
83
|
+
* narrowing — see the note at the top of this file.
|
|
84
|
+
*/
|
|
85
|
+
export const enabled: boolean;
|
|
86
|
+
`;
|
|
87
|
+
/**
|
|
88
|
+
* Which characters of a TypeScript source are code — everything outside a comment or a string.
|
|
89
|
+
*
|
|
90
|
+
* Both passes below need it, and for the same reason: a brace in `` `POST {basePath}/feedback` `` and
|
|
91
|
+
* a type name in an `{@link …}` tag are text, and counting or rewriting them would be a silent wrong
|
|
92
|
+
* answer rather than an error. Template literals with `${}` substitutions are not handled, because a
|
|
93
|
+
* type position cannot hold one — `parseTypeAliases` refuses anything it cannot finish instead.
|
|
94
|
+
*/
|
|
95
|
+
function codeMask(source) {
|
|
96
|
+
const mask = new Array(source.length).fill(true);
|
|
97
|
+
let index = 0;
|
|
98
|
+
while (index < source.length) {
|
|
99
|
+
if (source.startsWith("//", index)) {
|
|
100
|
+
while (index < source.length && source[index] !== "\n") mask[index++] = false;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (source.startsWith("/*", index)) {
|
|
104
|
+
const close = source.indexOf("*/", index + 2);
|
|
105
|
+
const stop = close === -1 ? source.length : close + 2;
|
|
106
|
+
while (index < stop) mask[index++] = false;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const quote = source[index];
|
|
110
|
+
if (quote === "\"" || quote === "'" || quote === "`") {
|
|
111
|
+
mask[index++] = false;
|
|
112
|
+
while (index < source.length) {
|
|
113
|
+
const char = source[index];
|
|
114
|
+
mask[index++] = false;
|
|
115
|
+
if (char === "\\") {
|
|
116
|
+
if (index < source.length) mask[index++] = false;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (char === quote) break;
|
|
120
|
+
}
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
index += 1;
|
|
124
|
+
}
|
|
125
|
+
return mask;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* An identifier, wherever one may start, and the head of a top-level exported type alias.
|
|
129
|
+
*
|
|
130
|
+
* Both are built per call rather than shared. A `g` flag carries `lastIndex` between uses, and
|
|
131
|
+
* {@link inlineAliases} recurses — one shared instance would have an inner call move the outer's
|
|
132
|
+
* cursor, which reads as a reference the generator simply skipped.
|
|
133
|
+
*/
|
|
134
|
+
const identifierPattern = () => /[A-Za-z_$][A-Za-z0-9_$]*/g;
|
|
135
|
+
const typeAliasPattern = () => /^export type ([A-Za-z_$][A-Za-z0-9_$]*)\s*=/gm;
|
|
136
|
+
/**
|
|
137
|
+
* The source text of every top-level `export type X = …;` in a module, keyed by name.
|
|
138
|
+
*
|
|
139
|
+
* The value is the type node's own text, verbatim: every doc comment, every union arm, every `| null`
|
|
140
|
+
* exactly as written. It is found by scanning for the `;` that closes the alias at bracket depth zero,
|
|
141
|
+
* over code characters only.
|
|
142
|
+
*/
|
|
143
|
+
function parseTypeAliases(source) {
|
|
144
|
+
const mask = codeMask(source);
|
|
145
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
146
|
+
const pattern = typeAliasPattern();
|
|
147
|
+
for (let match = pattern.exec(source); match; match = pattern.exec(source)) {
|
|
148
|
+
const [head, name] = match;
|
|
149
|
+
const start = match.index;
|
|
150
|
+
if (!mask[start] || !name) continue;
|
|
151
|
+
const from = start + head.length;
|
|
152
|
+
let depth = 0;
|
|
153
|
+
let end = -1;
|
|
154
|
+
for (let index = from; index < source.length; index += 1) {
|
|
155
|
+
if (!mask[index]) continue;
|
|
156
|
+
const char = source[index];
|
|
157
|
+
if (char === "{" || char === "(" || char === "[") depth += 1;
|
|
158
|
+
else if (char === "}" || char === ")" || char === "]") depth -= 1;
|
|
159
|
+
else if (char === ";" && depth === 0) {
|
|
160
|
+
end = index;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (end === -1) throw new Error(`\`export type ${name}\` is never closed by a \`;\` at depth zero.`);
|
|
165
|
+
aliases.set(name, source.slice(from, end));
|
|
166
|
+
}
|
|
167
|
+
return aliases;
|
|
168
|
+
}
|
|
169
|
+
/** The leading spaces of the line `index` falls on. What an inlined alias has to be pushed out by. */
|
|
170
|
+
function indentAt(text, index) {
|
|
171
|
+
const lineStart = text.lastIndexOf("\n", index - 1) + 1;
|
|
172
|
+
return /^[ ]*/.exec(text.slice(lineStart, index))?.[0] ?? "";
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Replace every reference to a sibling type alias with that alias's own text, re-indented to sit where
|
|
176
|
+
* the reference did.
|
|
177
|
+
*
|
|
178
|
+
* `PaymentsClientProduct` is the only one today, and it is named rather than inlined at the source for
|
|
179
|
+
* a reason #395 recorded — the annotation goes on the `.map` callback, where a fresh literal is still
|
|
180
|
+
* checked in both directions. The declaration an adopter reads has no such seam to hang a name on, so
|
|
181
|
+
* the shape is written where it is used.
|
|
182
|
+
*/
|
|
183
|
+
function inlineAliases(text, aliases, seen = []) {
|
|
184
|
+
const mask = codeMask(text);
|
|
185
|
+
const parts = [];
|
|
186
|
+
let cursor = 0;
|
|
187
|
+
const pattern = identifierPattern();
|
|
188
|
+
for (let match = pattern.exec(text); match; match = pattern.exec(text)) {
|
|
189
|
+
const name = match[0];
|
|
190
|
+
const body = aliases.get(name);
|
|
191
|
+
if (body === void 0 || !mask[match.index]) continue;
|
|
192
|
+
if (seen.includes(name)) throw new Error(`\`${name}\` refers to itself through ${seen.join(" → ")}.`);
|
|
193
|
+
const indent = indentAt(text, match.index);
|
|
194
|
+
const inlined = inlineAliases(body, aliases, [...seen, name]);
|
|
195
|
+
parts.push(text.slice(cursor, match.index), reindent(inlined.trim(), indent));
|
|
196
|
+
cursor = match.index + name.length;
|
|
197
|
+
}
|
|
198
|
+
parts.push(text.slice(cursor));
|
|
199
|
+
return parts.join("");
|
|
200
|
+
}
|
|
201
|
+
/** Push every line but the first out by `indent`. The first is already sitting where it was written. */
|
|
202
|
+
function reindent(text, indent) {
|
|
203
|
+
return text.split("\n").map((line, position) => position === 0 || line === "" ? line : `${indent}${line}`).join("\n");
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Move a block from the column it was written at to the one it is emitted at.
|
|
207
|
+
*
|
|
208
|
+
* The alias text arrives at whatever depth its file put it — `tsc` prints a union's arms at zero and
|
|
209
|
+
* their members at four — and inside a `declare module` block those want four and eight. So the block
|
|
210
|
+
* is dedented to column zero and then pushed out uniformly, which keeps every relative depth, including
|
|
211
|
+
* the ` *` continuation of a doc comment.
|
|
212
|
+
*
|
|
213
|
+
* **The first line is trimmed rather than measured, and the rest are dedented by their common indent.**
|
|
214
|
+
* A type alias's body begins on the `=` line, so its first line arrives as `{` behind a single space
|
|
215
|
+
* that says nothing about the block's depth. Taking that space as the base — which is what this did
|
|
216
|
+
* until #476 changed the input from source to emitted declaration — shifted every following line one
|
|
217
|
+
* column left of where it belonged, and the misalignment was invisible in the old input only because a
|
|
218
|
+
* hand-written union happened to start on a line of its own.
|
|
219
|
+
*/
|
|
220
|
+
function shift(text, indent) {
|
|
221
|
+
const lines = text.split("\n");
|
|
222
|
+
while (lines.length > 0 && (lines[0] ?? "").trim() === "") lines.shift();
|
|
223
|
+
while (lines.length > 0 && (lines.at(-1) ?? "").trim() === "") lines.pop();
|
|
224
|
+
const base = lines.slice(1).filter((line) => line.trim() !== "").reduce((width, line) => Math.min(width, (/^[ ]*/.exec(line)?.[0] ?? "").length), Infinity);
|
|
225
|
+
const dedent = Number.isFinite(base) ? base : 0;
|
|
226
|
+
return lines.map((line, position) => {
|
|
227
|
+
if (line.trim() === "") return "";
|
|
228
|
+
return `${indent}${position === 0 ? line.trim() : line.slice(dedent)}`;
|
|
229
|
+
}).join("\n");
|
|
230
|
+
}
|
|
231
|
+
/** One `declare module "virtual:pithy/<module>"` block, fixed text and declared shape together. */
|
|
232
|
+
function renderModule(module, declaredType) {
|
|
233
|
+
return [
|
|
234
|
+
`declare module "virtual:pithy/${module}" {`,
|
|
235
|
+
ENABLED_EXPORT,
|
|
236
|
+
" const config:",
|
|
237
|
+
`${shift(declaredType, " ")};`,
|
|
238
|
+
" export default config;",
|
|
239
|
+
"}",
|
|
240
|
+
""
|
|
241
|
+
].join("\n");
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* The whole file, from each module's projection source.
|
|
245
|
+
*
|
|
246
|
+
* Pure, and the module list is an argument, so the test can hand it a projection this repository does
|
|
247
|
+
* not contain and read what came out. A generator asserted only against the real four would pass just
|
|
248
|
+
* as well if it ignored them and printed a constant.
|
|
249
|
+
*/
|
|
250
|
+
function renderClientEnv(sources, modules = DECLARED_MODULES) {
|
|
251
|
+
const blocks = [];
|
|
252
|
+
for (const declared of modules) {
|
|
253
|
+
const source = sources.get(declared.module);
|
|
254
|
+
if (source === void 0) throw new Error(`No projection source for virtual:pithy/${declared.module}.`);
|
|
255
|
+
const aliases = parseTypeAliases(source);
|
|
256
|
+
const body = aliases.get(declared.type);
|
|
257
|
+
if (body === void 0) throw new Error(`${declared.specifier} exports no type \`${declared.type}\`.`);
|
|
258
|
+
aliases.delete(declared.type);
|
|
259
|
+
blocks.push(renderModule(declared.module, inlineAliases(body, aliases)));
|
|
260
|
+
}
|
|
261
|
+
return `${PREAMBLE}\n${blocks.join("\n")}`;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Read the four projections' sources through their own packages' exports maps.
|
|
265
|
+
*
|
|
266
|
+
* **Resolved, then mirrored back to `src` — Jim, #476.** Until every package shipped a build,
|
|
267
|
+
* `@pithy-sh/auth/src/client/projection` resolved to the TypeScript itself. It resolves to
|
|
268
|
+
* `dist/client/projection.js` now, where the types are stripped and `AuthClientProjection` does not
|
|
269
|
+
* exist, so the resolved path is walked back to the module it was emitted from.
|
|
270
|
+
*
|
|
271
|
+
* The declaration beside it, `dist/client/projection.d.ts`, is the other candidate and was tried first.
|
|
272
|
+
* It is arguably the more truthful input — it is the type an adopter can actually see — but it makes
|
|
273
|
+
* this generator's build wait on five capability packages' builds, and `@pithy-sh/vite` depends on none
|
|
274
|
+
* of them. That is five cross-package `dependsOn` edges in `turbo.jsonc` that a sixth capability's
|
|
275
|
+
* projection would silently need and silently not have: the generator would read a stale declaration,
|
|
276
|
+
* emit a stale template, and say nothing. Source has no such ordering, `turbo.jsonc` already names
|
|
277
|
+
* every `packages/<name>/src/client/projection.ts` as this build's input, and the authored text is
|
|
278
|
+
* what the file's own header promises an adopter is emitted verbatim.
|
|
279
|
+
*
|
|
280
|
+
* `dist/x.js` → `src/x.ts` is not a guess about the layout: `tsdown` and `tsc -p tsconfig.build.json`
|
|
281
|
+
* both mirror `src/` into `dist/`, so the two are the same path under a different root, and
|
|
282
|
+
* `packaging.test.ts` fails on any published module missing either half.
|
|
283
|
+
*/
|
|
284
|
+
async function readProjectionSources() {
|
|
285
|
+
const sources = /* @__PURE__ */ new Map();
|
|
286
|
+
for (const declared of DECLARED_MODULES) {
|
|
287
|
+
const built = fileURLToPath(import.meta.resolve(declared.specifier));
|
|
288
|
+
const source = built.replace(/([\\/])dist\1(.+)\.js$/, "$1src$1$2.ts");
|
|
289
|
+
if (source === built) throw new Error(`${declared.specifier} did not resolve into a build: ${built}`);
|
|
290
|
+
sources.set(declared.module, await readFile(source, "utf8"));
|
|
291
|
+
}
|
|
292
|
+
return sources;
|
|
293
|
+
}
|
|
294
|
+
/** The declaration as it should be on disk. What `bun run generate` writes and the gate compares. */
|
|
295
|
+
async function generateClientEnv() {
|
|
296
|
+
return renderClientEnv(await readProjectionSources());
|
|
297
|
+
}
|
|
298
|
+
//#endregion
|
|
299
|
+
export { DECLARED_MODULES, generateClientEnv, inlineAliases, parseTypeAliases, readProjectionSources, renderClientEnv, renderModule };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The slice of `@cloudflare/vite-plugin`'s worker config this touches.
|
|
3
|
+
*
|
|
4
|
+
* Structural rather than imported. `@pithy-sh/vite` does not depend on that plugin — an adopter picks
|
|
5
|
+
* its own version — and naming one field is enough to be checked against it at the call site, where the
|
|
6
|
+
* real type applies.
|
|
7
|
+
*/
|
|
8
|
+
export interface WorkerVars {
|
|
9
|
+
/** The Worker's plain environment variables, as `wrangler.jsonc` declared them. */
|
|
10
|
+
vars?: Record<string, WorkerVarValue>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* What a wrangler `vars` entry may hold — wrangler's own `Json`, restated.
|
|
14
|
+
*
|
|
15
|
+
* Restated rather than imported for the reason {@link WorkerVars} is structural, and it has to be this
|
|
16
|
+
* exact shape rather than `unknown`: the customizer's return type is checked against
|
|
17
|
+
* `Partial<WorkerConfig>` at the call site, and `Record<string, unknown>` is not assignable to
|
|
18
|
+
* `Record<string, Json>` — an index signature is invariant. A looser type here compiles in this package
|
|
19
|
+
* and fails in every adopter's, which is the worst place to find out.
|
|
20
|
+
*/
|
|
21
|
+
export type WorkerVarValue = string | number | boolean | null | WorkerVarValue[] | {
|
|
22
|
+
[key: string]: WorkerVarValue;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Give the Worker the origin `pithy dev` allocated this checkout, as `BASE_URL`.
|
|
26
|
+
*
|
|
27
|
+
* Spread into `@cloudflare/vite-plugin`'s `config` customizer:
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* cloudflare({ config: devWorkerConfig() })
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* **Why an adopter writes a line at all.** A Worker launched through `wrangler dev` is handed its own
|
|
34
|
+
* origin on the argv, by `pithy dev`, with nothing to configure. A Worker launched through a custom
|
|
35
|
+
* `dev.command` is a Vite dev server, and there is no argv to append a `--var` to: `@cloudflare/vite-plugin`
|
|
36
|
+
* takes the Worker's `vars` from `wrangler.jsonc`, and the only documented way in is this customizer,
|
|
37
|
+
* which lives in the adopter's `vite.config.ts`. `pithy init` scaffolds it; an existing project adds
|
|
38
|
+
* the line once and `pithy doctor` says so until it does.
|
|
39
|
+
*
|
|
40
|
+
* **Why it has to be given rather than written down.** A dev port is *allocated* — every checkout
|
|
41
|
+
* reserves its own block — so a `vars.BASE_URL` in `wrangler.jsonc` is right in the first checkout on a
|
|
42
|
+
* machine and wrong in every other one. That is not cosmetic: `BASE_URL` is the `iss` on every
|
|
43
|
+
* control-plane token a Worker signs and the origin its callback links are built against, so a second
|
|
44
|
+
* checkout signed tokens as the first one and its own seam denied every call, 401, with every stored
|
|
45
|
+
* value agreeing (#462, `pithy-sh/dashboard#95`).
|
|
46
|
+
*
|
|
47
|
+
* **Outside `pithy dev` it does nothing at all.** A plain `vite dev`, a build, or CI has no
|
|
48
|
+
* {@link WORKER_ORIGIN_VAR} in its environment, and the Worker keeps whatever `wrangler.jsonc` says —
|
|
49
|
+
* which for a deployed environment is the value `applyDomains` generated from `domains`, and is
|
|
50
|
+
* correct. This never invents an origin; it only passes on one that was allocated.
|
|
51
|
+
*/
|
|
52
|
+
export declare function devWorkerConfig(env?: NodeJS.ProcessEnv): (config: WorkerVars) => WorkerVars;
|
|
53
|
+
//# sourceMappingURL=devOrigin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devOrigin.d.ts","sourceRoot":"","sources":["../src/devOrigin.ts"],"names":[],"mappings":"AAMA;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACvC;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,cAAc,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAA;CAAE,CAAC;AAErH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,CAAC,MAAM,EAAE,UAAU,KAAK,UAAU,CAuBxG"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { LOCAL_ENVIRONMENT } from "@pithy-sh/core/src/naming/environment";
|
|
2
|
+
import { ENVIRONMENT_VAR, WORKER_ORIGIN_VAR } from "@pithy-sh/core/src/worker/identity";
|
|
3
|
+
//#region src/devOrigin.ts
|
|
4
|
+
/**
|
|
5
|
+
* Give the Worker the origin `pithy dev` allocated this checkout, as `BASE_URL`.
|
|
6
|
+
*
|
|
7
|
+
* Spread into `@cloudflare/vite-plugin`'s `config` customizer:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* cloudflare({ config: devWorkerConfig() })
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* **Why an adopter writes a line at all.** A Worker launched through `wrangler dev` is handed its own
|
|
14
|
+
* origin on the argv, by `pithy dev`, with nothing to configure. A Worker launched through a custom
|
|
15
|
+
* `dev.command` is a Vite dev server, and there is no argv to append a `--var` to: `@cloudflare/vite-plugin`
|
|
16
|
+
* takes the Worker's `vars` from `wrangler.jsonc`, and the only documented way in is this customizer,
|
|
17
|
+
* which lives in the adopter's `vite.config.ts`. `pithy init` scaffolds it; an existing project adds
|
|
18
|
+
* the line once and `pithy doctor` says so until it does.
|
|
19
|
+
*
|
|
20
|
+
* **Why it has to be given rather than written down.** A dev port is *allocated* — every checkout
|
|
21
|
+
* reserves its own block — so a `vars.BASE_URL` in `wrangler.jsonc` is right in the first checkout on a
|
|
22
|
+
* machine and wrong in every other one. That is not cosmetic: `BASE_URL` is the `iss` on every
|
|
23
|
+
* control-plane token a Worker signs and the origin its callback links are built against, so a second
|
|
24
|
+
* checkout signed tokens as the first one and its own seam denied every call, 401, with every stored
|
|
25
|
+
* value agreeing (#462, `pithy-sh/dashboard#95`).
|
|
26
|
+
*
|
|
27
|
+
* **Outside `pithy dev` it does nothing at all.** A plain `vite dev`, a build, or CI has no
|
|
28
|
+
* {@link WORKER_ORIGIN_VAR} in its environment, and the Worker keeps whatever `wrangler.jsonc` says —
|
|
29
|
+
* which for a deployed environment is the value `applyDomains` generated from `domains`, and is
|
|
30
|
+
* correct. This never invents an origin; it only passes on one that was allocated.
|
|
31
|
+
*/
|
|
32
|
+
function devWorkerConfig(env = process.env) {
|
|
33
|
+
return (config) => {
|
|
34
|
+
const origin = env[WORKER_ORIGIN_VAR];
|
|
35
|
+
if (origin === void 0 || origin.trim() === "") return {};
|
|
36
|
+
const environment = config.vars?.[ENVIRONMENT_VAR];
|
|
37
|
+
if (typeof environment === "string" && environment !== LOCAL_ENVIRONMENT) return {};
|
|
38
|
+
return { vars: {
|
|
39
|
+
...config.vars,
|
|
40
|
+
BASE_URL: origin
|
|
41
|
+
} };
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { devWorkerConfig };
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What {@link pithy} hands back: a Vite plugin, described without naming a Vite type.
|
|
3
|
+
*
|
|
4
|
+
* **The return type used to be Vite's own `Plugin`, and that made the peer range a lie — Jim,
|
|
5
|
+
* 2026-08-21 (#414).** `package.json` declares `vite: ^6.1.0 || ^7.0.0 || ^8.0.0`, but a `Plugin` in
|
|
6
|
+
* the signature is a `Plugin` out of *this package's* `node_modules`. An adopter who resolved any
|
|
7
|
+
* other copy — which is every adopter developing against a symlinked kit, and every adopter whose
|
|
8
|
+
* install did not deduplicate — had to prove the kit's copy assignable to theirs, field by field,
|
|
9
|
+
* before `plugins: [pithy()]` would compile. Across majors that comparison fails outright: `hotUpdate`
|
|
10
|
+
* does not carry the same `this` in 6 as in 8, so this plugin was not a Vite 6 plugin however identical
|
|
11
|
+
* the hook body is. Within a major it does not even finish — `Plugin` is deep and recursive enough to
|
|
12
|
+
* exhaust tsc's depth budget, which is the `TS2321: Excessive stack depth` that stopped
|
|
13
|
+
* `pithy-sh/dashboard` typechecking the `vite.config.ts` `pithy init` had just written for it. A peer
|
|
14
|
+
* dependency that only compiles when the install deduplicates is not a peer dependency.
|
|
15
|
+
*
|
|
16
|
+
* So the signature promises the two things every Vite in that range agrees on and an adopter's
|
|
17
|
+
* checker can settle in two comparisons: the plugin has a name, and it runs early. No recursion, no
|
|
18
|
+
* hook signatures, nothing that can differ between two copies of the same library.
|
|
19
|
+
*
|
|
20
|
+
* **What is still checked.** The object below is written `satisfies Plugin` against the Vite this
|
|
21
|
+
* package develops against, so every hook keeps its parameter types, its `this`, and its return type.
|
|
22
|
+
* A hook that reads the wrong field off `config`, returns the wrong shape, or is spelled with a name
|
|
23
|
+
* Vite does not call is still a red build — here, where the kit is compiled, and in
|
|
24
|
+
* `tooling/vite-adopter`, which compiles this file too.
|
|
25
|
+
*
|
|
26
|
+
* **What is not.** The adopter's checker no longer re-derives any of that against their copy. It sees
|
|
27
|
+
* two properties and takes the hooks on trust. Four things pay for that trust and none of them is a
|
|
28
|
+
* type an adopter has to compare: `tooling/vite-adopter` compiles the return against every major in
|
|
29
|
+
* the peer range and runs a real `vite build` through the plugin at each of them, {@link
|
|
30
|
+
* PITHY_PLUGIN_HOOKS} holds every hook name to something all three call, `plugin.test.ts` drives each
|
|
31
|
+
* hook by hand, and it runs a real `vite build` and reads the bundle.
|
|
32
|
+
*
|
|
33
|
+
* **One gap, named rather than papered over — Jim, 2026-08-21.** The hook *signatures* are checked
|
|
34
|
+
* against one Vite, and there is no arrangement in which they could be checked against three. Vite 8
|
|
35
|
+
* is rolldown-based and Vite 6 and 7 are rollup-based, so `hotUpdate`'s `this` is
|
|
36
|
+
* `MinimalPluginContext & { environment: DevEnvironment }` out of two different bundlers — `meta`
|
|
37
|
+
* carries `rolldownVersion` in one and not the other. A single object cannot be written `satisfies
|
|
38
|
+
* Plugin` against both; restoring `pithy(): Plugin` reports exactly that at 6.1.6 and 7.0.0, and it is
|
|
39
|
+
* a fact about the two Vites rather than about this plugin. What is checked across the range is the
|
|
40
|
+
* hook *set* and the hook *behavior*: a name Vite 6 never calls is a red build, and a build that does
|
|
41
|
+
* not inline the projection at 6.1.6 is a red test. What is taken on trust across the range is that a
|
|
42
|
+
* hook Vite 6 calls with an argument of its own shape reaches the same field.
|
|
43
|
+
*
|
|
44
|
+
* **Why the floor is `^6.1.0` is not recorded here, because the reason first written down was wrong —
|
|
45
|
+
* Jim, 2026-08-21.** It said `hotUpdate` arrived in 6.1 and that below it this plugin would be silently
|
|
46
|
+
* inert. It did not: `npm pack vite@6.0.0` carries `hotUpdate?: ObjectHook<…>` in `index.d.ts`, and its
|
|
47
|
+
* `HotUpdatePluginContext` is byte-identical to 6.1.6's. The floor may still be right for a reason
|
|
48
|
+
* nobody has written down, and `6.0.0` is not pinned in `tooling/vite-adopter`, so nothing here has ever
|
|
49
|
+
* compiled against it.
|
|
50
|
+
*
|
|
51
|
+
* Two honest next steps, neither taken in this change: pin `6.0.0` in the fixture and find out, or drop
|
|
52
|
+
* the floor to `^6.0.0` and let the fixture say whether that holds. **An invented reason is worse than
|
|
53
|
+
* an absent one**, because the next person reads it instead of measuring.
|
|
54
|
+
*/
|
|
55
|
+
export interface PithyPlugin {
|
|
56
|
+
/** `"pithy"`. The plugin's identity in Vite's plugin list, and in any error raised from a hook. */
|
|
57
|
+
name: string;
|
|
58
|
+
/**
|
|
59
|
+
* `"pre"`, always. `virtual:pithy/*` is claimed before any other plugin can, so the order of an
|
|
60
|
+
* adopter's `plugins` array never decides whether the front end can read its own backend.
|
|
61
|
+
*/
|
|
62
|
+
enforce: "pre";
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Every hook {@link pithy} defines, by name.
|
|
66
|
+
*
|
|
67
|
+
* **The one thing about the hooks an adopter's checker can be given for free.** {@link PithyPlugin}
|
|
68
|
+
* deliberately names no Vite type, so nothing in the return says which hooks exist — and a hook Vite
|
|
69
|
+
* never calls is dead code that looks like a feature. A name is not a Vite type: `tooling/vite-adopter`
|
|
70
|
+
* asserts each of these is a `keyof Plugin` in 6, 7 and 8, which costs an adopter nothing and catches
|
|
71
|
+
* the regression that matters here. Adding a `buildApp` hook, which Vite 7 introduced and Vite 6 has no
|
|
72
|
+
* name for, reddens the fixture rather than shipping under a range that claims 6.
|
|
73
|
+
*
|
|
74
|
+
* `plugin.test.ts` holds this list to the object itself, both directions, at runtime — a hook added
|
|
75
|
+
* without listing it is a failure there, and a name listed that no longer exists is one too. A list
|
|
76
|
+
* that can drift from what it lists is a worse gate than none.
|
|
77
|
+
*/
|
|
78
|
+
export declare const PITHY_PLUGIN_HOOKS: readonly ["configResolved", "configureServer", "resolveId", "load", "hotUpdate"];
|
|
79
|
+
/** One of the names {@link PITHY_PLUGIN_HOOKS} lists. */
|
|
80
|
+
export type PithyPluginHook = (typeof PITHY_PLUGIN_HOOKS)[number];
|
|
81
|
+
/** Options for {@link pithy}. Every one has a working default; `pithy()` is the usual call. */
|
|
82
|
+
export interface PithyPluginOptions {
|
|
83
|
+
/**
|
|
84
|
+
* The Worker's `pithy.config.ts`. Relative paths resolve against the Vite root, which for a Pithy
|
|
85
|
+
* Worker is `apps/<name>/` — so the default, `pithy.config.ts`, is already the right file.
|
|
86
|
+
*/
|
|
87
|
+
configFile?: string;
|
|
88
|
+
/**
|
|
89
|
+
* The environment this bundle is built for. Defaults to `ENVIRONMENT` in the process env, then
|
|
90
|
+
* `dev`. `pithy deploy --env <name>` is what threads a real value in, so a production bundle sees
|
|
91
|
+
* production's projection and a dev bundle sees dev's.
|
|
92
|
+
*/
|
|
93
|
+
environment?: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Serve one `virtual:pithy/<capability>` module per capability, projected from the Worker's own
|
|
97
|
+
* `pithy.config.ts`.
|
|
98
|
+
*
|
|
99
|
+
* A screen imports what it needs — `import auth from "virtual:pithy/auth"` — and gets that
|
|
100
|
+
* capability's client-safe projection inlined at build time. A capability the Worker does not compose
|
|
101
|
+
* resolves to `{ enabled: false }` rather than failing the import, including a name nobody has ever
|
|
102
|
+
* heard of: a screen branches on `enabled`, it does not guard on whether the module exists.
|
|
103
|
+
*/
|
|
104
|
+
export declare function pithy(options?: PithyPluginOptions): PithyPlugin;
|
|
105
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAcA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,MAAM,WAAW,WAAW;IAC1B,mGAAmG;IACnG,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,OAAO,EAAE,KAAK,CAAC;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,YAAI,gBAAgB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,CAAU,CAAC;AAEnH,yDAAyD;AACzD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,+FAA+F;AAC/F,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,OAAO,GAAE,kBAAuB,GAAG,WAAW,CA4FnE"}
|