@pracht/vite-plugin 0.11.0 → 0.11.1

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.
Files changed (2) hide show
  1. package/package.json +8 -4
  2. package/virtual.d.ts +255 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pracht/vite-plugin",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "Vite plugin for Pracht apps with virtual modules, dev SSR, prerendering, route inspection, and multi-adapter builds.",
5
5
  "keywords": [
6
6
  "pracht",
@@ -25,7 +25,8 @@
25
25
  "directory": "packages/vite-plugin"
26
26
  },
27
27
  "files": [
28
- "dist"
28
+ "dist",
29
+ "virtual.d.ts"
29
30
  ],
30
31
  "type": "module",
31
32
  "exports": {
@@ -36,6 +37,9 @@
36
37
  "./pages-router": {
37
38
  "types": "./dist/pages-router.d.mts",
38
39
  "default": "./dist/pages-router.mjs"
40
+ },
41
+ "./virtual": {
42
+ "types": "./virtual.d.ts"
39
43
  }
40
44
  },
41
45
  "publishConfig": {
@@ -48,8 +52,8 @@
48
52
  "es-module-lexer": "^1.7.0",
49
53
  "@pracht/adapter-node": "0.4.2",
50
54
  "@pracht/capabilities": "0.3.0",
51
- "@pracht/preact-ssr-precompile": "0.1.3",
52
- "@pracht/core": "0.16.0"
55
+ "@pracht/core": "0.16.0",
56
+ "@pracht/preact-ssr-precompile": "0.1.3"
53
57
  },
54
58
  "peerDependencies": {
55
59
  "vite": "^8.0.0"
package/virtual.d.ts ADDED
@@ -0,0 +1,255 @@
1
+ declare module "virtual:pracht/server" {
2
+ const mod: { fetch: (request: Request, env: any, ctx: any) => Promise<Response> };
3
+ export default mod;
4
+ }
5
+
6
+ declare module "virtual:pracht/client" {}
7
+
8
+ declare module "virtual:pracht/capabilities" {
9
+ import type {
10
+ CapabilityBrowserCallOptions,
11
+ CapabilityCallOptionsFor,
12
+ CapabilityInputArgs,
13
+ CapabilityInputFor,
14
+ CapabilityOutputFor,
15
+ HasRegisteredCapabilities,
16
+ HttpCapabilityName,
17
+ NonDestructiveCapabilityName,
18
+ Register,
19
+ } from "@pracht/core";
20
+ import type {
21
+ CapabilityEffect,
22
+ CapabilityEnvelope,
23
+ CapabilityErrorPayload,
24
+ CapabilityIssue,
25
+ } from "@pracht/capabilities";
26
+
27
+ // The envelope types are the protocol package's — re-exported so existing
28
+ // `import type { ... } from "virtual:pracht/capabilities"` keeps working.
29
+ export type { CapabilityEnvelope, CapabilityErrorPayload, CapabilityIssue };
30
+
31
+ export interface CallCapabilityOptions extends CapabilityBrowserCallOptions {
32
+ /**
33
+ * Confirmation token for committing a destructive capability, taken from
34
+ * the prior call's `confirmation_required` error envelope. Sets the
35
+ * confirmation header for you. A destructive call must either prepare with
36
+ * `{ prepare: true }` or commit with this token once `pracht typegen` has
37
+ * registered its effect class.
38
+ */
39
+ confirm?: string;
40
+ /**
41
+ * Start the prepare half of a destructive call. The server returns a
42
+ * `confirmation_required` envelope containing the token; repeat the call
43
+ * with `confirm` to commit. Typed destructive calls require exactly one of
44
+ * `prepare: true` or `confirm`.
45
+ */
46
+ prepare?: true;
47
+ /**
48
+ * Successful non-`read` calls revalidate the active route's data
49
+ * automatically; pass `false` to skip it for this call.
50
+ */
51
+ revalidate?: boolean;
52
+ }
53
+
54
+ /**
55
+ * Destructive calls require exactly one of `{ prepare: true }` or
56
+ * `{ confirm }`. `prepare` is not sent over the wire; the dispatcher uses it
57
+ * only to remove any confirmation token inherited through caller-supplied
58
+ * headers. The server is what refuses to run the unconfirmed call.
59
+ */
60
+ type OptionsFor<TName extends string> = CapabilityCallOptionsFor<TName, CallCapabilityOptions>;
61
+
62
+ /**
63
+ * HTTP endpoints of http-exposed capabilities, keyed by capability name.
64
+ *
65
+ * Has a **null prototype**, so a capability named `toString` cannot shadow an
66
+ * inherited member during lookup. Index it and enumerate it as usual, but
67
+ * reach for `Object.hasOwn(capabilityEndpoints, name)` rather than
68
+ * `capabilityEndpoints.hasOwnProperty(name)` — there is no `Object.prototype`
69
+ * to inherit that from. TypeScript cannot express the missing prototype, so
70
+ * the `Record` type below overstates what is available.
71
+ */
72
+ export const capabilityEndpoints: Record<
73
+ string,
74
+ { method: string; path: string; effect: CapabilityEffect | null }
75
+ >;
76
+
77
+ interface TypedCallCapability {
78
+ /**
79
+ * Names that cannot be `destructive`. Listed first and with an optional
80
+ * options argument, so it is always arity-compatible with a one- or
81
+ * two-argument call — which makes it the signature that reports what is
82
+ * wrong with an unresolvable name, instead of an argument count.
83
+ */
84
+ <TName extends NonDestructiveCapabilityName>(
85
+ name: TName,
86
+ ...args: CapabilityInputArgs<TName, CallCapabilityOptions>
87
+ ): Promise<CapabilityEnvelope<CapabilityOutputFor<TName>>>;
88
+ /** Possibly `destructive`: the prepare marker or the token is required. */
89
+ <TName extends HttpCapabilityName>(
90
+ name: TName,
91
+ input: CapabilityInputFor<TName>,
92
+ options: OptionsFor<TName>,
93
+ ): Promise<CapabilityEnvelope<CapabilityOutputFor<TName>>>;
94
+ }
95
+
96
+ interface UntypedCallCapability {
97
+ <T = unknown>(
98
+ name: string,
99
+ input?: unknown,
100
+ opts?: CallCapabilityOptions,
101
+ ): Promise<CapabilityEnvelope<T>>;
102
+ }
103
+
104
+ /**
105
+ * Invoke an http-exposed capability from the browser via its HTTP projection.
106
+ * Once `pracht typegen` has registered the capability graph on
107
+ * `Register["capabilities"]`, the name, input, output, and confirmation
108
+ * requirement all come from the registration: a private capability, an
109
+ * unknown name, a mismatched input, or a `destructive` call without an
110
+ * explicit prepare/commit choice are compile errors rather than runtime
111
+ * envelopes.
112
+ *
113
+ * Declared as a conditionally-typed value rather than as an overload pair
114
+ * whose fallback `name` resolves to `never`. That fallback survived overload
115
+ * resolution and absorbed anything arity filtering rejected, so every
116
+ * mistake — including a `destructive` call that merely forgot its options —
117
+ * came back as `'"notes.purge"' is not assignable to 'never'`: blaming the
118
+ * name, never naming the cause. Here the untyped form is simply absent for a
119
+ * registered app, and the two typed signatures split by effect class so that
120
+ * an unresolvable name is always arity-compatible with the first one and gets
121
+ * reported as a name.
122
+ *
123
+ * A dynamic name is no longer accepted once typegen has run; assert it with
124
+ * `name as HttpCapabilityName` when the name genuinely comes from data, and
125
+ * keep in mind the runtime still answers an unknown one with
126
+ * `unknown_capability`.
127
+ */
128
+ export const callCapability: HasRegisteredCapabilities extends true
129
+ ? TypedCallCapability
130
+ : UntypedCallCapability;
131
+
132
+ /**
133
+ * The same calls as `callCapability`, reached as a nested object built from
134
+ * the dotted capability names — `capabilities.notes.search({ query })`.
135
+ * Private capabilities are simply absent from it. Because the members are
136
+ * real property accesses, a typo here reports as "Property 'serach' does not
137
+ * exist … Did you mean 'search'?" — which `callCapability("notes.serach")`
138
+ * cannot do, since a string literal argument has no such suggestion.
139
+ *
140
+ * Current typegen output declares the nested client explicitly, so each leaf
141
+ * carries the capability's title and description as JSDoc. The mapped type
142
+ * below remains the compatibility fallback for older generated files.
143
+ *
144
+ * Identical runtime path to `callCapability` (same endpoint table, same
145
+ * settled event, same revalidation), so nothing forks between the two.
146
+ */
147
+ export const capabilities: PrachtCapabilityClient;
148
+
149
+ type GeneratedCapabilityClient = Register extends { capabilityClient: infer TClient }
150
+ ? TClient
151
+ : never;
152
+
153
+ /**
154
+ * Dotted names expanded into nested namespaces, http-exposed only. Current
155
+ * typegen output supplies the explicit client (including JSDoc); older
156
+ * generated files use the mapped fallback. Before typegen has run, every
157
+ * segment stays callable with unknown input/output.
158
+ */
159
+ export type PrachtCapabilityClient = HasRegisteredCapabilities extends true
160
+ ? [GeneratedCapabilityClient] extends [never]
161
+ ? CapabilityClientNode<HttpCapabilityName>
162
+ : GeneratedCapabilityClient
163
+ : Record<string, UntypedCapabilityClientNode>;
164
+
165
+ interface UntypedCapabilityClientNode {
166
+ <T = unknown>(input?: unknown, opts?: CallCapabilityOptions): Promise<CapabilityEnvelope<T>>;
167
+ [segment: string]: UntypedCapabilityClientNode;
168
+ }
169
+
170
+ type CapabilityMethod<TName extends string> = (
171
+ ...args: CapabilityInputArgs<TName, OptionsFor<TName>>
172
+ ) => Promise<CapabilityEnvelope<CapabilityOutputFor<TName>>>;
173
+
174
+ /**
175
+ * `Prefix` carries the already-consumed path so a leaf can look its own full
176
+ * dotted name back up in the flat registration map.
177
+ */
178
+ type CapabilitySegment<
179
+ TAll extends string,
180
+ TPrefix extends string,
181
+ > = TAll extends `${TPrefix}${infer TRest}`
182
+ ? TRest extends `${infer THead}.${string}`
183
+ ? THead
184
+ : TRest
185
+ : never;
186
+
187
+ /**
188
+ * A name that is also a prefix of another (`notes` alongside `notes.search`)
189
+ * cannot be both a function and a namespace. The runtime builder resolves
190
+ * that by letting the namespace win, so the type must too — otherwise
191
+ * `capabilities.notes(...)` would typecheck and throw at runtime. The
192
+ * shadowed name stays callable through `callCapability()`, and
193
+ * `pracht verify` warns about it.
194
+ */
195
+ type CapabilityClientNode<TAll extends string, TPrefix extends string = ""> = {
196
+ [TSeg in CapabilitySegment<TAll, TPrefix>]: [
197
+ Extract<TAll, `${TPrefix}${TSeg}.${string}`>,
198
+ ] extends [never]
199
+ ? CapabilityMethod<`${TPrefix}${TSeg}`>
200
+ : CapabilityClientNode<TAll, `${TPrefix}${TSeg}.`>;
201
+ };
202
+
203
+ /**
204
+ * Call state for a user-triggered capability call — a button, a search box, a
205
+ * picker. `call()` takes the same arguments as `callCapability` minus the
206
+ * name, and resolves to the same envelope.
207
+ *
208
+ * This is a mutation-shaped hook, not a fetch-on-render one: it dispatches
209
+ * when you call it, never during render. For data a page needs on load, run
210
+ * the capability in a `loader` with `invokeCapability()` — that result is
211
+ * server-rendered into the HTML and revalidates automatically after
212
+ * non-`read` calls, which a render-time fetch cannot do.
213
+ *
214
+ * ```tsx
215
+ * const search = useCapability("notes.search");
216
+ * await search.call({ query });
217
+ * // search.data / search.error / search.pending / search.reset()
218
+ * ```
219
+ *
220
+ * Concurrent calls are last-one-wins: an earlier response that arrives after
221
+ * a later one is discarded, so typing into a search box cannot show a stale
222
+ * result. `data` stays visible while a follow-up call is `pending`.
223
+ * It also remains the most recent successful result when that follow-up fails;
224
+ * only `reset()` or changing the capability name clears it.
225
+ */
226
+ export function useCapability<TName extends HttpCapabilityName>(
227
+ name: TName,
228
+ ): PrachtCapabilityHook<TName>;
229
+
230
+ export interface PrachtCapabilityHook<TName extends HttpCapabilityName> {
231
+ call: (
232
+ ...args: CapabilityInputArgs<TName, OptionsFor<TName>>
233
+ ) => Promise<CapabilityEnvelope<CapabilityOutputFor<TName>>>;
234
+ /** Data from the most recent successful call, until `reset()`. */
235
+ data: CapabilityOutputFor<TName> | undefined;
236
+ /** Error payload from the most recent failed call, until `reset()`. */
237
+ error: CapabilityErrorPayload | undefined;
238
+ /** Whether a call is in flight. */
239
+ pending: boolean;
240
+ /** Clear `data`/`error`/`pending` and abandon any in-flight result. */
241
+ reset: () => void;
242
+ }
243
+ }
244
+
245
+ declare module "virtual:pracht/webmcp" {
246
+ /** Registers WebMCP page tools; returns false when the API is unavailable. */
247
+ export function registerPrachtWebmcpTools(): boolean;
248
+ }
249
+
250
+ // Preserve the ambient declaration shipped with Pracht's compatibility-level
251
+ // `.tsrx` discovery. Other custom formats provide their own declaration.
252
+ declare module "*.tsrx" {
253
+ const mod: Record<string, unknown>;
254
+ export = mod;
255
+ }