@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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -0
  3. package/dist/binding.d.ts +63 -0
  4. package/dist/binding.d.ts.map +1 -0
  5. package/dist/binding.js +107 -0
  6. package/dist/binding.js.map +1 -0
  7. package/dist/boot.d.ts +38 -0
  8. package/dist/boot.d.ts.map +1 -0
  9. package/dist/boot.js +53 -0
  10. package/dist/boot.js.map +1 -0
  11. package/dist/bootstrap.d.ts +4 -0
  12. package/dist/bootstrap.d.ts.map +1 -0
  13. package/dist/bootstrap.js +825 -0
  14. package/dist/bootstrap.js.map +1 -0
  15. package/dist/builtins/config.d.ts +4 -0
  16. package/dist/builtins/config.d.ts.map +1 -0
  17. package/dist/builtins/config.js +6 -0
  18. package/dist/builtins/config.js.map +1 -0
  19. package/dist/builtins/logger.d.ts +28 -0
  20. package/dist/builtins/logger.d.ts.map +1 -0
  21. package/dist/builtins/logger.js +76 -0
  22. package/dist/builtins/logger.js.map +1 -0
  23. package/dist/call.d.ts +172 -0
  24. package/dist/call.d.ts.map +1 -0
  25. package/dist/call.js +209 -0
  26. package/dist/call.js.map +1 -0
  27. package/dist/collector.d.ts +28 -0
  28. package/dist/collector.d.ts.map +1 -0
  29. package/dist/collector.js +32 -0
  30. package/dist/collector.js.map +1 -0
  31. package/dist/config-loader.d.ts +59 -0
  32. package/dist/config-loader.d.ts.map +1 -0
  33. package/dist/config-loader.js +97 -0
  34. package/dist/config-loader.js.map +1 -0
  35. package/dist/contract.d.ts +43 -0
  36. package/dist/contract.d.ts.map +1 -0
  37. package/dist/contract.js +29 -0
  38. package/dist/contract.js.map +1 -0
  39. package/dist/crud.d.ts +61 -0
  40. package/dist/crud.d.ts.map +1 -0
  41. package/dist/crud.js +85 -0
  42. package/dist/crud.js.map +1 -0
  43. package/dist/define.d.ts +8 -0
  44. package/dist/define.d.ts.map +1 -0
  45. package/dist/define.js +9 -0
  46. package/dist/define.js.map +1 -0
  47. package/dist/egress.d.ts +78 -0
  48. package/dist/egress.d.ts.map +1 -0
  49. package/dist/egress.js +185 -0
  50. package/dist/egress.js.map +1 -0
  51. package/dist/emit.d.ts +51 -0
  52. package/dist/emit.d.ts.map +1 -0
  53. package/dist/emit.js +57 -0
  54. package/dist/emit.js.map +1 -0
  55. package/dist/errors.d.ts +22 -0
  56. package/dist/errors.d.ts.map +1 -0
  57. package/dist/errors.js +74 -0
  58. package/dist/errors.js.map +1 -0
  59. package/dist/frond-config.d.ts +80 -0
  60. package/dist/frond-config.d.ts.map +1 -0
  61. package/dist/frond-config.js +32 -0
  62. package/dist/frond-config.js.map +1 -0
  63. package/dist/graph.d.ts +33 -0
  64. package/dist/graph.d.ts.map +1 -0
  65. package/dist/graph.js +142 -0
  66. package/dist/graph.js.map +1 -0
  67. package/dist/handler-parser.d.ts +82 -0
  68. package/dist/handler-parser.d.ts.map +1 -0
  69. package/dist/handler-parser.js +505 -0
  70. package/dist/handler-parser.js.map +1 -0
  71. package/dist/imports.d.ts +22 -0
  72. package/dist/imports.d.ts.map +1 -0
  73. package/dist/imports.js +80 -0
  74. package/dist/imports.js.map +1 -0
  75. package/dist/index.d.ts +43 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +37 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/invocation.d.ts +20 -0
  80. package/dist/invocation.d.ts.map +1 -0
  81. package/dist/invocation.js +15 -0
  82. package/dist/invocation.js.map +1 -0
  83. package/dist/middleware/error-handler.d.ts +15 -0
  84. package/dist/middleware/error-handler.d.ts.map +1 -0
  85. package/dist/middleware/error-handler.js +29 -0
  86. package/dist/middleware/error-handler.js.map +1 -0
  87. package/dist/middleware/logger.d.ts +14 -0
  88. package/dist/middleware/logger.d.ts.map +1 -0
  89. package/dist/middleware/logger.js +25 -0
  90. package/dist/middleware/logger.js.map +1 -0
  91. package/dist/middleware.d.ts +75 -0
  92. package/dist/middleware.d.ts.map +1 -0
  93. package/dist/middleware.js +89 -0
  94. package/dist/middleware.js.map +1 -0
  95. package/dist/operation.d.ts +87 -0
  96. package/dist/operation.d.ts.map +1 -0
  97. package/dist/operation.js +95 -0
  98. package/dist/operation.js.map +1 -0
  99. package/dist/orm.d.ts +96 -0
  100. package/dist/orm.d.ts.map +1 -0
  101. package/dist/orm.js +33 -0
  102. package/dist/orm.js.map +1 -0
  103. package/dist/presenter.d.ts +63 -0
  104. package/dist/presenter.d.ts.map +1 -0
  105. package/dist/presenter.js +72 -0
  106. package/dist/presenter.js.map +1 -0
  107. package/dist/remote.d.ts +48 -0
  108. package/dist/remote.d.ts.map +1 -0
  109. package/dist/remote.js +135 -0
  110. package/dist/remote.js.map +1 -0
  111. package/dist/repository.d.ts +55 -0
  112. package/dist/repository.d.ts.map +1 -0
  113. package/dist/repository.js +26 -0
  114. package/dist/repository.js.map +1 -0
  115. package/dist/scan-cache.d.ts +10 -0
  116. package/dist/scan-cache.d.ts.map +1 -0
  117. package/dist/scan-cache.js +92 -0
  118. package/dist/scan-cache.js.map +1 -0
  119. package/dist/scanner.d.ts +34 -0
  120. package/dist/scanner.d.ts.map +1 -0
  121. package/dist/scanner.js +595 -0
  122. package/dist/scanner.js.map +1 -0
  123. package/dist/seed.d.ts +28 -0
  124. package/dist/seed.d.ts.map +1 -0
  125. package/dist/seed.js +110 -0
  126. package/dist/seed.js.map +1 -0
  127. package/dist/types.d.ts +387 -0
  128. package/dist/types.d.ts.map +1 -0
  129. package/dist/types.js +2 -0
  130. package/dist/types.js.map +1 -0
  131. package/dist/verify.d.ts +57 -0
  132. package/dist/verify.d.ts.map +1 -0
  133. package/dist/verify.js +147 -0
  134. package/dist/verify.js.map +1 -0
  135. package/package.json +47 -0
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Collector(Entity) — resolves handler input parameters from invocation context.
3
+ *
4
+ * The dual of Presenter: Presenter enriches output, Collector resolves input.
5
+ *
6
+ * Usage:
7
+ * ```ts
8
+ * export default class UserCollector extends Collector(User) {
9
+ * constructor(private userOrm: UserOrm) { super(); }
10
+ *
11
+ * async collect(ctx: InvocationContext) {
12
+ * return this.userOrm.findById(ctx.state.userId as string);
13
+ * }
14
+ * }
15
+ * ```
16
+ */
17
+ const COLLECTOR_TARGET = Symbol.for('fougere:collector_target');
18
+ export function Collector(entity) {
19
+ class CollectorBase {
20
+ static [COLLECTOR_TARGET] = entity;
21
+ }
22
+ return CollectorBase;
23
+ }
24
+ /** Get the entity class a collector targets. */
25
+ export function getCollectorTarget(ctor) {
26
+ return ctor[COLLECTOR_TARGET];
27
+ }
28
+ /** Container key of an entity's collector — 'user' → 'UserCollector'. */
29
+ export function collectorKeyOf(entity) {
30
+ return `${entity[0].toUpperCase()}${entity.slice(1)}Collector`;
31
+ }
32
+ //# sourceMappingURL=collector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector.js","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,MAAM,gBAAgB,GAAG,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;AAEhE,MAAM,UAAU,SAAS,CAAwB,MAAS;IACxD,MAAM,aAAa;QACjB,MAAM,CAAC,CAAC,gBAAgB,CAAC,GAAG,MAAM,CAAC;;IAErC,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,kBAAkB,CAAC,IAAc;IAC/C,OAAQ,IAAY,CAAC,gBAAgB,CAAC,CAAC;AACzC,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC;AACjE,CAAC"}
@@ -0,0 +1,59 @@
1
+ import type { AuthConfig } from './types.js';
2
+ export interface FougereConfig {
3
+ /** Database configuration. */
4
+ db?: 'sqlite' | {
5
+ dialect: 'sqlite';
6
+ path?: string;
7
+ } | false;
8
+ /** Directory containing fronds. Defaults to 'fronds'. */
9
+ frondsDir?: string;
10
+ /** Remote fronds — frondName → base URL. */
11
+ remotes?: Record<string, string>;
12
+ /** Auth declaration — picks a provider package and forwards options to it. */
13
+ auth?: AuthConfig;
14
+ }
15
+ export interface ResolvedConfig {
16
+ /** Global config merged with all overrides. */
17
+ global: FougereConfig;
18
+ /** Per-frond config overrides. */
19
+ fronds: Record<string, FougereConfig>;
20
+ }
21
+ /**
22
+ * Load the root fougere.config.{ts,js,mjs} from the given directory.
23
+ */
24
+ export declare function loadConfig(root: string): Promise<FougereConfig>;
25
+ /**
26
+ * Load per-frond config files from each frond's directory.
27
+ */
28
+ export declare function loadFrondConfigs(root: string, frondsDir?: string): Promise<Record<string, FougereConfig>>;
29
+ /**
30
+ * Override a config with another, the invariant of every cascade level:
31
+ * scalar keys replace, but `remotes` (the topology) MERGES — an override adds
32
+ * or redirects a frond without erasing the others. Used for workspace→app and
33
+ * for global→CLI alike.
34
+ */
35
+ export declare function mergeGlobal(base: FougereConfig, override: Partial<FougereConfig>): FougereConfig;
36
+ /**
37
+ * Merge configs with cascade: global → frond → CLI overrides.
38
+ *
39
+ * Returns a ResolvedConfig with the merged global and per-frond overrides.
40
+ */
41
+ export declare function mergeConfig(global: FougereConfig, frondConfigs: Record<string, FougereConfig>, cliOverrides?: Partial<FougereConfig>): ResolvedConfig;
42
+ /**
43
+ * Get the effective config for a specific frond.
44
+ * Cascade: global → frond override.
45
+ */
46
+ export declare function configForFrond(resolved: ResolvedConfig, frondName: string): FougereConfig;
47
+ /**
48
+ * Load config along the workspace→app frontier. The workspace-root config is
49
+ * the base (canonical topology: `remotes`, shared `db`); the app-root config
50
+ * overrides. Same `root` boundary the fronds already cascade along. When both
51
+ * roots resolve to the same dir (single app, no workspace), this is the plain
52
+ * root config — idempotent, no behavior change.
53
+ */
54
+ export declare function loadCascadedConfig(workspaceRoot: string, appRoot: string): Promise<FougereConfig>;
55
+ /**
56
+ * Load everything: root config + frond configs + merge with CLI overrides.
57
+ */
58
+ export declare function resolveConfig(root: string, cliOverrides?: Partial<FougereConfig>): Promise<ResolvedConfig>;
59
+ //# sourceMappingURL=config-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../src/config-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK7C,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,EAAE,CAAC,EAAE,QAAQ,GAAG;QAAE,OAAO,EAAE,QAAQ,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC;IAC7D,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,8EAA8E;IAC9E,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,MAAM,EAAE,aAAa,CAAC;IACtB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACvC;AAkBD;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAErE;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAW,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAgBjH;AAID;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAMhG;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EAC3C,YAAY,GAAE,OAAO,CAAC,aAAa,CAAM,GACxC,cAAc,CAEhB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,aAAa,CAIzF;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAIvG;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,YAAY,GAAE,OAAO,CAAC,aAAa,CAAM,GACxC,OAAO,CAAC,cAAc,CAAC,CAKzB"}
@@ -0,0 +1,97 @@
1
+ import { resolve, join } from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import { readdir } from 'node:fs/promises';
4
+ import { getModuleLoader } from './scanner.js';
5
+ // ── Loading ──────────────────────────────────────
6
+ const CONFIG_FILES = ['fougere.config.ts', 'fougere.config.js', 'fougere.config.mjs'];
7
+ async function loadConfigFrom(dir) {
8
+ const loader = getModuleLoader();
9
+ for (const file of CONFIG_FILES) {
10
+ const path = resolve(dir, file);
11
+ if (existsSync(path)) {
12
+ const mod = await loader(path);
13
+ return (mod.default ?? mod);
14
+ }
15
+ }
16
+ return {};
17
+ }
18
+ /**
19
+ * Load the root fougere.config.{ts,js,mjs} from the given directory.
20
+ */
21
+ export async function loadConfig(root) {
22
+ return loadConfigFrom(root);
23
+ }
24
+ /**
25
+ * Load per-frond config files from each frond's directory.
26
+ */
27
+ export async function loadFrondConfigs(root, frondsDir = 'fronds') {
28
+ const dir = join(root, frondsDir);
29
+ if (!existsSync(dir))
30
+ return {};
31
+ const entries = await readdir(dir, { withFileTypes: true });
32
+ const configs = {};
33
+ for (const entry of entries) {
34
+ if (!entry.isDirectory())
35
+ continue;
36
+ const frondConfig = await loadConfigFrom(join(dir, entry.name));
37
+ if (Object.keys(frondConfig).length > 0) {
38
+ configs[entry.name] = frondConfig;
39
+ }
40
+ }
41
+ return configs;
42
+ }
43
+ // ── Merging ──────────────────────────────────────
44
+ /**
45
+ * Override a config with another, the invariant of every cascade level:
46
+ * scalar keys replace, but `remotes` (the topology) MERGES — an override adds
47
+ * or redirects a frond without erasing the others. Used for workspace→app and
48
+ * for global→CLI alike.
49
+ */
50
+ export function mergeGlobal(base, override) {
51
+ const merged = { ...base, ...override };
52
+ if (base.remotes || override.remotes) {
53
+ merged.remotes = { ...base.remotes, ...override.remotes };
54
+ }
55
+ return merged;
56
+ }
57
+ /**
58
+ * Merge configs with cascade: global → frond → CLI overrides.
59
+ *
60
+ * Returns a ResolvedConfig with the merged global and per-frond overrides.
61
+ */
62
+ export function mergeConfig(global, frondConfigs, cliOverrides = {}) {
63
+ return { global: mergeGlobal(global, cliOverrides), fronds: frondConfigs };
64
+ }
65
+ /**
66
+ * Get the effective config for a specific frond.
67
+ * Cascade: global → frond override.
68
+ */
69
+ export function configForFrond(resolved, frondName) {
70
+ const frondOverride = resolved.fronds[frondName];
71
+ if (!frondOverride)
72
+ return resolved.global;
73
+ return { ...resolved.global, ...frondOverride };
74
+ }
75
+ /**
76
+ * Load config along the workspace→app frontier. The workspace-root config is
77
+ * the base (canonical topology: `remotes`, shared `db`); the app-root config
78
+ * overrides. Same `root` boundary the fronds already cascade along. When both
79
+ * roots resolve to the same dir (single app, no workspace), this is the plain
80
+ * root config — idempotent, no behavior change.
81
+ */
82
+ export async function loadCascadedConfig(workspaceRoot, appRoot) {
83
+ const base = await loadConfig(workspaceRoot);
84
+ if (resolve(workspaceRoot) === resolve(appRoot))
85
+ return base;
86
+ return mergeGlobal(base, await loadConfig(appRoot));
87
+ }
88
+ /**
89
+ * Load everything: root config + frond configs + merge with CLI overrides.
90
+ */
91
+ export async function resolveConfig(root, cliOverrides = {}) {
92
+ const global = await loadConfig(root);
93
+ const frondsDir = cliOverrides.frondsDir ?? global.frondsDir ?? 'fronds';
94
+ const frondConfigs = await loadFrondConfigs(root, frondsDir);
95
+ return mergeConfig(global, frondConfigs, cliOverrides);
96
+ }
97
+ //# sourceMappingURL=config-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../src/config-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Contract surface — what a sender needs to speak to a receiver, and
3
+ * nothing that assumes a runtime: the error vocabulary, the invocation
4
+ * shape, the call value, the naming convention.
5
+ *
6
+ * Browser-safe by design: no node builtin may enter this module's import
7
+ * graph. Published as the `@fougere/core/contract` subpath so client
8
+ * bundles never touch the full index (scanner, config loader).
9
+ */
10
+ import { type InvocationContext } from './invocation.js';
11
+ import type { FrondCall } from './call.js';
12
+ export { FougereError, ErrorCode } from './middleware.js';
13
+ export type { FougereErrorOptions } from './middleware.js';
14
+ export { EMPTY_INVOCATION } from './invocation.js';
15
+ export type { InvocationContext } from './invocation.js';
16
+ export type { FrondCall, Transport } from './call.js';
17
+ /**
18
+ * What `rpc.discover` answers. It belongs here and not to the runtime: the
19
+ * reserved op travels on the same wire as every other call, so a consumer that
20
+ * only sends — a browser bundle, a frond written elsewhere — needs its shape and
21
+ * nothing else. Type-only, so `call.js` never enters the runtime graph.
22
+ *
23
+ * Stated once, on purpose: two private copies of this interface have already gone
24
+ * stale (the CLI's, then the Rust demo's) the day an op stopped being a bare name.
25
+ */
26
+ export type { IdentityCard, CardOp } from './call.js';
27
+ /** Registration key of a class — 'Post' → 'post', 'PostHandler' → 'postHandler'. */
28
+ export declare function toRegistrationName(name: string): string;
29
+ /** A call, fully fabricated: the designation and its completed invocation. */
30
+ export interface CallValue {
31
+ call: FrondCall;
32
+ invocation: InvocationContext;
33
+ }
34
+ /**
35
+ * Fabricate the call value — one designation, two spellings:
36
+ * `callValueOf(Post, 'list', { query })` (class + verb) or
37
+ * `callValueOf({ entity, op }, input)` (raw, for dynamic bridges).
38
+ * Missing invocation fields complete to the empty invocation.
39
+ */
40
+ export declare function callValueOf(target: {
41
+ name: string;
42
+ } | FrondCall, opOrInput?: string | Partial<InvocationContext>, input?: Partial<InvocationContext>): CallValue;
43
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD;;;;;;;;GAQG;AACH,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEtD,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,EAAE,iBAAiB,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,EACpC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,EAC/C,KAAK,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GACjC,SAAS,CAMX"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Contract surface — what a sender needs to speak to a receiver, and
3
+ * nothing that assumes a runtime: the error vocabulary, the invocation
4
+ * shape, the call value, the naming convention.
5
+ *
6
+ * Browser-safe by design: no node builtin may enter this module's import
7
+ * graph. Published as the `@fougere/core/contract` subpath so client
8
+ * bundles never touch the full index (scanner, config loader).
9
+ */
10
+ import { EMPTY_INVOCATION } from './invocation.js';
11
+ export { FougereError, ErrorCode } from './middleware.js';
12
+ export { EMPTY_INVOCATION } from './invocation.js';
13
+ /** Registration key of a class — 'Post' → 'post', 'PostHandler' → 'postHandler'. */
14
+ export function toRegistrationName(name) {
15
+ return name[0].toLowerCase() + name.slice(1);
16
+ }
17
+ /**
18
+ * Fabricate the call value — one designation, two spellings:
19
+ * `callValueOf(Post, 'list', { query })` (class + verb) or
20
+ * `callValueOf({ entity, op }, input)` (raw, for dynamic bridges).
21
+ * Missing invocation fields complete to the empty invocation.
22
+ */
23
+ export function callValueOf(target, opOrInput, input) {
24
+ const [call, given] = typeof opOrInput === 'string'
25
+ ? [{ entity: toRegistrationName(target.name), op: opOrInput }, input]
26
+ : [target, opOrInput];
27
+ return { call, invocation: { ...EMPTY_INVOCATION, ...given } };
28
+ }
29
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,gBAAgB,EAA0B,MAAM,iBAAiB,CAAC;AAG3E,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAcnD,oFAAoF;AACpF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,MAAoC,EACpC,SAA+C,EAC/C,KAAkC;IAElC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GACjB,OAAO,SAAS,KAAK,QAAQ;QAC3B,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,kBAAkB,CAAE,MAA2B,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,KAAK,CAAC;QAC3F,CAAC,CAAC,CAAC,MAAmB,EAAE,SAAS,CAAC,CAAC;IACvC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,GAAG,gBAAgB,EAAE,GAAG,KAAK,EAAE,EAAE,CAAC;AACjE,CAAC"}
package/dist/crud.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ import type { EntityOrm, ListOptions, ListResult } from './orm.js';
2
+ import type { OperationContract } from './operation.js';
3
+ /** Anything the derivation algebra produces — an entity class or one of its views. */
4
+ type SchemaCtor = abstract new (...args: any[]) => any;
5
+ /** The five ops the mixin fabricates. */
6
+ export type CrudOpName = 'list' | 'findById' | 'create' | 'update' | 'delete';
7
+ /** Which view each op speaks — omitted ops speak the entity, the trivial view. */
8
+ export type CrudViews = Partial<Record<CrudOpName, SchemaCtor>>;
9
+ /**
10
+ * The view an op emits, fabricated: the one declared for it, the single view when the
11
+ * whole handler declares one, the entity otherwise. `PostCard` is not a hand-written
12
+ * type — `Post.pick(...)` derives it field by field, so an op's return is a projection
13
+ * of the entity exactly like the entity is the projection that keeps everything.
14
+ */
15
+ type OutOf<V, K extends CrudOpName, T> = [
16
+ V
17
+ ] extends [SchemaCtor] ? InstanceType<V & SchemaCtor> : K extends keyof V ? (V[K] extends SchemaCtor ? InstanceType<V[K]> : T) : T;
18
+ /**
19
+ * The five ops, typed from the entity and its views.
20
+ *
21
+ * Two things the mixin declares but does not own. The **output** is the view the
22
+ * handler names (`Crud(Post, { list: PostCard })`) — fabricated, so a redefinition
23
+ * that returns cards stays assignable. The **trailing parameters** are resolved by
24
+ * type from the container (`delete(id, user: User | null)` gets its user from a
25
+ * collector): the mixin cannot know them, they belong to the app, so it declares that
26
+ * a tail exists and that it supplies none — which is what `never` says, and what keeps
27
+ * a judged redefinition assignable.
28
+ */
29
+ export interface CrudOps<T, V = {}> {
30
+ orm: EntityOrm<T>;
31
+ list(options?: ListOptions, ...collected: never[]): Promise<ListResult<OutOf<V, 'list', T>>>;
32
+ findById(id: string, ...collected: never[]): Promise<OutOf<V, 'findById', T> | undefined>;
33
+ create(input: Partial<T>, ...collected: never[]): Promise<OutOf<V, 'create', T>>;
34
+ update(id: string, input: Partial<T>, ...collected: never[]): Promise<OutOf<V, 'update', T>>;
35
+ delete(id: string, ...collected: never[]): Promise<boolean>;
36
+ }
37
+ /** The prefab handler class — its ops, plus the statics the bootstrap and adapters read. */
38
+ export interface CrudConstructor<T, V = {}> {
39
+ new (orm: EntityOrm<T>): CrudOps<T, V>;
40
+ readonly __entity: unknown;
41
+ readonly __output: unknown;
42
+ readonly __opOutputs?: CrudViews;
43
+ readonly __ops: Record<string, OperationContract>;
44
+ }
45
+ /**
46
+ * Mixin — extends Crud(Entity) to get all 5 typed CRUD methods.
47
+ *
48
+ * The second argument (optional) names the view the ops emit, and comes in two
49
+ * spellings of one idea — a view per op, or one view for all five:
50
+ *
51
+ * Crud(Post) → every op emits Post
52
+ * Crud(Post, { list: PostCard }) → list emits cards, the rest emit Post. Declaration
53
+ * only: the handler keeps its full-row ORM, so a
54
+ * judge can still read `body`.
55
+ * Crud(Post, PostPublic) → every op emits PostPublic, and the bootstrap
56
+ * scopes the injected ORM via .output(PostPublic) —
57
+ * the whole handler speaks the restricted view.
58
+ */
59
+ export declare function Crud<E extends abstract new (...args: any[]) => any, V extends CrudViews | SchemaCtor = {}>(entity: E, output?: V): CrudConstructor<InstanceType<E>, V>;
60
+ export {};
61
+ //# sourceMappingURL=crud.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../src/crud.ts"],"names":[],"mappings":"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"}
package/dist/crud.js ADDED
@@ -0,0 +1,85 @@
1
+ /** The id of the row an op acts on — a route segment, or a query fallback. */
2
+ const byId = { name: 'id', source: { kind: 'param', name: 'id' }, optional: false };
3
+ const fromBody = { name: 'input', source: { kind: 'body' }, optional: false };
4
+ /**
5
+ * The five ops a Crud handler brings, declared rather than discovered.
6
+ *
7
+ * The mixin built them, so it alone knows their contract in full: what judges
8
+ * their input, where each argument comes from. It says so on the class, at
9
+ * runtime — which is what makes the guarantee independent of the AST scan (an
10
+ * installed app cannot resolve this file, and never needs to).
11
+ */
12
+ /**
13
+ * `output` says the entity, and saying it costs nothing at runtime.
14
+ *
15
+ * The façade already projected onto the entity when nothing else was named, so this
16
+ * changes no result: `outputFieldsFor` reads `contractOutput` and falls back to the
17
+ * entity, and both are the same shape here. What changes is that the sentence now
18
+ * EXISTS — the identity card publishes `output` per op, and a card was measured
19
+ * carrying none at all (2026-08-06, ten ops, zero outputs), which typed every remote
20
+ * return as `unknown` for anyone building on it.
21
+ *
22
+ * It does not close the view: only an explicit `__opOutputs` does (`closed: perOp !==
23
+ * undefined`), so a named view still wins and a presenter's computed fields still ride
24
+ * out. `delete` names none — a boolean is not a shape.
25
+ */
26
+ function crudOps(entity) {
27
+ return {
28
+ list: { output: entity, cardinality: 'page', binding: [{ name: 'options', source: { kind: 'query' }, optional: true }] },
29
+ findById: { output: entity, cardinality: 'maybe', binding: [byId] },
30
+ create: { input: entity, output: entity, cardinality: 'one', binding: [fromBody] },
31
+ // The patch view carries its own mode: an absent field is untouched, an
32
+ // immutable one re-supplied is refused.
33
+ update: { input: entity.partial?.(), output: entity, cardinality: 'one', binding: [byId, fromBody] },
34
+ delete: { cardinality: 'none', binding: [byId] },
35
+ };
36
+ }
37
+ /**
38
+ * The mixin's single "trust me" point — the twin of `asSchemaConstructor` in @fougere/schema.
39
+ *
40
+ * The implementation returns whatever the ORM hands back; the declaration names the view
41
+ * each op emits at the port. TypeScript cannot connect the two (the view is a runtime
42
+ * argument, the type is a generic), so one assertion states that the mixin honours what it
43
+ * declared — and the façade makes it true, projecting each op's result onto its view
44
+ * (`outputFieldsFor` in `bootstrap.ts`).
45
+ */
46
+ function asCrudConstructor(impl) {
47
+ return impl;
48
+ }
49
+ /**
50
+ * Mixin — extends Crud(Entity) to get all 5 typed CRUD methods.
51
+ *
52
+ * The second argument (optional) names the view the ops emit, and comes in two
53
+ * spellings of one idea — a view per op, or one view for all five:
54
+ *
55
+ * Crud(Post) → every op emits Post
56
+ * Crud(Post, { list: PostCard }) → list emits cards, the rest emit Post. Declaration
57
+ * only: the handler keeps its full-row ORM, so a
58
+ * judge can still read `body`.
59
+ * Crud(Post, PostPublic) → every op emits PostPublic, and the bootstrap
60
+ * scopes the injected ORM via .output(PostPublic) —
61
+ * the whole handler speaks the restricted view.
62
+ */
63
+ export function Crud(entity, output) {
64
+ // A view is a class (it carries fields) ; a map of views is a plain object.
65
+ const perOp = typeof output === 'object' && output !== null ? output : undefined;
66
+ const wholeHandler = typeof output === 'function' ? output : undefined;
67
+ return asCrudConstructor(class CrudHandler {
68
+ static __entity = entity;
69
+ /** Handler-wide view only — a per-op map must NOT scope the ORM the judges read. */
70
+ static __output = wholeHandler ?? entity;
71
+ static __opOutputs = perOp;
72
+ /** What this prefab handler declares — read by the façade, merged under the author's own methods. */
73
+ static __ops = crudOps(entity);
74
+ orm;
75
+ constructor(orm) {
76
+ this.orm = orm;
77
+ }
78
+ async list(options) { return this.orm.list(options); }
79
+ async findById(id) { return this.orm.findById(id); }
80
+ async create(input) { return this.orm.create(input); }
81
+ async update(id, input) { return this.orm.update(id, input); }
82
+ async delete(id) { return this.orm.delete(id); }
83
+ });
84
+ }
85
+ //# sourceMappingURL=crud.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crud.js","sourceRoot":"","sources":["../src/crud.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { FougereConfig } from './config-loader.js';
2
+ /**
3
+ * Identity helper for fougere.config.ts — preserves literal types for autocomplete
4
+ * while validating against FougereConfig. Equivalent to `satisfies FougereConfig`
5
+ * but more explicit and aligned with defineFrond().
6
+ */
7
+ export declare function defineFougere<T extends FougereConfig>(config: T): T;
8
+ //# sourceMappingURL=define.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,aAAa,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAEnE"}
package/dist/define.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Identity helper for fougere.config.ts — preserves literal types for autocomplete
3
+ * while validating against FougereConfig. Equivalent to `satisfies FougereConfig`
4
+ * but more explicit and aligned with defineFrond().
5
+ */
6
+ export function defineFougere(config) {
7
+ return config;
8
+ }
9
+ //# sourceMappingURL=define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAA0B,MAAS;IAC9D,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * A value leaving the domain.
3
+ *
4
+ * Two things happen every time, in this order:
5
+ *
6
+ * 1. JUDGE — is this a legal value? Read from `shape` alone, so the answer does not
7
+ * depend on who is receiving. It has to come first: judging after the projection
8
+ * would fail on a client view that legitimately dropped its `writeOnly` fields.
9
+ * 2. PROJECT — what may THIS receiver see? A client does not get a password hash;
10
+ * storage sees everything.
11
+ *
12
+ * Storage is a way out like the others. It used to be the exception — the ORM wrote
13
+ * whatever a handler handed it, so `status: 'n-importe-quoi'` on a
14
+ * `oneOf('draft','published')` was stored and read back unchanged (measured
15
+ * 2026-07-25). The database is a weak judge: it catches nullability and, outside
16
+ * SQLite, the column type — never a closed set, a format or a range, since the DDL
17
+ * emits no CHECK. The choice was never "fail late or judge early", it was "corrupt
18
+ * silently or judge early".
19
+ *
20
+ * What the judge must NOT read: `boundary` and `lifecycle`. Both answer "may a CLIENT
21
+ * send this?", a question with no meaning when the domain itself is writing — reading
22
+ * them here is exactly what makes `Post.validate(a_db_row)` come back invalid.
23
+ *
24
+ * Shallow, like `encodeFields` it stands on: a relation's nested rows are not reached.
25
+ * A handler that hand-rolls its own envelope owns its own egress.
26
+ */
27
+ import { type Fields } from '@fougere/schema';
28
+ /**
29
+ * Refuse a value the shape does not accept, before it goes anywhere.
30
+ *
31
+ * Only the keys actually present are judged, so a patch stays a patch: an update
32
+ * naming one field says nothing about the others. A key with no field is not this
33
+ * judge's business — the receiver fails on its own (an unknown column), and a client
34
+ * input already met `validateFields`, which refuses strangers.
35
+ */
36
+ export declare function judgeEgress(fields: Fields, value: unknown, entity: string, operation: string): void;
37
+ /**
38
+ * Drop what this receiver may not see — a `writeOnly` field goes here — then hand the
39
+ * rest to `encodeFields`, which converts the values to their wire form.
40
+ *
41
+ * `closed` says the field set is the WHOLE of what this op emits, so anything else is
42
+ * dropped. That is what naming a view for an op means (`Crud(Post, { list: PostCard })`):
43
+ * the author states the audience, and a field they left out must not ride along. Open is
44
+ * the default and stays the rule for the entity itself — a presenter's computed field is
45
+ * an addition to the entity's output, not an intruder.
46
+ */
47
+ export declare function projectEgress(fields: Fields, result: unknown, closed?: boolean): unknown;
48
+ /**
49
+ * Add a presenter's computed fields — the last step out, after the projection.
50
+ *
51
+ * After, not before, for two reasons: a computed field is not an entity field, so
52
+ * `encodeFields` has no boundary to apply to it; and it must not be dropped by the
53
+ * projection, which keeps only what the entity declares.
54
+ *
55
+ * Skipped when the op names a view (`closed`): there the author stated exactly what
56
+ * this audience gets, and an addition they did not list would contradict it. Open is
57
+ * the default, and there a computed field is an addition to the entity's output, not
58
+ * an intruder.
59
+ *
60
+ * It ran only in the REST and GraphQL projections before, each with its own copy — so
61
+ * the same presenter answered on two doors and was silently absent from `useQuery`.
62
+ * The cost travels with it, which is why the signature takes the page: a field is handed
63
+ * every row at once and answers one value per row, so a read it needs is issued once.
64
+ */
65
+ /** Resolved arguments per computed field — everything the signature declares after the rows. */
66
+ export type PresenterArgs = Record<string, unknown[]>;
67
+ export declare function presentEgress(result: unknown, presenter: Record<string, unknown> | undefined, fieldNames: string[] | undefined, entity?: string, operation?: string, args?: PresenterArgs): Promise<unknown>;
68
+ /**
69
+ * The storage way out: judge, then hand over. There is no projection step — storage is
70
+ * the receiver that sees everything, which is precisely why a `writeOnly` field can be
71
+ * persisted while never reaching a browser.
72
+ *
73
+ * `Object.create` keeps the original on the prototype chain, so reads, `output()` and
74
+ * whatever else an adapter carries still resolve, and a copy scoped later inherits the
75
+ * judgement instead of escaping it.
76
+ */
77
+ export declare function guardStorage<T extends object>(orm: T, fields: Fields, entityName: string): T;
78
+ //# sourceMappingURL=egress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"egress.d.ts","sourceRoot":"","sources":["../src/egress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAA4B,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAMxE;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAsBnG;AAiBD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAQ,GAAG,OAAO,CAgBtF;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,gGAAgG;AAChG,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAEtD,wBAAsB,aAAa,CACjC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC9C,UAAU,EAAE,MAAM,EAAE,GAAG,SAAS,EAChC,MAAM,SAAY,EAClB,SAAS,SAAY,EACrB,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,OAAO,CAAC,CA0ClB;AAUD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,CAgC5F"}