@fougere/core 0.1.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.
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/binding.d.ts +63 -0
- package/dist/binding.d.ts.map +1 -0
- package/dist/binding.js +107 -0
- package/dist/binding.js.map +1 -0
- package/dist/boot.d.ts +38 -0
- package/dist/boot.d.ts.map +1 -0
- package/dist/boot.js +53 -0
- package/dist/boot.js.map +1 -0
- package/dist/bootstrap.d.ts +4 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +825 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/builtins/config.d.ts +4 -0
- package/dist/builtins/config.d.ts.map +1 -0
- package/dist/builtins/config.js +6 -0
- package/dist/builtins/config.js.map +1 -0
- package/dist/builtins/logger.d.ts +28 -0
- package/dist/builtins/logger.d.ts.map +1 -0
- package/dist/builtins/logger.js +76 -0
- package/dist/builtins/logger.js.map +1 -0
- package/dist/call.d.ts +172 -0
- package/dist/call.d.ts.map +1 -0
- package/dist/call.js +209 -0
- package/dist/call.js.map +1 -0
- package/dist/collector.d.ts +28 -0
- package/dist/collector.d.ts.map +1 -0
- package/dist/collector.js +32 -0
- package/dist/collector.js.map +1 -0
- package/dist/config-loader.d.ts +59 -0
- package/dist/config-loader.d.ts.map +1 -0
- package/dist/config-loader.js +97 -0
- package/dist/config-loader.js.map +1 -0
- package/dist/contract.d.ts +43 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +29 -0
- package/dist/contract.js.map +1 -0
- package/dist/crud.d.ts +61 -0
- package/dist/crud.d.ts.map +1 -0
- package/dist/crud.js +85 -0
- package/dist/crud.js.map +1 -0
- package/dist/define.d.ts +8 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/define.js +9 -0
- package/dist/define.js.map +1 -0
- package/dist/egress.d.ts +78 -0
- package/dist/egress.d.ts.map +1 -0
- package/dist/egress.js +185 -0
- package/dist/egress.js.map +1 -0
- package/dist/emit.d.ts +51 -0
- package/dist/emit.d.ts.map +1 -0
- package/dist/emit.js +57 -0
- package/dist/emit.js.map +1 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +74 -0
- package/dist/errors.js.map +1 -0
- package/dist/frond-config.d.ts +80 -0
- package/dist/frond-config.d.ts.map +1 -0
- package/dist/frond-config.js +32 -0
- package/dist/frond-config.js.map +1 -0
- package/dist/graph.d.ts +33 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +142 -0
- package/dist/graph.js.map +1 -0
- package/dist/handler-parser.d.ts +82 -0
- package/dist/handler-parser.d.ts.map +1 -0
- package/dist/handler-parser.js +505 -0
- package/dist/handler-parser.js.map +1 -0
- package/dist/imports.d.ts +22 -0
- package/dist/imports.d.ts.map +1 -0
- package/dist/imports.js +80 -0
- package/dist/imports.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/invocation.d.ts +20 -0
- package/dist/invocation.d.ts.map +1 -0
- package/dist/invocation.js +15 -0
- package/dist/invocation.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +15 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +29 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/logger.d.ts +14 -0
- package/dist/middleware/logger.d.ts.map +1 -0
- package/dist/middleware/logger.js +25 -0
- package/dist/middleware/logger.js.map +1 -0
- package/dist/middleware.d.ts +75 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +89 -0
- package/dist/middleware.js.map +1 -0
- package/dist/operation.d.ts +87 -0
- package/dist/operation.d.ts.map +1 -0
- package/dist/operation.js +95 -0
- package/dist/operation.js.map +1 -0
- package/dist/orm.d.ts +96 -0
- package/dist/orm.d.ts.map +1 -0
- package/dist/orm.js +33 -0
- package/dist/orm.js.map +1 -0
- package/dist/presenter.d.ts +63 -0
- package/dist/presenter.d.ts.map +1 -0
- package/dist/presenter.js +72 -0
- package/dist/presenter.js.map +1 -0
- package/dist/remote.d.ts +48 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +135 -0
- package/dist/remote.js.map +1 -0
- package/dist/repository.d.ts +55 -0
- package/dist/repository.d.ts.map +1 -0
- package/dist/repository.js +26 -0
- package/dist/repository.js.map +1 -0
- package/dist/scan-cache.d.ts +10 -0
- package/dist/scan-cache.d.ts.map +1 -0
- package/dist/scan-cache.js +92 -0
- package/dist/scan-cache.js.map +1 -0
- package/dist/scanner.d.ts +34 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +595 -0
- package/dist/scanner.js.map +1 -0
- package/dist/seed.d.ts +28 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +110 -0
- package/dist/seed.js.map +1 -0
- package/dist/types.d.ts +387 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/verify.d.ts +57 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +147 -0
- package/dist/verify.js.map +1 -0
- package/package.json +47 -0
package/dist/seed.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeds in dependency order — a `ref()` target is planted before its referrer.
|
|
3
|
+
*
|
|
4
|
+
* The order is a fact about the entity graph, not about the surface running the seeds,
|
|
5
|
+
* so it is stated once here. The Nuxt module read it off a pairwise comparator (which
|
|
6
|
+
* `Array.sort` cannot make transitive: `a` before `b` and `b` before `c` never implies
|
|
7
|
+
* `a` before `c`), and `boot()` had no order at all — the same fact, told twice and
|
|
8
|
+
* wrong both times. A list seeded before its owner failed on the foreign key, and the
|
|
9
|
+
* driver's error named neither the entity nor the file.
|
|
10
|
+
*
|
|
11
|
+
* Kahn, like `orderTables` in `@fougere/schema-sql` — the same shape one level up: there
|
|
12
|
+
* the nodes are tables and the edges FK columns, here they are seeds and the edges the
|
|
13
|
+
* `one` relations of the entities they target. A cycle cannot be satisfied by ordering,
|
|
14
|
+
* so its remaining seeds keep their scan order and land last: the driver refuses what is
|
|
15
|
+
* genuinely impossible, and everything acyclic around it still gets planted.
|
|
16
|
+
*/
|
|
17
|
+
export function orderSeeds(fronds) {
|
|
18
|
+
const refs = new Map();
|
|
19
|
+
for (const frond of fronds) {
|
|
20
|
+
for (const entity of frond.entities) {
|
|
21
|
+
const targets = new Set();
|
|
22
|
+
for (const field of Object.values(entity.entityClass.getFields())) {
|
|
23
|
+
if (field.role?.relation?.kind !== 'one')
|
|
24
|
+
continue;
|
|
25
|
+
const target = field.role.relation.to().name?.toLowerCase();
|
|
26
|
+
if (target && target !== entity.name.toLowerCase())
|
|
27
|
+
targets.add(target);
|
|
28
|
+
}
|
|
29
|
+
refs.set(entity.name.toLowerCase(), targets);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const seeds = fronds.flatMap((frond) => frond.seeds);
|
|
33
|
+
const seeded = new Set(seeds.map((seed) => seed.entityName.toLowerCase()));
|
|
34
|
+
// Only what is actually seeded can be waited for: a relation to an entity with no seed
|
|
35
|
+
// is already satisfied by whatever put its rows there.
|
|
36
|
+
const waiting = new Map(seeds.map((seed) => {
|
|
37
|
+
const own = seed.entityName.toLowerCase();
|
|
38
|
+
const targets = [...(refs.get(own) ?? [])].filter((target) => seeded.has(target) && target !== own);
|
|
39
|
+
return [seed, new Set(targets)];
|
|
40
|
+
}));
|
|
41
|
+
const ordered = [];
|
|
42
|
+
const planted = new Set();
|
|
43
|
+
while (waiting.size > 0) {
|
|
44
|
+
const ready = [...waiting.keys()].find((seed) => [...waiting.get(seed)].every((dep) => planted.has(dep)));
|
|
45
|
+
if (!ready)
|
|
46
|
+
break;
|
|
47
|
+
ordered.push(ready);
|
|
48
|
+
planted.add(ready.entityName.toLowerCase());
|
|
49
|
+
waiting.delete(ready);
|
|
50
|
+
}
|
|
51
|
+
return [...ordered, ...waiting.keys()];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Plant a set of seeds, in the order given. Reports what it did, one line per entity.
|
|
55
|
+
*
|
|
56
|
+
* The one seeding loop. `boot()` had one and the Nuxt module generated a second into its
|
|
57
|
+
* Nitro plugin, which had drifted: no ORM fallback, so an entity with no façade was
|
|
58
|
+
* skipped there and planted here. Two answers to "how does a row get in at boot" is one
|
|
59
|
+
* too many, and the second was the one running in the browser.
|
|
60
|
+
*/
|
|
61
|
+
export async function runSeeds(app, seeds, report = () => { }) {
|
|
62
|
+
for (const seed of seeds) {
|
|
63
|
+
const resolve = (name) => app.resolve(name + 'Handler');
|
|
64
|
+
const data = typeof seed.data === 'function' ? await seed.data(resolve) : seed.data;
|
|
65
|
+
const door = doorFor(app, seed.entityName);
|
|
66
|
+
if (!door) {
|
|
67
|
+
report(` ${seed.entityName}: no handler façade nor ORM — skipping seed`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const existing = await door.list();
|
|
71
|
+
if (existing.length > 0) {
|
|
72
|
+
report(` ${seed.entityName}: skipped (${existing.length} exist)`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
// The driver's error alone names neither the entity nor the row — and a foreign
|
|
76
|
+
// key is exactly what a seed gets wrong.
|
|
77
|
+
for (const item of data) {
|
|
78
|
+
try {
|
|
79
|
+
await door.write(item);
|
|
80
|
+
}
|
|
81
|
+
catch (cause) {
|
|
82
|
+
throw new Error(`Seed '${seed.entityName}' failed on ${JSON.stringify(item)}: ${cause?.message ?? cause}`, { cause });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
report(` ${seed.entityName}: ${data.length} records`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A seed is not a client: it writes at boot, from inside. The façade is used when the
|
|
90
|
+
* entity declares one (its judge catches a bad seed early), the storage when it does
|
|
91
|
+
* not — an entity that exposes nothing is still an entity whose reference rows must land.
|
|
92
|
+
*/
|
|
93
|
+
function doorFor(app, entityName) {
|
|
94
|
+
let handler;
|
|
95
|
+
try {
|
|
96
|
+
handler = app.resolve(`${entityName}Handler`);
|
|
97
|
+
}
|
|
98
|
+
catch { }
|
|
99
|
+
if (typeof handler?.list === 'function' && typeof handler.create === 'function') {
|
|
100
|
+
return {
|
|
101
|
+
list: () => handler.list(),
|
|
102
|
+
write: (item) => handler.create({ params: {}, query: {}, body: item, state: {} }),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const orm = app.ormFor(entityName);
|
|
106
|
+
if (!orm)
|
|
107
|
+
return undefined;
|
|
108
|
+
return { list: () => orm.list(), write: (item) => orm.create(item) };
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=seed.js.map
|
package/dist/seed.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.js","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import type { Container } from '@fougere/container';
|
|
2
|
+
import type { ParsedParam } from './operation.js';
|
|
3
|
+
import type { EntityOrm, OrmFactory } from './orm.js';
|
|
4
|
+
import type { SchemaLike } from '@fougere/schema';
|
|
5
|
+
import type { OperationContract, OperationsMap } from './operation.js';
|
|
6
|
+
import type { AppMiddleware } from './middleware.js';
|
|
7
|
+
import type { PresenterViews } from './presenter.js';
|
|
8
|
+
import type { Transport } from './call.js';
|
|
9
|
+
/**
|
|
10
|
+
* A discovered provider — a class under `services/` or `repositories/`, injected by type.
|
|
11
|
+
*
|
|
12
|
+
* Which of the two directories it came from is deliberately not recorded. The scan used
|
|
13
|
+
* to tag it `kind: 'service' | 'repository'`, a field it filled on every provider and
|
|
14
|
+
* that **no consumer ever read** — the two directories are two spellings of the same
|
|
15
|
+
* thing, and DI resolves by type either way.
|
|
16
|
+
*/
|
|
17
|
+
export interface ProviderEntry {
|
|
18
|
+
/** Registration key: class name with first letter lowercased. */
|
|
19
|
+
name: string;
|
|
20
|
+
/** The class constructor (default export of the file). */
|
|
21
|
+
ctor: new (...args: unknown[]) => unknown;
|
|
22
|
+
/** Constructor dependency type names (from AST scan). */
|
|
23
|
+
deps: string[];
|
|
24
|
+
/** Absolute file path (for debugging). */
|
|
25
|
+
filePath: string;
|
|
26
|
+
}
|
|
27
|
+
/** A discovered entity (Entity subclass in entities/ dir). */
|
|
28
|
+
export interface EntityEntry {
|
|
29
|
+
/** Registration key: class name lowercased first char (e.g. 'product'). */
|
|
30
|
+
name: string;
|
|
31
|
+
/** The Entity class (has static getFields()). */
|
|
32
|
+
entityClass: SchemaLike;
|
|
33
|
+
/** Absolute file path (for debugging). */
|
|
34
|
+
filePath: string;
|
|
35
|
+
/** Whether this entity is part of the frond's public contract. */
|
|
36
|
+
exposed?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** A discovered handler (controls what a service exposes). */
|
|
39
|
+
export interface HandlerEntry {
|
|
40
|
+
/** Registration key (e.g. 'postHandler'). */
|
|
41
|
+
name: string;
|
|
42
|
+
/**
|
|
43
|
+
* The name this handler answers to — its class name minus `Handler`, lowercased
|
|
44
|
+
* (`PostHandler` → `post`). It is what `facadeKeyOf` builds its key from and what
|
|
45
|
+
* travels as `FrondCall.entity` on the wire.
|
|
46
|
+
*
|
|
47
|
+
* **It is NOT an entity name**, and it was called `entityName` for long enough to
|
|
48
|
+
* mislead: `toEntityName` (`scanner.ts`) strips the suffix without checking that any
|
|
49
|
+
* entity carries the result, and the boot states the consequence — *pointing at
|
|
50
|
+
* nothing is legal*. A health check, a search across several shapes, a pure
|
|
51
|
+
* computation: ordinary handlers that own no row. `PresenterEntry`/`CollectorEntry`
|
|
52
|
+
* keep `entityName` because those really do target a declared entity.
|
|
53
|
+
*/
|
|
54
|
+
address: string;
|
|
55
|
+
/** The handler class. */
|
|
56
|
+
ctor: new (...args: unknown[]) => unknown;
|
|
57
|
+
/** All operations with full signatures for binding. */
|
|
58
|
+
operations: OperationsMap;
|
|
59
|
+
/** Constructor dependency type names (from AST scan). */
|
|
60
|
+
deps: string[];
|
|
61
|
+
/** Absolute file path (for debugging). */
|
|
62
|
+
filePath: string;
|
|
63
|
+
/** Whether this handler is part of the frond's public contract. */
|
|
64
|
+
exposed?: boolean;
|
|
65
|
+
/** Output schema override — when Crud(Entity, Output), restricts ORM output. */
|
|
66
|
+
outputOverride?: SchemaLike;
|
|
67
|
+
/** Surface name — subdirectory in handlers/ (e.g. 'admin', 'public'). */
|
|
68
|
+
surface?: string;
|
|
69
|
+
}
|
|
70
|
+
/** Return type metadata for a presenter computed field. */
|
|
71
|
+
export interface PresenterFieldMeta {
|
|
72
|
+
name: string;
|
|
73
|
+
/** Inferred return type name: 'string', 'number', 'boolean', or a class name. */
|
|
74
|
+
returnType?: string;
|
|
75
|
+
/**
|
|
76
|
+
* The field emits a LIST per row — `tags(posts: Post[]): string[][]`.
|
|
77
|
+
*
|
|
78
|
+
* The method answers one value per row, so the outer array level of its return type is
|
|
79
|
+
* the page, not the field: what remains after removing it is the field's own arity.
|
|
80
|
+
* Nothing measured that remainder, so a computed list and a computed scalar looked
|
|
81
|
+
* identical and every projection announced the scalar.
|
|
82
|
+
*/
|
|
83
|
+
list?: boolean;
|
|
84
|
+
/** Whether the return is nullable. */
|
|
85
|
+
nullable?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* The declared parameters AFTER the rows. Kept raw rather than resolved: the scan
|
|
88
|
+
* meets presenters before collectors, so the plan is computed at boot where the
|
|
89
|
+
* collector names are known — exactly as a handler op's is.
|
|
90
|
+
*
|
|
91
|
+
* A computed field that declares `user: User | null` is then fed by the collector
|
|
92
|
+
* that resolves one. A presenter is not a second mechanism.
|
|
93
|
+
*/
|
|
94
|
+
params?: ParsedParam[];
|
|
95
|
+
}
|
|
96
|
+
/** A discovered presenter (computed fields for an entity's output). */
|
|
97
|
+
export interface PresenterEntry {
|
|
98
|
+
/** Entity name this presenter enriches (e.g. 'post' from PostPresenter). */
|
|
99
|
+
entityName: string;
|
|
100
|
+
/** The presenter class. */
|
|
101
|
+
ctor: new (...args: unknown[]) => unknown;
|
|
102
|
+
/** Computed field names (method names on prototype). */
|
|
103
|
+
fields: string[];
|
|
104
|
+
/** Per-field type metadata (inferred from source via parser). */
|
|
105
|
+
fieldMeta: PresenterFieldMeta[];
|
|
106
|
+
/**
|
|
107
|
+
* The view each computed field emits, when the presenter declares one
|
|
108
|
+
* (`Presenter(Order, { items: [OrderItemView] })`). Stated rather than inferred: the
|
|
109
|
+
* parser reads a scalar off a return type and nothing more, so an object-valued field
|
|
110
|
+
* has no derivable shape without this.
|
|
111
|
+
*/
|
|
112
|
+
views?: PresenterViews;
|
|
113
|
+
/** Constructor dependency type names (from AST scan). */
|
|
114
|
+
deps: string[];
|
|
115
|
+
/** Absolute file path (for debugging). */
|
|
116
|
+
filePath: string;
|
|
117
|
+
}
|
|
118
|
+
/** A discovered collector (resolves handler input params from invocation context). */
|
|
119
|
+
export interface CollectorEntry {
|
|
120
|
+
/** Entity name this collector resolves (e.g. 'user' from UserCollector). */
|
|
121
|
+
entityName: string;
|
|
122
|
+
/** The collector class. */
|
|
123
|
+
ctor: new (...args: unknown[]) => unknown;
|
|
124
|
+
/** Constructor dependency type names (from AST scan). */
|
|
125
|
+
deps: string[];
|
|
126
|
+
/** Absolute file path (for debugging). */
|
|
127
|
+
filePath: string;
|
|
128
|
+
}
|
|
129
|
+
/** A discovered seed file (array of records or async factory). */
|
|
130
|
+
export interface SeedEntry {
|
|
131
|
+
/** Entity name this seed targets (from filename: Author.seed.ts → 'author'). */
|
|
132
|
+
entityName: string;
|
|
133
|
+
/** Raw default export — array or function. Resolved at runtime. */
|
|
134
|
+
data: Record<string, unknown>[] | SeedFactory;
|
|
135
|
+
/** Absolute file path (for debugging). */
|
|
136
|
+
filePath: string;
|
|
137
|
+
}
|
|
138
|
+
/** Seed factory — receives a resolver to access handlers cross-frond. */
|
|
139
|
+
export type SeedFactory = (resolve: <T>(name: string) => T) => Promise<Record<string, unknown>[]>;
|
|
140
|
+
/** Where a frond lives on disk. */
|
|
141
|
+
export interface FrondSource {
|
|
142
|
+
/** Absolute path to the frond directory. */
|
|
143
|
+
path: string;
|
|
144
|
+
/** The @frond/{name} package name. Always present. */
|
|
145
|
+
package: string;
|
|
146
|
+
}
|
|
147
|
+
export interface FrondDescriptor {
|
|
148
|
+
name: string;
|
|
149
|
+
source: FrondSource;
|
|
150
|
+
providers: ProviderEntry[];
|
|
151
|
+
entities: EntityEntry[];
|
|
152
|
+
handlers: HandlerEntry[];
|
|
153
|
+
presenters: PresenterEntry[];
|
|
154
|
+
collectors: CollectorEntry[];
|
|
155
|
+
seeds: SeedEntry[];
|
|
156
|
+
/** Per-surface entity lists from frond.config.ts (e.g. { graphql: ['Post'], rest: ['Post', 'Author'] }). */
|
|
157
|
+
surfaces?: Record<string, string[]>;
|
|
158
|
+
/**
|
|
159
|
+
* Per-operation overrides from frond.config.ts. Key = operation name.
|
|
160
|
+
*
|
|
161
|
+
* Two depths, as declared on {@link OperationOverride}: the surface keys travel to the
|
|
162
|
+
* transport adapters, the contract keys (`input`/`output`/`binding`) travel to the
|
|
163
|
+
* façade — where config is the third producer of an operation's contract, alongside a
|
|
164
|
+
* prefab's `__ops` and the scan.
|
|
165
|
+
*/
|
|
166
|
+
operationsOverrides?: Record<string, OperationContract & {
|
|
167
|
+
kind?: 'query' | 'command';
|
|
168
|
+
/** Class name to resolve from DI (overrides default `{Entity}Handler` lookup). */
|
|
169
|
+
handlerName?: string;
|
|
170
|
+
/** Method name on the resolved handler (defaults to op name). */
|
|
171
|
+
method?: string;
|
|
172
|
+
/** CASL ability string for opt-in policy check. */
|
|
173
|
+
policy?: string;
|
|
174
|
+
}>;
|
|
175
|
+
}
|
|
176
|
+
/** Result of scanning a project directory. */
|
|
177
|
+
/**
|
|
178
|
+
* Something the scan could NOT do — recorded instead of swallowed.
|
|
179
|
+
*
|
|
180
|
+
* The scan answers with what it found. Until now it answered the same way whether
|
|
181
|
+
* a directory held nothing or could not be read, and whether a handler declared no
|
|
182
|
+
* operation or failed to parse: `catch → empty`. So every downstream reader — the
|
|
183
|
+
* façade, the identity card, anything asking "what does this app serve?" — could
|
|
184
|
+
* not tell **"there is nothing"** from **"I could not look"**.
|
|
185
|
+
*
|
|
186
|
+
* That distinction is what makes a rule about an ABSENCE sound. Without it, a check
|
|
187
|
+
* derived from the scan reports "nothing wrong" precisely when it read nothing.
|
|
188
|
+
*/
|
|
189
|
+
export interface ScanDiagnostic {
|
|
190
|
+
/**
|
|
191
|
+
* `blocking` — the app now serves less than its source declares, and no caller
|
|
192
|
+
* can know it: a handler that failed to parse contributes zero operations.
|
|
193
|
+
* `warning` — something may be missing and the scan cannot decide, e.g. a base
|
|
194
|
+
* class it is not allowed to resolve. Statable in `frond.config.ts`.
|
|
195
|
+
*/
|
|
196
|
+
severity: 'blocking' | 'warning';
|
|
197
|
+
/** Stable rule name — `handler-parse-failed`, `directory-unreadable`. */
|
|
198
|
+
code: string;
|
|
199
|
+
/** Absolute path of what could not be read. */
|
|
200
|
+
filePath: string;
|
|
201
|
+
/** The frond it belongs to, when the scan got far enough to know. */
|
|
202
|
+
frond?: string;
|
|
203
|
+
/** What could not be done, and what it costs. One sentence, for a human. */
|
|
204
|
+
message: string;
|
|
205
|
+
/** The underlying failure, kept whole. */
|
|
206
|
+
cause?: unknown;
|
|
207
|
+
}
|
|
208
|
+
export interface ScanResult {
|
|
209
|
+
fronds: FrondDescriptor[];
|
|
210
|
+
/** What the scan could not do. Empty is a claim, not a default — see {@link ScanDiagnostic}. */
|
|
211
|
+
diagnostics: ScanDiagnostic[];
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Lazy auth declaration written in fougere.config.ts.
|
|
215
|
+
*
|
|
216
|
+
* Each @fougere/auth-* package exports a factory (e.g. `betterAuth(opts)`) that
|
|
217
|
+
* returns this shape. The `create()` method is called once at boot with the
|
|
218
|
+
* resolved db + ormFactory, so the provider can wire its engine through Fougere.
|
|
219
|
+
*/
|
|
220
|
+
export interface AuthConfig {
|
|
221
|
+
/** Build the runtime — invoked by createApp at boot with the resolved storage handles. */
|
|
222
|
+
create(ctx: AuthContext): AuthRuntime | Promise<AuthRuntime>;
|
|
223
|
+
/**
|
|
224
|
+
* Entities the provider will use. Optional metadata — useful for the core's
|
|
225
|
+
* future migration registry to know which tables auth needs.
|
|
226
|
+
*/
|
|
227
|
+
entities?: Record<string, SchemaLike>;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Context passed by the core to an auth provider's create() function.
|
|
231
|
+
* Provides the resources the provider needs to integrate with the app.
|
|
232
|
+
*/
|
|
233
|
+
export interface AuthContext {
|
|
234
|
+
/** Storage handle (Kysely DB instance, Prisma client, etc.) — opaque to core. */
|
|
235
|
+
db: unknown;
|
|
236
|
+
/** Per-entity ORM factory — auth provider uses this to back its adapter. */
|
|
237
|
+
ormFactory: OrmFactory;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Runtime returned by an auth provider's create(config, ctx) function.
|
|
241
|
+
* The core mounts this on the App and the HTTP layer (Nuxt module / router) uses it.
|
|
242
|
+
*/
|
|
243
|
+
export interface AuthRuntime {
|
|
244
|
+
/** Entities used by the provider, including any defaults it filled in. */
|
|
245
|
+
entities: Record<string, SchemaLike>;
|
|
246
|
+
/**
|
|
247
|
+
* Per-entity ORMs the provider built for itself. Exposed so app code can
|
|
248
|
+
* query auth tables (e.g. list active sessions for a user) without rebuilding
|
|
249
|
+
* the same EntityOrm.
|
|
250
|
+
*/
|
|
251
|
+
orms: Record<string, EntityOrm>;
|
|
252
|
+
/** Web Standard handler that processes /auth/* requests. */
|
|
253
|
+
handler: (request: Request) => Promise<Response>;
|
|
254
|
+
/** Programmatic API exposed by the provider (getSession, signOut, ...). */
|
|
255
|
+
api: Record<string, unknown>;
|
|
256
|
+
/** Effective mount path (echoes config.basePath or the provider's default). */
|
|
257
|
+
basePath: string;
|
|
258
|
+
}
|
|
259
|
+
/** Options for createApp(). */
|
|
260
|
+
export interface CreateAppOptions {
|
|
261
|
+
/** Project root directory. Defaults to process.cwd(). */
|
|
262
|
+
root?: string;
|
|
263
|
+
/** Factory function to create the container. Required. */
|
|
264
|
+
createContainer: () => Container;
|
|
265
|
+
/** Factory to auto-generate EntityOrm for each scanned entity. */
|
|
266
|
+
ormFactory?: OrmFactory;
|
|
267
|
+
/** Only load these fronds (by name). If absent, load all. */
|
|
268
|
+
fronds?: string[];
|
|
269
|
+
/**
|
|
270
|
+
* Remote fronds — label → address. What each remote hosts is discovered
|
|
271
|
+
* at the first miss (rpc.discover), never declared here.
|
|
272
|
+
*/
|
|
273
|
+
remotes?: Record<string, string>;
|
|
274
|
+
/**
|
|
275
|
+
* Builds the transport used to reach `remotes` addresses. Wired by
|
|
276
|
+
* layer-2 packages (e.g. @fougere/transport-http); required when
|
|
277
|
+
* `remotes` is non-empty.
|
|
278
|
+
*/
|
|
279
|
+
remoteTransport?: (url: string) => Transport;
|
|
280
|
+
/**
|
|
281
|
+
* Carries an announced fact out of this process — a broker, a queue, a log.
|
|
282
|
+
*
|
|
283
|
+
* Called for every emission, ALONGSIDE the local dispatch and never instead of it: a
|
|
284
|
+
* listener in this process is reached directly, and this is how the others hear.
|
|
285
|
+
*
|
|
286
|
+
* It exists because the local dispatch finds its listeners by having READ their code,
|
|
287
|
+
* which stops at the repository boundary. A carrier hands the fact to a NAME; the far
|
|
288
|
+
* side subscribes to that same name from its own code, and neither ever reads the other.
|
|
289
|
+
*
|
|
290
|
+
* Its failure never reaches the emitter — same rule as a subscriber's.
|
|
291
|
+
*/
|
|
292
|
+
onEmit?: (fact: string, payload: unknown) => void | Promise<void>;
|
|
293
|
+
/**
|
|
294
|
+
* Storage handle to expose to the auth provider via AuthContext.db.
|
|
295
|
+
* Required when `auth` is set.
|
|
296
|
+
*/
|
|
297
|
+
db?: unknown;
|
|
298
|
+
/** Auth declaration to wire into the app at boot. */
|
|
299
|
+
auth?: AuthConfig;
|
|
300
|
+
}
|
|
301
|
+
/** The App object returned by createApp(). */
|
|
302
|
+
export interface App {
|
|
303
|
+
/** Root container with builtins + frond scopes. */
|
|
304
|
+
container: Container;
|
|
305
|
+
/** Discovered fronds metadata. */
|
|
306
|
+
fronds: FrondDescriptor[];
|
|
307
|
+
/** Resolve from root container (shortcut). */
|
|
308
|
+
resolve<T>(name: string): T;
|
|
309
|
+
/**
|
|
310
|
+
* Resolve an entity's schema — the local `entityClass` when it's hosted or
|
|
311
|
+
* scanned here, else reconstructed from the remote's identity card
|
|
312
|
+
* (rpc.discover → reconstruct()). The one way to get a live schema for an
|
|
313
|
+
* entity whose class may not be importable at all (another repo, another
|
|
314
|
+
* language) — a card travelled, but until now its `schema` was thrown away.
|
|
315
|
+
*/
|
|
316
|
+
schemaFor(entity: string): Promise<SchemaLike>;
|
|
317
|
+
/**
|
|
318
|
+
* The door a name exposes to one audience, or `undefined` when it exposes
|
|
319
|
+
* none. A named surface is CLOSED: a name with no door of its own under that
|
|
320
|
+
* surface is not in it — it never falls back to the full one.
|
|
321
|
+
*
|
|
322
|
+
* **Not always a façade**, despite the name: when the owning frond is declared
|
|
323
|
+
* remote, nothing is registered locally (`bootstrap.ts`, `declaredRemotes`) and
|
|
324
|
+
* this resolves to a doublure — a façade-shaped stand-in (`remote.ts`). The
|
|
325
|
+
* caller cannot tell, which is the point; the word here was simply wrong.
|
|
326
|
+
*
|
|
327
|
+
* The one way an adapter reaches that door. It exists so the key format stays
|
|
328
|
+
* inside core (see `facadeKeyOf`), which is what lets a projection package
|
|
329
|
+
* stay structurally typed and depend on nothing.
|
|
330
|
+
*/
|
|
331
|
+
facadeFor(entity: string, surface?: string): Record<string, Function> | undefined;
|
|
332
|
+
/**
|
|
333
|
+
* The facts this app has a listener for — what a carrier must subscribe to on its behalf.
|
|
334
|
+
*
|
|
335
|
+
* Derived from the signatures it scanned (`Fact<T>` in an op's parameters), so a process
|
|
336
|
+
* states what it listens to without anyone declaring it twice. The dual of `onEmit`: one
|
|
337
|
+
* says what leaves, this says what should be brought in.
|
|
338
|
+
*/
|
|
339
|
+
listensTo(): string[];
|
|
340
|
+
/**
|
|
341
|
+
* Hand a fact that came from OUTSIDE to the listeners in this process — and stop there.
|
|
342
|
+
*
|
|
343
|
+
* The dual of `onEmit`, and deliberately not the same thing as announcing: resolving the
|
|
344
|
+
* emission value to deliver an inbound fact would carry it straight back out through
|
|
345
|
+
* `onEmit`, so a hub echoed every reading it received to the whole fleet. Receiving and
|
|
346
|
+
* announcing are two operations; only one of them leaves.
|
|
347
|
+
*
|
|
348
|
+
* The local dispatch is identical either way — same judge, same binding, same
|
|
349
|
+
* middlewares — so a fact off a wire is no less checked than one raised next door.
|
|
350
|
+
*
|
|
351
|
+
* **It waits, and it tells** — the opposite of announcing, on purpose. It resolves once
|
|
352
|
+
* every listener here is done, and REJECTS with an `AggregateError` if any refused. A
|
|
353
|
+
* carrier's whole job is to know whether the fact landed: at-least-once is retrying what
|
|
354
|
+
* failed, so a delivery that cannot report makes durability impossible to build above it.
|
|
355
|
+
* "Dispatch is not delivery" protects the EMITTER from a slow subscriber; a carrier is
|
|
356
|
+
* not the emitter, it is the party whose business this is.
|
|
357
|
+
*
|
|
358
|
+
* It still holds nothing. A refused fact is refused, and whether it comes back is the
|
|
359
|
+
* carrier's decision — Fougere puts the channel underneath rather than reimplementing it.
|
|
360
|
+
*/
|
|
361
|
+
deliver(fact: string, payload: unknown): Promise<void>;
|
|
362
|
+
/**
|
|
363
|
+
* The storage an entity is backed by, resolved through its owning frond's scope —
|
|
364
|
+
* the dual of {@link facadeFor}. `undefined` when no loaded frond hosts the entity,
|
|
365
|
+
* or when the app booted with no storage at all.
|
|
366
|
+
*
|
|
367
|
+
* `unknown` because the port belongs to whoever wired it: narrowing it to `EntityOrm`
|
|
368
|
+
* is the caller saying which implementation they are standing on.
|
|
369
|
+
*/
|
|
370
|
+
ormFor(entity: string): unknown | undefined;
|
|
371
|
+
/** Dispose the root container. */
|
|
372
|
+
dispose(): Promise<void>;
|
|
373
|
+
/**
|
|
374
|
+
* The same disposal, spelled so the language does it: `await using app = await
|
|
375
|
+
* createApp(…)`. Twelve of the twenty-six mounts in this repo's own tests never
|
|
376
|
+
* called `dispose()` — a scope that closes itself is the only version of that
|
|
377
|
+
* rule nobody forgets.
|
|
378
|
+
*/
|
|
379
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
380
|
+
/** Register a global app middleware (runs on every operation). */
|
|
381
|
+
use(middleware: AppMiddleware): void;
|
|
382
|
+
/** Register an app middleware scoped to a specific entity. */
|
|
383
|
+
use(entity: string, middleware: AppMiddleware): void;
|
|
384
|
+
/** Auth runtime, present when fougere.config.ts declares `auth`. */
|
|
385
|
+
auth?: AuthRuntime;
|
|
386
|
+
}
|
|
387
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { App } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* What a rule found in an app.
|
|
4
|
+
*
|
|
5
|
+
* `message` is the sentence the rule replaces — the prose a human used to have
|
|
6
|
+
* to remember. `Known issues` says "Keep collectors in the consuming frond";
|
|
7
|
+
* this is that sentence, told about a specific handler, by something that runs.
|
|
8
|
+
*/
|
|
9
|
+
export interface Violation {
|
|
10
|
+
/** Rule name, stable — 'cross-frond-dependency'. */
|
|
11
|
+
rule: string;
|
|
12
|
+
/** The frond the subject lives in. */
|
|
13
|
+
frond: string;
|
|
14
|
+
/** What violates it — 'PostHandler'. */
|
|
15
|
+
subject: string;
|
|
16
|
+
/** Where to go and look. */
|
|
17
|
+
filePath: string;
|
|
18
|
+
/**
|
|
19
|
+
* What the subject reaches for, and where it actually lives.
|
|
20
|
+
*
|
|
21
|
+
* `frond` above is the CONSUMER; the target used to exist only inside the
|
|
22
|
+
* sentence. A reader that has to parse prose to learn which frond a violation
|
|
23
|
+
* points at is a second opinion on a fact the checker already held — so the
|
|
24
|
+
* boot can ask "does this violation target a frond named in `remotes:`?" by
|
|
25
|
+
* reading a field.
|
|
26
|
+
*/
|
|
27
|
+
dependsOn: {
|
|
28
|
+
key: string;
|
|
29
|
+
frond: string;
|
|
30
|
+
kind: string;
|
|
31
|
+
};
|
|
32
|
+
/** What breaks, and what the caller gets instead. */
|
|
33
|
+
message: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Does this app survive being split?
|
|
37
|
+
*
|
|
38
|
+
* A frond runs in-process or in its own process behind JSON-RPC with identical
|
|
39
|
+
* user code — that is the whole claim. What the claim does not say is that
|
|
40
|
+
* *every* app survives the move: a dependency that crosses a frond boundary
|
|
41
|
+
* resolves today because both scopes live in one container, and resolves to
|
|
42
|
+
* nothing the day the other frond answers over the wire.
|
|
43
|
+
*
|
|
44
|
+
* The gradient is the one question no other framework can ask. Nest has nothing
|
|
45
|
+
* to check — every inter-module call is already remote, so nothing changes when
|
|
46
|
+
* you split. Spring Modulith has nothing to check either — it plans no exit.
|
|
47
|
+
* Here the question means something, and until now it had no answer.
|
|
48
|
+
*
|
|
49
|
+
* Pure over `app.fronds`: no mount, no process, no file. Call it from a test.
|
|
50
|
+
*/
|
|
51
|
+
export declare function verify(app: Pick<App, 'fronds'>): Violation[];
|
|
52
|
+
/**
|
|
53
|
+
* The same verdict, as an assertion. Throws naming every rule that failed, so a
|
|
54
|
+
* test is one line and its output is the sentence — not a diff of two objects.
|
|
55
|
+
*/
|
|
56
|
+
export declare function assertSplittable(app: Pick<App, 'fronds'>): void;
|
|
57
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAmB,MAAM,YAAY,CAAC;AAMvD;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;;OAQG;IACH,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;CACjB;AAwCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,SAAS,EAAE,CA+E5D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,IAAI,CAK/D"}
|