@fougere/app 0.2.0-alpha.0

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 (50) hide show
  1. package/LICENSE +21 -0
  2. package/dist/auth.d.ts +11 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +10 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/boot.d.ts +35 -0
  7. package/dist/boot.d.ts.map +1 -0
  8. package/dist/boot.js +208 -0
  9. package/dist/boot.js.map +1 -0
  10. package/dist/client.d.ts +64 -0
  11. package/dist/client.d.ts.map +1 -0
  12. package/dist/client.js +124 -0
  13. package/dist/client.js.map +1 -0
  14. package/dist/express.d.ts +25 -0
  15. package/dist/express.d.ts.map +1 -0
  16. package/dist/express.js +180 -0
  17. package/dist/express.js.map +1 -0
  18. package/dist/form.d.ts +102 -0
  19. package/dist/form.d.ts.map +1 -0
  20. package/dist/form.js +104 -0
  21. package/dist/form.js.map +1 -0
  22. package/dist/graphql.d.ts +34 -0
  23. package/dist/graphql.d.ts.map +1 -0
  24. package/dist/graphql.js +58 -0
  25. package/dist/graphql.js.map +1 -0
  26. package/dist/index.d.ts +20 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +20 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/rest.d.ts +41 -0
  31. package/dist/rest.d.ts.map +1 -0
  32. package/dist/rest.js +92 -0
  33. package/dist/rest.js.map +1 -0
  34. package/dist/serve.d.ts +110 -0
  35. package/dist/serve.d.ts.map +1 -0
  36. package/dist/serve.js +136 -0
  37. package/dist/serve.js.map +1 -0
  38. package/dist/session.d.ts +14 -0
  39. package/dist/session.d.ts.map +1 -0
  40. package/dist/session.js +17 -0
  41. package/dist/session.js.map +1 -0
  42. package/dist/state.d.ts +3 -0
  43. package/dist/state.d.ts.map +1 -0
  44. package/dist/state.js +32 -0
  45. package/dist/state.js.map +1 -0
  46. package/dist/web.d.ts +25 -0
  47. package/dist/web.d.ts.map +1 -0
  48. package/dist/web.js +93 -0
  49. package/dist/web.js.map +1 -0
  50. package/package.json +86 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fougere contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/auth.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Auth accessor — reads the AuthRuntime that the core mounted on the app.
3
+ *
4
+ * Auth is no longer a separate singleton: it lives on `app.auth`, built once
5
+ * during boot from the `auth` field of fougere.config.ts. This accessor is a
6
+ * thin async helper for server code (middleware, routes, /api/me).
7
+ */
8
+ import type { AuthRuntime } from '@fougere/core';
9
+ /** Get the auth runtime resolved at boot. Throws if no `auth` was declared in fougere.config.ts. */
10
+ export declare function useFougereAuth(): Promise<AuthRuntime>;
11
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,oGAAoG;AACpG,wBAAsB,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC,CAQ3D"}
package/dist/auth.js ADDED
@@ -0,0 +1,10 @@
1
+ import { useFougereApp } from './boot.js';
2
+ /** Get the auth runtime resolved at boot. Throws if no `auth` was declared in fougere.config.ts. */
3
+ export async function useFougereAuth() {
4
+ const app = await useFougereApp();
5
+ if (!app.auth) {
6
+ throw new Error('Auth not configured — declare `auth: { provider, ... }` in fougere.config.ts.');
7
+ }
8
+ return app.auth;
9
+ }
10
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,oGAAoG;AACpG,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,GAAG,GAAG,MAAM,aAAa,EAAE,CAAC;IAClC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC;AAClB,CAAC"}
package/dist/boot.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ import type { App, EntityOrm } from '@fougere/core';
2
+ import { type SchemaLike } from '@fougere/schema';
3
+ export interface FougereServerConfig {
4
+ /** Storage handle. Forwarded to the auth provider via AuthContext.db. */
5
+ db?: unknown;
6
+ /** Per-entity ORM factory. */
7
+ ormFactory?: (entity: SchemaLike, name: string) => EntityOrm;
8
+ /** Called after app is created. Use for migrations, seeding, etc. */
9
+ afterBoot?: (app: App) => void | Promise<void>;
10
+ /**
11
+ * What the boot line names as the host — 'Nuxt/Nitro', 'Next'. Stated by the
12
+ * adapter, never sniffed: a boot that guesses its host from what happens to be
13
+ * importable is the hidden runtime the doctrine refuses.
14
+ */
15
+ host?: string;
16
+ }
17
+ /**
18
+ * Override the data layer — only needed if you don't want the convention-driven
19
+ * setup based on `config.db` in fougere.config.ts.
20
+ */
21
+ export declare function configureFougere(config: FougereServerConfig): void;
22
+ /** Get the booted Fougere app. Lazy — boots on first call, then caches. */
23
+ export declare function useFougereApp(): Promise<App>;
24
+ /**
25
+ * The store an app with no `db` runs on.
26
+ *
27
+ * It used to ignore both its arguments — `(_entity, _name)` — so it forced the field
28
+ * name `id`, minted a uuid whatever the entity declared, and realized none of the
29
+ * lifecycle rules: `created()` stamped nothing, a declared default stayed absent. The
30
+ * same page therefore behaved one way here and another way on SQLite.
31
+ *
32
+ * It reads the axes now, through the one realization every storage shares.
33
+ */
34
+ export declare function createMemoryOrm(entity: SchemaLike, name: string): EntityOrm;
35
+ //# sourceMappingURL=boot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boot.d.ts","sourceRoot":"","sources":["../src/boot.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAA4B,MAAM,eAAe,CAAC;AAC9E,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI5E,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,8BAA8B;IAC9B,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;IAC7D,qEAAqE;IACrE,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AASD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,QAG3D;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,CAK5C;AAiGD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAoE3E"}
package/dist/boot.js ADDED
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Fougere server bootstrap — single entry point for an app's lifecycle,
3
+ * whatever hosts it.
4
+ *
5
+ * Nothing here knows h3, Nitro, Vue or React: a boot reads `fougere.config.ts`,
6
+ * scans fronds off the filesystem and hands back an `App`. That is why it lives
7
+ * in `@fougere/app` rather than in one of the two adapters — the second host
8
+ * would otherwise have copied it, and a copied boot drifts (the seeding loop
9
+ * already did, `core/src/seed.ts`).
10
+ *
11
+ * Default path (zero-config): fougere.config.ts declares `db: 'sqlite'` and
12
+ * everything else; this module auto-resolves the storage handle, builds an
13
+ * ormFactory, runs auto-DDL from the entities, and boots the app.
14
+ *
15
+ * Escape hatch: `configureFougere({ db, ormFactory })` can be called from the
16
+ * host's own startup (a Nitro plugin, a Next instrumentation hook) if the user
17
+ * wants a custom data layer — alternative driver, managed migrations, etc.
18
+ */
19
+ import { createApp, loadCascadedConfig, setModuleLoader, frondAliases, Logger } from '@fougere/core';
20
+ import { createContainer } from '@fougere/container';
21
+ import { applyCreate, applyUpdate } from '@fougere/schema';
22
+ // ── State ────────────────────────────────────────
23
+ let _config = {};
24
+ let _appPromise = null;
25
+ // ── Public API ───────────────────────────────────
26
+ /**
27
+ * Override the data layer — only needed if you don't want the convention-driven
28
+ * setup based on `config.db` in fougere.config.ts.
29
+ */
30
+ export function configureFougere(config) {
31
+ _config = config;
32
+ _appPromise = null;
33
+ }
34
+ /** Get the booted Fougere app. Lazy — boots on first call, then caches. */
35
+ export function useFougereApp() {
36
+ if (!_appPromise) {
37
+ _appPromise = boot();
38
+ }
39
+ return _appPromise;
40
+ }
41
+ // ── Boot ─────────────────────────────────────────
42
+ async function boot() {
43
+ const bootStart = performance.now();
44
+ const log = new Logger('boot', { level: 'debug' });
45
+ log.info(`booting (${_config.host ?? 'app'})`);
46
+ const { createJiti } = await import('jiti');
47
+ // Nitro serves from a bundle, but the scan still reads frond sources from disk — so the
48
+ // named form a frond uses for its neighbour has to resolve here too.
49
+ const jiti = createJiti(import.meta.url, {
50
+ interopDefault: true,
51
+ alias: await frondAliases(process.env.FOUGERE_ROOT ?? process.cwd()),
52
+ });
53
+ setModuleLoader((filePath) => jiti.import(filePath));
54
+ // Config cascades along the workspace→app frontier: the workspace root (via
55
+ // FOUGERE_ROOT, where `remotes`/shared db live) is the base, the app (cwd)
56
+ // overrides. Same boundary the fronds cascade along. No `root` → both equal.
57
+ const configRoot = process.cwd();
58
+ const root = process.env.FOUGERE_ROOT ?? configRoot;
59
+ const fileConfig = await loadCascadedConfig(root, configRoot);
60
+ // Auto-resolve the data layer from config.db when the user didn't provide a
61
+ // custom one via configureFougere. The resolution itself lives in @fougere/runtime
62
+ // — this host must not know which storage package backs `db:`.
63
+ let db = _config.db;
64
+ let ormFactory = _config.ormFactory;
65
+ let migrateSchema;
66
+ if (!ormFactory) {
67
+ const { resolveStorage } = await import('@fougere/runtime');
68
+ const storage = resolveStorage(fileConfig.db);
69
+ if (storage.ormFactory) {
70
+ log.debug('auto-resolving storage from config.db');
71
+ db = storage.db;
72
+ ormFactory = storage.ormFactory;
73
+ migrateSchema = storage.afterBoot;
74
+ }
75
+ else {
76
+ log.debug('no db declared — falling back to in-memory ORM');
77
+ ormFactory = createMemoryOrm;
78
+ }
79
+ }
80
+ // Layer-2 wiring: `remotes: { catalog: 'http://...' }` in fougere.config.ts
81
+ // is all the user writes — the default transport comes from here.
82
+ let remoteTransport;
83
+ if (Object.keys(fileConfig.remotes ?? {}).length > 0) {
84
+ log.debug(`remotes declared (${Object.keys(fileConfig.remotes).join(', ')}) — wiring HTTP transport`);
85
+ const { createHttpTransport } = await import('@fougere/transport-http');
86
+ remoteTransport = (url) => createHttpTransport(url);
87
+ }
88
+ const app = await createApp({
89
+ root,
90
+ createContainer,
91
+ ormFactory,
92
+ db,
93
+ auth: fileConfig.auth,
94
+ adapters: fileConfig.adapters,
95
+ remotes: fileConfig.remotes,
96
+ remoteTransport,
97
+ });
98
+ // Bring the schema up to date — creates missing tables and adds columns an
99
+ // entity gained. Auth entities travel with the app, no synthetic frond needed.
100
+ if (migrateSchema) {
101
+ log.debug('migrating schema from entities');
102
+ await migrateSchema(app);
103
+ }
104
+ if (_config.afterBoot) {
105
+ // A host that declares `afterBoot` OWNS what happens after the boot, seeding
106
+ // included — Nuxt's generated Nitro plugin does exactly that, because Nitro
107
+ // bundles and the seed modules have to be spelled out as static imports for it.
108
+ log.debug('running afterBoot');
109
+ await _config.afterBoot(app);
110
+ log.info('afterBoot done');
111
+ }
112
+ else {
113
+ // Nobody claimed it, so the boot seeds. The scan already carries each seed's
114
+ // data (`SeedEntry.data` is its resolved default export), and the order comes
115
+ // from core — the same `orderSeeds`/`runSeeds` pair Nuxt's plugin calls. A host
116
+ // that does not bundle its frond sources needs nothing else.
117
+ const { orderSeeds, runSeeds } = await import('@fougere/core');
118
+ await runSeeds(app, orderSeeds(app.fronds), (message) => log.info(`[seed]${message}`));
119
+ }
120
+ const ms = (performance.now() - bootStart).toFixed(0);
121
+ log.info(`ready in ${ms}ms — ${app.fronds.length} frond(s)${app.auth ? ` + auth (${app.auth.basePath})` : ''}`);
122
+ return app;
123
+ }
124
+ // ── Fallback ORM ─────────────────────────────────
125
+ /**
126
+ * The store an app with no `db` runs on.
127
+ *
128
+ * It used to ignore both its arguments — `(_entity, _name)` — so it forced the field
129
+ * name `id`, minted a uuid whatever the entity declared, and realized none of the
130
+ * lifecycle rules: `created()` stamped nothing, a declared default stayed absent. The
131
+ * same page therefore behaved one way here and another way on SQLite.
132
+ *
133
+ * It reads the axes now, through the one realization every storage shares.
134
+ */
135
+ export function createMemoryOrm(entity, name) {
136
+ const fields = entity.getFields();
137
+ const pk = Object.entries(fields).find(([, field]) => field.role?.primary)?.[0] ?? 'id';
138
+ const store = new Map();
139
+ // `EntityOrm.findById(id: string)` — but a key can hold a number, and a Map keyed on
140
+ // `1` does not answer `'1'`. SQL never had the question; here the divergence was
141
+ // silent and only on this storage.
142
+ const keyOf = (value) => String(value);
143
+ const matches = (row, criteria) => Object.entries(criteria).every(([key, value]) => Object.is(row[key], value));
144
+ return {
145
+ client: store,
146
+ async list(options) {
147
+ let items = [...store.values()];
148
+ if (options?.where)
149
+ items = items.filter((row) => matches(row, options.where));
150
+ // Held before the page is cut, and after the filter: `total` answers "how many
151
+ // match", which is what a paginator divides. Reading `store.size` at the end
152
+ // answered a different question — every row the store holds, including the ones
153
+ // the filter exists to keep out of this caller's sight.
154
+ const matching = items.length;
155
+ const limit = options?.limit;
156
+ const offset = options?.page && limit ? (options.page - 1) * limit : options?.offset ?? 0;
157
+ if (offset > 0)
158
+ items = items.slice(offset);
159
+ const hasMore = limit ? items.length > limit : false;
160
+ if (limit)
161
+ items = items.slice(0, limit);
162
+ const result = items;
163
+ result.hasMore = hasMore;
164
+ result.endCursor = items.length > 0 ? String(items[items.length - 1][pk] ?? '') : undefined;
165
+ if (options?.count)
166
+ result.total = matching;
167
+ return result;
168
+ },
169
+ async findById(id) { return store.get(keyOf(id)); },
170
+ async findBy(criteria) {
171
+ return [...store.values()].find((row) => matches(row, criteria));
172
+ },
173
+ async findAllBy(criteria) {
174
+ return [...store.values()].filter((row) => matches(row, criteria));
175
+ },
176
+ async create(input) {
177
+ const record = applyCreate(fields, input);
178
+ const id = record[pk];
179
+ // `primary(text())` declares no generator, so nothing fills the hole and the
180
+ // caller has to. Keying on `undefined` would let the second create overwrite the
181
+ // first, in silence — the old version hid this by inventing an `id` field the
182
+ // entity never declared.
183
+ if (id === undefined) {
184
+ throw new Error(`${name}.create: '${pk}' is the primary key and nothing supplied it — this entity declares no generator for it.`);
185
+ }
186
+ // A create is not an upsert. `Map.set` overwrites, so a second create under the
187
+ // same key answered "created" while destroying the previous row — SQL answers a
188
+ // constraint violation, and a store that loses data silently is worse than one
189
+ // that fails.
190
+ if (store.has(keyOf(id))) {
191
+ throw new Error(`${name}.create: '${pk}' ${JSON.stringify(id)} already exists.`);
192
+ }
193
+ store.set(keyOf(id), record);
194
+ return record;
195
+ },
196
+ async update(id, input) {
197
+ const existing = store.get(keyOf(id));
198
+ if (!existing)
199
+ throw new Error(`Not found: ${id}`);
200
+ const updated = { ...existing, ...applyUpdate(fields, input), [pk]: existing[pk] };
201
+ store.set(keyOf(id), updated);
202
+ return updated;
203
+ },
204
+ async delete(id) { return store.delete(keyOf(id)); },
205
+ output(_schema) { return this; },
206
+ };
207
+ }
208
+ //# sourceMappingURL=boot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boot.js","sourceRoot":"","sources":["../src/boot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The couple, minus the reactivity — everything `useQuery`/`useCommand` decide
3
+ * before a framework's state primitives get involved.
4
+ *
5
+ * Designation is class + verb: the imported entity class carries the metadata,
6
+ * its name carries the registration key. That is true in Vue and in React, and so
7
+ * is the link — a successful command on an entity revalidates every mounted query
8
+ * on that entity, because the entity is designated on both sides and nothing has
9
+ * to be declared. What differs between hosts is only HOW a value becomes reactive
10
+ * and how a revalidation is triggered, which is ~50 lines each and belongs to them.
11
+ *
12
+ * Browser-safe by construction: this module reaches `@fougere/core/contract` and
13
+ * the transport's client subpath, never the boot. `@fougere/app/client` is the
14
+ * subpath that keeps it that way.
15
+ */
16
+ import { FougereError, type InvocationContext, type FrondCall } from '@fougere/core/contract';
17
+ /** An entity class is a designation: its name is the registration key. */
18
+ export type EntityClass = {
19
+ name: string;
20
+ };
21
+ /** What a page provides of an invocation — the rest is stamped server-side. */
22
+ export type CallInput = Partial<Pick<InvocationContext, 'params' | 'query' | 'body'>>;
23
+ /** The one door the browser knows. A named surface adds `/{surface}` to it. */
24
+ export declare const CALL_ENDPOINT = "/_fougere/call";
25
+ export type Fetcher = <T>(url: string, options: {
26
+ method: 'POST';
27
+ body: unknown;
28
+ }) => Promise<T>;
29
+ /** The registration key an entity class designates. */
30
+ export declare function entityKeyOf(entity: EntityClass): string;
31
+ export declare function callOf(entity: EntityClass, op: string): FrondCall;
32
+ export declare function invocationOf(input?: CallInput): InvocationContext;
33
+ /**
34
+ * The cache key of a read. Same designation and same input means the same key —
35
+ * which is what lets two components asking the same thing share one request, and
36
+ * what the command side matches against to revalidate.
37
+ */
38
+ export declare function queryKeyOf(entityKey: string, op: string, input?: CallInput): string;
39
+ export declare function sendCall(fetcher: Fetcher, call: FrondCall, invocation: InvocationContext, endpoint?: string): Promise<unknown>;
40
+ /** Register a mounted read. Returns the unregistration, for the host's scope teardown. */
41
+ export declare function trackQuery(entityKey: string, key: string): () => void;
42
+ /** The keys a successful command on this entity should revalidate. */
43
+ export declare function mountedKeys(entityKey: string): string[];
44
+ /** Register a mounted read's refetch. Returns the unregistration. */
45
+ export declare function onRefetch(key: string, run: () => void): () => void;
46
+ export declare function revalidate(keys: string[]): void;
47
+ /** The browser's way to reach the envelope. Same-origin, so no base URL to configure. */
48
+ export declare const fetcher: Fetcher;
49
+ /** A list result reads as items whatever the wire delivered — bare array or envelope. */
50
+ export declare function itemsOf<T>(data: unknown): T[];
51
+ export declare function pageOf(data: unknown): {
52
+ total?: number;
53
+ hasMore?: boolean;
54
+ endCursor?: string;
55
+ };
56
+ /**
57
+ * Whatever failed, as the error the primitives promise. A transport failure is not
58
+ * a domain refusal, so it arrives under SERVICE_UNAVAILABLE rather than borrowing a
59
+ * code the server never sent.
60
+ */
61
+ export declare function asFougereError(err: unknown, entityKey: string, op: string): FougereError;
62
+ export { formFieldsOf, payloadOf, errorsByField, type FormEntity, type FormField, } from './form.js';
63
+ export { sessionViewOf, type SessionView } from './session.js';
64
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EACL,YAAY,EAGZ,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACf,MAAM,wBAAwB,CAAC;AAGhC,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3C,+EAA+E;AAC/E,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC;AAEtF,+EAA+E;AAC/E,eAAO,MAAM,aAAa,mBAAmB,CAAC;AAE9C,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAIjG,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEvD;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,CAEjE;AAED,wBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,iBAAiB,CAEjE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,GAAG,MAAM,CAEnF;AAED,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,iBAAiB,EAC7B,QAAQ,GAAE,MAAsB,GAC/B,OAAO,CAAC,OAAO,CAAC,CAMlB;AAOD,0FAA0F;AAC1F,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,IAAI,CAKrE;AAED,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvD;AAYD,qEAAqE;AACrE,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAKlE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAE/C;AAED,yFAAyF;AACzF,eAAO,MAAM,OAAO,EAAE,OAOrB,CAAC;AAIF,yFAAyF;AACzF,wBAAgB,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,CAAC,EAAE,CAM7C;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAE/F;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,YAAY,CAUxF;AAKD,OAAO,EACL,YAAY,EACZ,SAAS,EACT,aAAa,EACb,KAAK,UAAU,EACf,KAAK,SAAS,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,124 @@
1
+ /**
2
+ * The couple, minus the reactivity — everything `useQuery`/`useCommand` decide
3
+ * before a framework's state primitives get involved.
4
+ *
5
+ * Designation is class + verb: the imported entity class carries the metadata,
6
+ * its name carries the registration key. That is true in Vue and in React, and so
7
+ * is the link — a successful command on an entity revalidates every mounted query
8
+ * on that entity, because the entity is designated on both sides and nothing has
9
+ * to be declared. What differs between hosts is only HOW a value becomes reactive
10
+ * and how a revalidation is triggered, which is ~50 lines each and belongs to them.
11
+ *
12
+ * Browser-safe by construction: this module reaches `@fougere/core/contract` and
13
+ * the transport's client subpath, never the boot. `@fougere/app/client` is the
14
+ * subpath that keeps it that way.
15
+ */
16
+ import { FougereError, ErrorCode, toRegistrationName, } from '@fougere/core/contract';
17
+ import { frameCall, unframeResponse } from '@fougere/transport-http/client';
18
+ /** The one door the browser knows. A named surface adds `/{surface}` to it. */
19
+ export const CALL_ENDPOINT = '/_fougere/call';
20
+ let nextId = 1;
21
+ /** The registration key an entity class designates. */
22
+ export function entityKeyOf(entity) {
23
+ return toRegistrationName(entity.name);
24
+ }
25
+ export function callOf(entity, op) {
26
+ return { entity: entityKeyOf(entity), op };
27
+ }
28
+ export function invocationOf(input) {
29
+ return { params: {}, query: {}, body: undefined, state: {}, ...input };
30
+ }
31
+ /**
32
+ * The cache key of a read. Same designation and same input means the same key —
33
+ * which is what lets two components asking the same thing share one request, and
34
+ * what the command side matches against to revalidate.
35
+ */
36
+ export function queryKeyOf(entityKey, op, input) {
37
+ return `fougere:${entityKey}.${op}:${JSON.stringify(input ?? {})}`;
38
+ }
39
+ export async function sendCall(fetcher, call, invocation, endpoint = CALL_ENDPOINT) {
40
+ const response = await fetcher(endpoint, {
41
+ method: 'POST',
42
+ body: frameCall(call, invocation, nextId++),
43
+ });
44
+ return unframeResponse(response, call);
45
+ }
46
+ // ── The link ─────────────────────────────────────
47
+ /** Mounted queries per entity — the command side of the link reads this. */
48
+ const mounted = new Map();
49
+ /** Register a mounted read. Returns the unregistration, for the host's scope teardown. */
50
+ export function trackQuery(entityKey, key) {
51
+ const keys = mounted.get(entityKey) ?? new Set();
52
+ keys.add(key);
53
+ mounted.set(entityKey, keys);
54
+ return () => keys.delete(key);
55
+ }
56
+ /** The keys a successful command on this entity should revalidate. */
57
+ export function mountedKeys(entityKey) {
58
+ return [...(mounted.get(entityKey) ?? [])];
59
+ }
60
+ /**
61
+ * `mountedKeys` says WHICH reads a command invalidates; these say how to make one
62
+ * happen. Both halves turned out to be host-independent — Nuxt is the exception,
63
+ * because `refreshNuxtData` already is this registry.
64
+ *
65
+ * They lived in `@fougere/react` until a second non-Nuxt client needed them, which
66
+ * is when it became visible that nothing in them is React.
67
+ */
68
+ const refetchers = new Map();
69
+ /** Register a mounted read's refetch. Returns the unregistration. */
70
+ export function onRefetch(key, run) {
71
+ const set = refetchers.get(key) ?? new Set();
72
+ set.add(run);
73
+ refetchers.set(key, set);
74
+ return () => set.delete(run);
75
+ }
76
+ export function revalidate(keys) {
77
+ for (const key of keys)
78
+ for (const run of refetchers.get(key) ?? [])
79
+ run();
80
+ }
81
+ /** The browser's way to reach the envelope. Same-origin, so no base URL to configure. */
82
+ export const fetcher = async (url, options) => {
83
+ const response = await fetch(url, {
84
+ method: options.method,
85
+ headers: { 'content-type': 'application/json' },
86
+ body: JSON.stringify(options.body),
87
+ });
88
+ return (await response.json());
89
+ };
90
+ // ── Reading a result ─────────────────────────────
91
+ /** A list result reads as items whatever the wire delivered — bare array or envelope. */
92
+ export function itemsOf(data) {
93
+ if (Array.isArray(data))
94
+ return data;
95
+ if (data && typeof data === 'object' && Array.isArray(data.items)) {
96
+ return data.items;
97
+ }
98
+ return [];
99
+ }
100
+ export function pageOf(data) {
101
+ return (data ?? {});
102
+ }
103
+ /**
104
+ * Whatever failed, as the error the primitives promise. A transport failure is not
105
+ * a domain refusal, so it arrives under SERVICE_UNAVAILABLE rather than borrowing a
106
+ * code the server never sent.
107
+ */
108
+ export function asFougereError(err, entityKey, op) {
109
+ return err instanceof FougereError
110
+ ? err
111
+ : new FougereError({
112
+ code: ErrorCode.SERVICE_UNAVAILABLE,
113
+ message: err?.message ?? String(err),
114
+ entity: entityKey,
115
+ operation: op,
116
+ cause: err,
117
+ });
118
+ }
119
+ // The form contract is host-independent too, and a form is client code — so it
120
+ // reaches the browser through this subpath rather than through the package root,
121
+ // which carries the boot.
122
+ export { formFieldsOf, payloadOf, errorsByField, } from './form.js';
123
+ export { sessionViewOf } from './session.js';
124
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ type Next = (err?: unknown) => void;
2
+ export type ExpressMiddleware = (req: any, res: any, next: Next) => void;
3
+ /**
4
+ * The call envelope, at `/_fougere/call` — the door the browser primitives use.
5
+ *
6
+ * Mounted with `app.use()`, Express strips nothing, so the path still carries the
7
+ * audience segment (`/_fougere/call/public`) that `surfaceOf` reads.
8
+ */
9
+ export declare function fougereCall(mountPath?: string): ExpressMiddleware;
10
+ /** The session view, at `/_fougere/session`. */
11
+ export declare function fougereSession(mountPath?: string): ExpressMiddleware;
12
+ /**
13
+ * The REST projection, under `/api` by default.
14
+ *
15
+ * A path this app does not serve calls `next()` — so an app's own `/api/health` keeps
16
+ * answering whether it was registered before or after this middleware.
17
+ */
18
+ export declare function fougereRest(mountPath?: string): ExpressMiddleware;
19
+ /** GraphQL, at `/graphql` by default. Declines when the app declares no such adapter. */
20
+ export declare function fougereGraphQL(mountPath?: string): ExpressMiddleware;
21
+ /** Every door, for an app that wants all of them. What each one SERVES is still the
22
+ * app's declaration — mounting is not publishing. */
23
+ export declare function fougere(): ExpressMiddleware;
24
+ export {};
25
+ //# sourceMappingURL=express.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AA0DA,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AACpC,MAAM,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;AAsCzE;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,SAAS,SAAmB,GAAG,iBAAiB,CAqB3E;AAED,gDAAgD;AAChD,wBAAgB,cAAc,CAAC,SAAS,SAAsB,GAAG,iBAAiB,CAKjF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,SAAS,SAAS,GAAG,iBAAiB,CA4BjE;AAED,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,SAAS,SAAa,GAAG,iBAAiB,CAoBxE;AAED;sDACsD;AACtD,wBAAgB,OAAO,IAAI,iBAAiB,CAY3C"}