@fougere/admin 0.3.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 +71 -0
- package/dist/dashboard.d.ts +74 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +334 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/extensions.d.ts +27 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +74 -0
- package/dist/extensions.js.map +1 -0
- package/dist/facets.d.ts +60 -0
- package/dist/facets.d.ts.map +1 -0
- package/dist/facets.js +39 -0
- package/dist/facets.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +163 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +205 -0
- package/dist/provider.js.map +1 -0
- package/dist/react.d.ts +70 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +235 -0
- package/dist/react.js.map +1 -0
- package/dist/resources.d.ts +69 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +92 -0
- package/dist/resources.js.map +1 -0
- package/dist/runtime.d.ts +22 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +36 -0
- package/dist/runtime.js.map +1 -0
- package/dist/theme.d.ts +10 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +419 -0
- package/dist/theme.js.map +1 -0
- package/dist/topology-page.d.ts +8 -0
- package/dist/topology-page.d.ts.map +1 -0
- package/dist/topology-page.js +69 -0
- package/dist/topology-page.js.map +1 -0
- package/dist/topology.d.ts +46 -0
- package/dist/topology.d.ts.map +1 -0
- package/dist/topology.js +58 -0
- package/dist/topology.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { capabilitiesOf } from './resources.js';
|
|
2
|
+
import { mergeAdminFacets } from './facets.js';
|
|
3
|
+
/** Typed identity helper for extensions kept in their own module. */
|
|
4
|
+
export function defineAdminExtension(extension) {
|
|
5
|
+
return extension;
|
|
6
|
+
}
|
|
7
|
+
function mergeExtensions(extensions) {
|
|
8
|
+
const merged = {};
|
|
9
|
+
for (const extension of extensions) {
|
|
10
|
+
if (extension.label !== undefined)
|
|
11
|
+
merged.label = extension.label;
|
|
12
|
+
if (extension.facets !== undefined) {
|
|
13
|
+
merged.facets = mergeAdminFacets(merged.facets ?? {}, extension.facets);
|
|
14
|
+
}
|
|
15
|
+
if (extension.fields) {
|
|
16
|
+
merged.fields ??= {};
|
|
17
|
+
for (const [name, patch] of Object.entries(extension.fields)) {
|
|
18
|
+
merged.fields[name] = { ...merged.fields[name], ...patch };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (extension.operations) {
|
|
22
|
+
merged.operations ??= {};
|
|
23
|
+
for (const [name, patch] of Object.entries(extension.operations)) {
|
|
24
|
+
merged.operations[name] = { ...merged.operations[name], ...patch };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return merged;
|
|
29
|
+
}
|
|
30
|
+
function extendFields(fields, patches) {
|
|
31
|
+
return fields.flatMap((field) => {
|
|
32
|
+
const patch = patches?.[field.name];
|
|
33
|
+
if (patch?.hidden)
|
|
34
|
+
return [];
|
|
35
|
+
return [{ ...field, ...(patch?.label !== undefined ? { label: patch.label } : {}) }];
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function extendOperations(operations, patches) {
|
|
39
|
+
return operations.map((operation) => {
|
|
40
|
+
const patch = patches?.[operation.name];
|
|
41
|
+
return {
|
|
42
|
+
...operation,
|
|
43
|
+
...(patch?.label !== undefined ? { label: patch.label } : {}),
|
|
44
|
+
...(patch?.confirm === false
|
|
45
|
+
? { confirm: undefined }
|
|
46
|
+
: patch?.confirm !== undefined
|
|
47
|
+
? { confirm: patch.confirm }
|
|
48
|
+
: {}),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/** Apply deltas to the latest derived model. Unmentioned and future resources pass through. */
|
|
53
|
+
export function applyAdminExtensions(resources, extensions = []) {
|
|
54
|
+
const byResource = new Map();
|
|
55
|
+
for (const extension of extensions) {
|
|
56
|
+
const list = byResource.get(extension.resource) ?? [];
|
|
57
|
+
list.push(extension);
|
|
58
|
+
byResource.set(extension.resource, list);
|
|
59
|
+
}
|
|
60
|
+
return resources.map((resource) => {
|
|
61
|
+
const patch = mergeExtensions(byResource.get(resource.name) ?? []);
|
|
62
|
+
const operations = extendOperations(resource.operations, patch.operations);
|
|
63
|
+
return {
|
|
64
|
+
...resource,
|
|
65
|
+
...(patch.label !== undefined ? { label: patch.label } : {}),
|
|
66
|
+
facets: mergeAdminFacets(resource.facets, patch.facets ?? {}),
|
|
67
|
+
columns: extendFields(resource.columns, patch.fields),
|
|
68
|
+
fields: extendFields(resource.fields, patch.fields),
|
|
69
|
+
operations,
|
|
70
|
+
can: capabilitiesOf(operations),
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=extensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensions.js","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"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"}
|
package/dist/facets.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A semantic notion a renderer can act on, DECLARED — never guessed.
|
|
3
|
+
*
|
|
4
|
+
* A facet says something the schema does not: which field is the human title, which
|
|
5
|
+
* enum member means "published". Both are true statements about a domain, and neither
|
|
6
|
+
* is readable from a shape — `oneOf('draft', 'live')` says the set is closed, never
|
|
7
|
+
* what a member MEANS.
|
|
8
|
+
*
|
|
9
|
+
* There was an `inferAdminFacets` here that answered it by field NAME — `title`,
|
|
10
|
+
* `status`, `/^(user|users|member|account)$/`, `['draft','pending','review']`. It was
|
|
11
|
+
* removed on 2026-08-21: the repo recognises a field by its FORM, never by a word, and
|
|
12
|
+
* that heuristic reads an English vocabulary. An entity spelling `titre`, or a status
|
|
13
|
+
* spelling `brouillon`, lost its facet and nothing said so — a wrong answer given
|
|
14
|
+
* silently, where an absent one is merely absent.
|
|
15
|
+
*
|
|
16
|
+
* So a facet is config, and config is exceptions: no facet is the default, and a
|
|
17
|
+
* renderer that finds none shows the derived table, which is already the whole
|
|
18
|
+
* back-office. What is missing is upstream — the declaration has no way to say what a
|
|
19
|
+
* closed set's member means, and until it has, this is where a human says it.
|
|
20
|
+
*/
|
|
21
|
+
export interface EditorialFacet {
|
|
22
|
+
/** The field that carries the human-readable title. */
|
|
23
|
+
title: string;
|
|
24
|
+
/** The lifecycle vocabulary, grouped by meaning rather than by raw enum value. */
|
|
25
|
+
state?: {
|
|
26
|
+
field: string;
|
|
27
|
+
draft?: readonly string[];
|
|
28
|
+
published?: readonly string[];
|
|
29
|
+
};
|
|
30
|
+
createdAt?: string;
|
|
31
|
+
updatedAt?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface UsersFacet {
|
|
34
|
+
name: string;
|
|
35
|
+
email?: string;
|
|
36
|
+
role?: string;
|
|
37
|
+
state?: {
|
|
38
|
+
field: string;
|
|
39
|
+
active?: readonly string[];
|
|
40
|
+
invited?: readonly string[];
|
|
41
|
+
suspended?: readonly string[];
|
|
42
|
+
};
|
|
43
|
+
createdAt?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Open registry of the semantic projections renderers understand.
|
|
47
|
+
*
|
|
48
|
+
* Fougere ships `editorial` and `users`; a plugin adds `media`, `commerce`,
|
|
49
|
+
* `moderation` without the core renderer growing another enum member.
|
|
50
|
+
*/
|
|
51
|
+
export interface AdminFacetRegistry {
|
|
52
|
+
editorial: EditorialFacet;
|
|
53
|
+
users: UsersFacet;
|
|
54
|
+
[facet: string]: unknown;
|
|
55
|
+
}
|
|
56
|
+
export type AdminFacets = Partial<AdminFacetRegistry>;
|
|
57
|
+
export declare function defineAdminFacet<const Name extends string, const Value>(name: Name, value: Value): Record<Name, Value>;
|
|
58
|
+
/** Arrays replace; semantic objects merge recursively so small project deltas stay small. */
|
|
59
|
+
export declare function mergeAdminFacets(base: AdminFacets, patch: AdminFacets): AdminFacets;
|
|
60
|
+
//# sourceMappingURL=facets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facets.d.ts","sourceRoot":"","sources":["../src/facets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,cAAc;IAC7B,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,KAAK,CAAC,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC1B,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KAC/B,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC5B,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KAC/B,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,cAAc,CAAC;IAC1B,KAAK,EAAE,UAAU,CAAC;IAClB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAEtD,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,IAAI,SAAS,MAAM,EAAE,KAAK,CAAC,KAAK,EACrE,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,KAAK,GACX,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAErB;AAMD,6FAA6F;AAC7F,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,GAAG,WAAW,CAQnF"}
|
package/dist/facets.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A semantic notion a renderer can act on, DECLARED — never guessed.
|
|
3
|
+
*
|
|
4
|
+
* A facet says something the schema does not: which field is the human title, which
|
|
5
|
+
* enum member means "published". Both are true statements about a domain, and neither
|
|
6
|
+
* is readable from a shape — `oneOf('draft', 'live')` says the set is closed, never
|
|
7
|
+
* what a member MEANS.
|
|
8
|
+
*
|
|
9
|
+
* There was an `inferAdminFacets` here that answered it by field NAME — `title`,
|
|
10
|
+
* `status`, `/^(user|users|member|account)$/`, `['draft','pending','review']`. It was
|
|
11
|
+
* removed on 2026-08-21: the repo recognises a field by its FORM, never by a word, and
|
|
12
|
+
* that heuristic reads an English vocabulary. An entity spelling `titre`, or a status
|
|
13
|
+
* spelling `brouillon`, lost its facet and nothing said so — a wrong answer given
|
|
14
|
+
* silently, where an absent one is merely absent.
|
|
15
|
+
*
|
|
16
|
+
* So a facet is config, and config is exceptions: no facet is the default, and a
|
|
17
|
+
* renderer that finds none shows the derived table, which is already the whole
|
|
18
|
+
* back-office. What is missing is upstream — the declaration has no way to say what a
|
|
19
|
+
* closed set's member means, and until it has, this is where a human says it.
|
|
20
|
+
*/
|
|
21
|
+
export function defineAdminFacet(name, value) {
|
|
22
|
+
return { [name]: value };
|
|
23
|
+
}
|
|
24
|
+
function isPlainObject(value) {
|
|
25
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
26
|
+
}
|
|
27
|
+
/** Arrays replace; semantic objects merge recursively so small project deltas stay small. */
|
|
28
|
+
export function mergeAdminFacets(base, patch) {
|
|
29
|
+
const merge = (left, right) => {
|
|
30
|
+
if (!isPlainObject(left) || !isPlainObject(right))
|
|
31
|
+
return right;
|
|
32
|
+
const out = { ...left };
|
|
33
|
+
for (const [key, value] of Object.entries(right))
|
|
34
|
+
out[key] = merge(out[key], value);
|
|
35
|
+
return out;
|
|
36
|
+
};
|
|
37
|
+
return merge(base, patch);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=facets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facets.js","sourceRoot":"","sources":["../src/facets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA0CH,MAAM,UAAU,gBAAgB,CAC9B,IAAU,EACV,KAAY;IAEZ,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAyB,CAAC;AAClD,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,CAAC,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AACvE,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,gBAAgB,CAAC,IAAiB,EAAE,KAAkB;IACpE,MAAM,KAAK,GAAG,CAAC,IAAa,EAAE,KAAc,EAAW,EAAE;QACvD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAChE,MAAM,GAAG,GAA4B,EAAE,GAAG,IAAI,EAAE,CAAC;QACjD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;QACpF,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,EAAE,KAAK,CAAgB,CAAC;AAC3C,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@fougere/admin` — a back-office that states no rule of its own.
|
|
3
|
+
*
|
|
4
|
+
* It reads the identity card and renders what the card says: the doors are the menu,
|
|
5
|
+
* the schema is the columns and the form, the ops are the buttons. Nothing here
|
|
6
|
+
* knows an entity by name, which is why a new one needs no code — and why the same
|
|
7
|
+
* build serves a frond running in this process and one behind `remotes:`.
|
|
8
|
+
*
|
|
9
|
+
* Two layers, kept apart because they do not test the same way and because only the
|
|
10
|
+
* first is derivable: this module is the contract, `./react` is the rendering. That
|
|
11
|
+
* is the line `useFormFor` already draws.
|
|
12
|
+
*/
|
|
13
|
+
export { createDataProvider, createLazyDataProvider, type FougereDataProvider, type ProviderOptions, type ResourceKey, } from './provider.js';
|
|
14
|
+
export { resourcesOf, keysOf, fetchCard, capabilitiesOf, actionsOf, CRUD_OPS, type AdminOperation, type AdminResource, } from './resources.js';
|
|
15
|
+
export { defineAdminFacet, mergeAdminFacets, type AdminFacetRegistry, type AdminFacets, type EditorialFacet, type UsersFacet, } from './facets.js';
|
|
16
|
+
export { applyAdminExtensions, defineAdminExtension, type AdminExtension, type AdminFieldExtension, type AdminOperationExtension, } from './extensions.js';
|
|
17
|
+
export { fetchTopology, nodesOf, isOpaque, type TopologyReport, type TopologyNode, type FrondPlacement, type Edge, } from './topology.js';
|
|
18
|
+
export { createAdminRuntime, type AdminRuntime, type AdminRuntimeOptions, type LoadedAdmin, } from './runtime.js';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,WAAW,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,WAAW,EACX,MAAM,EACN,SAAS,EACT,cAAc,EACd,SAAS,EACT,QAAQ,EACR,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,GAC7B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,OAAO,EACP,QAAQ,EACR,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,IAAI,GACV,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@fougere/admin` — a back-office that states no rule of its own.
|
|
3
|
+
*
|
|
4
|
+
* It reads the identity card and renders what the card says: the doors are the menu,
|
|
5
|
+
* the schema is the columns and the form, the ops are the buttons. Nothing here
|
|
6
|
+
* knows an entity by name, which is why a new one needs no code — and why the same
|
|
7
|
+
* build serves a frond running in this process and one behind `remotes:`.
|
|
8
|
+
*
|
|
9
|
+
* Two layers, kept apart because they do not test the same way and because only the
|
|
10
|
+
* first is derivable: this module is the contract, `./react` is the rendering. That
|
|
11
|
+
* is the line `useFormFor` already draws.
|
|
12
|
+
*/
|
|
13
|
+
export { createDataProvider, createLazyDataProvider, } from './provider.js';
|
|
14
|
+
export { resourcesOf, keysOf, fetchCard, capabilitiesOf, actionsOf, CRUD_OPS, } from './resources.js';
|
|
15
|
+
export { defineAdminFacet, mergeAdminFacets, } from './facets.js';
|
|
16
|
+
export { applyAdminExtensions, defineAdminExtension, } from './extensions.js';
|
|
17
|
+
export { fetchTopology, nodesOf, isOpaque, } from './topology.js';
|
|
18
|
+
export { createAdminRuntime, } from './runtime.js';
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GAIvB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,WAAW,EACX,MAAM,EACN,SAAS,EACT,cAAc,EACd,SAAS,EACT,QAAQ,GAGT,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,GAKjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GAIrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,OAAO,EACP,QAAQ,GAKT,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,GAInB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-admin's `DataProvider`, answered by the Fougere wire.
|
|
3
|
+
*
|
|
4
|
+
* Nine functions, and every one of them had a counterpart already: `getList` is
|
|
5
|
+
* `ListOptions`, `getManyReference` is the ORM's `findAllByKeys` (written for a
|
|
6
|
+
* presenter that wanted "the items of these lists"), `filter` is `where` — an
|
|
7
|
+
* equality map that already refuses an unknown key, because a filter silently
|
|
8
|
+
* dropped had once returned the whole table.
|
|
9
|
+
*
|
|
10
|
+
* It talks to the FAÇADE and never to an ORM, which is not a precaution here but a
|
|
11
|
+
* consequence: this runs in a browser, and the only thing a browser can reach is the
|
|
12
|
+
* call endpoint. Judges, presenters and middlewares are therefore on the path of
|
|
13
|
+
* every read and every write the back-office makes.
|
|
14
|
+
*/
|
|
15
|
+
import { type Fetcher } from '@fougere/app/client';
|
|
16
|
+
/** What a resource must tell the provider — the rest of the card is the UI's business. */
|
|
17
|
+
export interface ResourceKey {
|
|
18
|
+
/** The registration key its door answers under — `post`, not `Post`. */
|
|
19
|
+
name: string;
|
|
20
|
+
/**
|
|
21
|
+
* The field that identifies a row. react-admin insists on `id`; an entity does not,
|
|
22
|
+
* so the provider renames it on the way out and back. `primaryFieldOf` reads it off
|
|
23
|
+
* the card, and answers `undefined` rather than defaulting — a shape with no primary
|
|
24
|
+
* has no row identity to invent.
|
|
25
|
+
*/
|
|
26
|
+
primary: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ProviderOptions {
|
|
29
|
+
/** Registration key → its identity. Built from the card by `resourcesOf`. */
|
|
30
|
+
resources: Record<string, ResourceKey>;
|
|
31
|
+
/** The call endpoint. A named surface appends `/{surface}` to it. */
|
|
32
|
+
endpoint?: string;
|
|
33
|
+
fetcher?: Fetcher;
|
|
34
|
+
}
|
|
35
|
+
type Row = Record<string, unknown>;
|
|
36
|
+
export declare function createDataProvider(options: ProviderOptions): {
|
|
37
|
+
/**
|
|
38
|
+
* One page, and how it knows there is another.
|
|
39
|
+
*
|
|
40
|
+
* `count: true` is asked for and read when it arrives — but `ListResult` extends
|
|
41
|
+
* `Array`, so `JSON.stringify` drops its `total` and nothing wraps a `page`
|
|
42
|
+
* result today. The same handler therefore answers a total in-process and none
|
|
43
|
+
* over the wire. Rather than depend on it, the page is fetched one row longer
|
|
44
|
+
* than asked: the surplus row IS the answer to "is there a next page", which is
|
|
45
|
+
* `pageInfo`, a form react-admin accepts in place of a total.
|
|
46
|
+
*/
|
|
47
|
+
getList: (resource: string, params: {
|
|
48
|
+
pagination?: {
|
|
49
|
+
page: number;
|
|
50
|
+
perPage: number;
|
|
51
|
+
};
|
|
52
|
+
sort?: {
|
|
53
|
+
field: string;
|
|
54
|
+
order: string;
|
|
55
|
+
};
|
|
56
|
+
filter?: Record<string, unknown>;
|
|
57
|
+
}) => Promise<{
|
|
58
|
+
data: Row[];
|
|
59
|
+
total?: number | undefined;
|
|
60
|
+
pageInfo: {
|
|
61
|
+
hasNextPage: boolean;
|
|
62
|
+
hasPreviousPage: boolean;
|
|
63
|
+
};
|
|
64
|
+
}>;
|
|
65
|
+
getOne: (resource: string, params: {
|
|
66
|
+
id: string | number;
|
|
67
|
+
}) => Promise<{
|
|
68
|
+
data: Row;
|
|
69
|
+
}>;
|
|
70
|
+
/**
|
|
71
|
+
* N calls, and the reason is upstream: `findByKeys` is a gesture of the ORM port,
|
|
72
|
+
* not one of `Crud`'s five ops, so there is no door to ask for several rows by id.
|
|
73
|
+
* Said here rather than hidden — the day `Crud` names that op this becomes one call.
|
|
74
|
+
*/
|
|
75
|
+
getMany: (resource: string, params: {
|
|
76
|
+
ids: Array<string | number>;
|
|
77
|
+
}) => Promise<{
|
|
78
|
+
data: Row[];
|
|
79
|
+
}>;
|
|
80
|
+
/** The rows pointing AT one — an equality filter, which `where` already is. */
|
|
81
|
+
getManyReference: (resource: string, params: {
|
|
82
|
+
target: string;
|
|
83
|
+
id: string | number;
|
|
84
|
+
pagination?: {
|
|
85
|
+
page: number;
|
|
86
|
+
perPage: number;
|
|
87
|
+
};
|
|
88
|
+
sort?: {
|
|
89
|
+
field: string;
|
|
90
|
+
order: string;
|
|
91
|
+
};
|
|
92
|
+
filter?: Record<string, unknown>;
|
|
93
|
+
}) => Promise<{
|
|
94
|
+
data: Row[];
|
|
95
|
+
total?: number;
|
|
96
|
+
}>;
|
|
97
|
+
create: (resource: string, params: {
|
|
98
|
+
data: Row;
|
|
99
|
+
}) => Promise<{
|
|
100
|
+
data: Row;
|
|
101
|
+
}>;
|
|
102
|
+
update: (resource: string, params: {
|
|
103
|
+
id: string | number;
|
|
104
|
+
data: Row;
|
|
105
|
+
}) => Promise<{
|
|
106
|
+
data: Row;
|
|
107
|
+
}>;
|
|
108
|
+
delete: (resource: string, params: {
|
|
109
|
+
id: string | number;
|
|
110
|
+
previousData?: Row;
|
|
111
|
+
}) => Promise<{
|
|
112
|
+
data: Row;
|
|
113
|
+
}>;
|
|
114
|
+
/**
|
|
115
|
+
* The bulk pair, one call per row.
|
|
116
|
+
*
|
|
117
|
+
* `Together<[…]>` would make them one unit of work — but it is a port a HANDLER
|
|
118
|
+
* asks for, and this is a browser. Making these atomic means naming an operation
|
|
119
|
+
* in the frond that says so, which is the app's sentence to write, not ours.
|
|
120
|
+
*/
|
|
121
|
+
updateMany: (resource: string, params: {
|
|
122
|
+
ids: Array<string | number>;
|
|
123
|
+
data: Row;
|
|
124
|
+
}) => Promise<{
|
|
125
|
+
data: (string | number)[];
|
|
126
|
+
}>;
|
|
127
|
+
deleteMany: (resource: string, params: {
|
|
128
|
+
ids: Array<string | number>;
|
|
129
|
+
}) => Promise<{
|
|
130
|
+
data: (string | number)[];
|
|
131
|
+
}>;
|
|
132
|
+
/**
|
|
133
|
+
* A tenth method, because the other nine are CRUD and a Frond is not.
|
|
134
|
+
*
|
|
135
|
+
* react-admin's verbs describe a RESOURCE; `publish` describes an ACTION, and none of
|
|
136
|
+
* the nine can carry it. `DataProvider` has an index signature for exactly this case.
|
|
137
|
+
*
|
|
138
|
+
* **The id rides in `params`, and that is an assumption.** The card states an op's
|
|
139
|
+
* input SCHEMA and never its binding plan — `computeBindingPlan` lives in the boot and
|
|
140
|
+
* does not travel — so a button derived from a card must guess where each argument is
|
|
141
|
+
* read from, and it guesses the CRUD convention. True of every op the scan derives
|
|
142
|
+
* today; the honest fix is upstream, making the binding ride in `CardOp`.
|
|
143
|
+
*/
|
|
144
|
+
invoke: (resource: string, params: {
|
|
145
|
+
op: string;
|
|
146
|
+
id?: string | number;
|
|
147
|
+
data?: Row;
|
|
148
|
+
}) => Promise<{
|
|
149
|
+
data: unknown;
|
|
150
|
+
}>;
|
|
151
|
+
};
|
|
152
|
+
export type FougereDataProvider = ReturnType<typeof createDataProvider>;
|
|
153
|
+
/**
|
|
154
|
+
* The card arrives asynchronously; react-admin requires a provider synchronously.
|
|
155
|
+
*
|
|
156
|
+
* Spell the nine delegates instead of hiding them behind a Proxy. Apart from being
|
|
157
|
+
* inspectable and type-checked, this survives object spreading — the prototype used a
|
|
158
|
+
* Proxy and then spread it into `{}`, which enumerated no methods and handed react-admin
|
|
159
|
+
* an empty provider.
|
|
160
|
+
*/
|
|
161
|
+
export declare function createLazyDataProvider(load: () => Promise<FougereDataProvider>): FougereDataProvider;
|
|
162
|
+
export {};
|
|
163
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-admin's `DataProvider`, answered by the Fougere wire.
|
|
3
|
+
*
|
|
4
|
+
* Nine functions, and every one of them had a counterpart already: `getList` is
|
|
5
|
+
* `ListOptions`, `getManyReference` is the ORM's `findAllByKeys` (written for a
|
|
6
|
+
* presenter that wanted "the items of these lists"), `filter` is `where` — an
|
|
7
|
+
* equality map that already refuses an unknown key, because a filter silently
|
|
8
|
+
* dropped had once returned the whole table.
|
|
9
|
+
*
|
|
10
|
+
* It talks to the FAÇADE and never to an ORM, which is not a precaution here but a
|
|
11
|
+
* consequence: this runs in a browser, and the only thing a browser can reach is the
|
|
12
|
+
* call endpoint. Judges, presenters and middlewares are therefore on the path of
|
|
13
|
+
* every read and every write the back-office makes.
|
|
14
|
+
*/
|
|
15
|
+
import { sendCall, fetcher as browserFetcher, CALL_ENDPOINT, itemsOf, pageOf, errorsByField, } from '@fougere/app/client';
|
|
16
|
+
import { validationErrorsOf } from '@fougere/core/contract';
|
|
17
|
+
/**
|
|
18
|
+
* A refusal, in the shape react-admin reads.
|
|
19
|
+
*
|
|
20
|
+
* `body.errors` keyed by field is what its forms display; `errorsByField` already
|
|
21
|
+
* produces exactly that, and is the same function the Vue and React form primitives
|
|
22
|
+
* use — so a field is highlighted identically whichever door the page came through.
|
|
23
|
+
* A non-validation failure passes through untouched: it is not a form's business.
|
|
24
|
+
*/
|
|
25
|
+
function asAdminError(err) {
|
|
26
|
+
const refusals = validationErrorsOf(err);
|
|
27
|
+
if (!refusals)
|
|
28
|
+
return err;
|
|
29
|
+
return Object.assign(new Error(err.message), {
|
|
30
|
+
status: 400,
|
|
31
|
+
body: { errors: errorsByField(refusals) },
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function createDataProvider(options) {
|
|
35
|
+
const { resources, endpoint = CALL_ENDPOINT, fetcher = browserFetcher } = options;
|
|
36
|
+
const keyOf = (resource) => {
|
|
37
|
+
const known = resources[resource];
|
|
38
|
+
if (!known)
|
|
39
|
+
throw new Error(`Unknown resource '${resource}' — the card names none of that address`);
|
|
40
|
+
return known;
|
|
41
|
+
};
|
|
42
|
+
const call = async (resource, op, input) => {
|
|
43
|
+
try {
|
|
44
|
+
return await sendCall(fetcher, { entity: resource, op }, {
|
|
45
|
+
params: {}, query: {}, body: undefined, state: {}, ...input,
|
|
46
|
+
}, endpoint);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
throw asAdminError(err);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
/** The row as react-admin needs it: whatever identifies it, under `id`. */
|
|
53
|
+
const identified = (row, key) => row === undefined || key.primary === 'id' ? row : { ...row, id: row[key.primary] };
|
|
54
|
+
/** Its dual — what leaves for the server carries the entity's own field name. */
|
|
55
|
+
const deidentified = (data, key) => {
|
|
56
|
+
if (key.primary === 'id')
|
|
57
|
+
return data;
|
|
58
|
+
const { id, ...rest } = data;
|
|
59
|
+
return id === undefined ? rest : { ...rest, [key.primary]: id };
|
|
60
|
+
};
|
|
61
|
+
const list = async (resource, query) => {
|
|
62
|
+
const key = keyOf(resource);
|
|
63
|
+
const answer = await call(resource, 'list', { query });
|
|
64
|
+
return {
|
|
65
|
+
data: itemsOf(answer).map((row) => identified(row, key)),
|
|
66
|
+
total: pageOf(answer).total,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
/**
|
|
71
|
+
* One page, and how it knows there is another.
|
|
72
|
+
*
|
|
73
|
+
* `count: true` is asked for and read when it arrives — but `ListResult` extends
|
|
74
|
+
* `Array`, so `JSON.stringify` drops its `total` and nothing wraps a `page`
|
|
75
|
+
* result today. The same handler therefore answers a total in-process and none
|
|
76
|
+
* over the wire. Rather than depend on it, the page is fetched one row longer
|
|
77
|
+
* than asked: the surplus row IS the answer to "is there a next page", which is
|
|
78
|
+
* `pageInfo`, a form react-admin accepts in place of a total.
|
|
79
|
+
*/
|
|
80
|
+
getList: async (resource, params) => {
|
|
81
|
+
const { page = 1, perPage = 25 } = params.pagination ?? {};
|
|
82
|
+
const { data, total } = await list(resource, {
|
|
83
|
+
limit: perPage + 1,
|
|
84
|
+
offset: (page - 1) * perPage,
|
|
85
|
+
count: true,
|
|
86
|
+
...(params.sort?.field
|
|
87
|
+
? { orderBy: params.sort.field, order: params.sort.order?.toLowerCase() }
|
|
88
|
+
: {}),
|
|
89
|
+
...(params.filter && Object.keys(params.filter).length ? { where: params.filter } : {}),
|
|
90
|
+
});
|
|
91
|
+
const hasNextPage = data.length > perPage;
|
|
92
|
+
return {
|
|
93
|
+
data: hasNextPage ? data.slice(0, perPage) : data,
|
|
94
|
+
...(total !== undefined ? { total } : {}),
|
|
95
|
+
pageInfo: { hasNextPage, hasPreviousPage: page > 1 },
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
getOne: async (resource, params) => {
|
|
99
|
+
const key = keyOf(resource);
|
|
100
|
+
const row = identified((await call(resource, 'findById', { params: { id: params.id } })), key);
|
|
101
|
+
if (!row)
|
|
102
|
+
throw Object.assign(new Error(`${resource} ${params.id} not found`), { status: 404 });
|
|
103
|
+
return { data: row };
|
|
104
|
+
},
|
|
105
|
+
/**
|
|
106
|
+
* N calls, and the reason is upstream: `findByKeys` is a gesture of the ORM port,
|
|
107
|
+
* not one of `Crud`'s five ops, so there is no door to ask for several rows by id.
|
|
108
|
+
* Said here rather than hidden — the day `Crud` names that op this becomes one call.
|
|
109
|
+
*/
|
|
110
|
+
getMany: async (resource, params) => {
|
|
111
|
+
const key = keyOf(resource);
|
|
112
|
+
const rows = await Promise.all(params.ids.map((id) => call(resource, 'findById', { params: { id } })));
|
|
113
|
+
return { data: rows.map((row) => identified(row, key)).filter((row) => !!row) };
|
|
114
|
+
},
|
|
115
|
+
/** The rows pointing AT one — an equality filter, which `where` already is. */
|
|
116
|
+
getManyReference: async (resource, params) => {
|
|
117
|
+
const { page = 1, perPage = 25 } = params.pagination ?? {};
|
|
118
|
+
return list(resource, {
|
|
119
|
+
limit: perPage,
|
|
120
|
+
offset: (page - 1) * perPage,
|
|
121
|
+
count: true,
|
|
122
|
+
where: { ...params.filter, [params.target]: params.id },
|
|
123
|
+
...(params.sort?.field
|
|
124
|
+
? { orderBy: params.sort.field, order: params.sort.order?.toLowerCase() }
|
|
125
|
+
: {}),
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
create: async (resource, params) => {
|
|
129
|
+
const key = keyOf(resource);
|
|
130
|
+
const row = await call(resource, 'create', { body: deidentified(params.data, key) });
|
|
131
|
+
return { data: identified(row, key) };
|
|
132
|
+
},
|
|
133
|
+
update: async (resource, params) => {
|
|
134
|
+
const key = keyOf(resource);
|
|
135
|
+
const row = await call(resource, 'update', {
|
|
136
|
+
params: { id: params.id },
|
|
137
|
+
body: deidentified(params.data, key),
|
|
138
|
+
});
|
|
139
|
+
return { data: identified(row, key) };
|
|
140
|
+
},
|
|
141
|
+
delete: async (resource, params) => {
|
|
142
|
+
await call(resource, 'delete', { params: { id: params.id } });
|
|
143
|
+
return { data: (params.previousData ?? { id: params.id }) };
|
|
144
|
+
},
|
|
145
|
+
/**
|
|
146
|
+
* The bulk pair, one call per row.
|
|
147
|
+
*
|
|
148
|
+
* `Together<[…]>` would make them one unit of work — but it is a port a HANDLER
|
|
149
|
+
* asks for, and this is a browser. Making these atomic means naming an operation
|
|
150
|
+
* in the frond that says so, which is the app's sentence to write, not ours.
|
|
151
|
+
*/
|
|
152
|
+
updateMany: async (resource, params) => {
|
|
153
|
+
const key = keyOf(resource);
|
|
154
|
+
await Promise.all(params.ids.map((id) => call(resource, 'update', {
|
|
155
|
+
params: { id }, body: deidentified(params.data, key),
|
|
156
|
+
})));
|
|
157
|
+
return { data: params.ids };
|
|
158
|
+
},
|
|
159
|
+
deleteMany: async (resource, params) => {
|
|
160
|
+
await Promise.all(params.ids.map((id) => call(resource, 'delete', { params: { id } })));
|
|
161
|
+
return { data: params.ids };
|
|
162
|
+
},
|
|
163
|
+
/**
|
|
164
|
+
* A tenth method, because the other nine are CRUD and a Frond is not.
|
|
165
|
+
*
|
|
166
|
+
* react-admin's verbs describe a RESOURCE; `publish` describes an ACTION, and none of
|
|
167
|
+
* the nine can carry it. `DataProvider` has an index signature for exactly this case.
|
|
168
|
+
*
|
|
169
|
+
* **The id rides in `params`, and that is an assumption.** The card states an op's
|
|
170
|
+
* input SCHEMA and never its binding plan — `computeBindingPlan` lives in the boot and
|
|
171
|
+
* does not travel — so a button derived from a card must guess where each argument is
|
|
172
|
+
* read from, and it guesses the CRUD convention. True of every op the scan derives
|
|
173
|
+
* today; the honest fix is upstream, making the binding ride in `CardOp`.
|
|
174
|
+
*/
|
|
175
|
+
invoke: async (resource, params) => ({
|
|
176
|
+
data: await call(resource, params.op, {
|
|
177
|
+
...(params.id !== undefined ? { params: { id: String(params.id) } } : {}),
|
|
178
|
+
...(params.data !== undefined ? { body: params.data } : {}),
|
|
179
|
+
}),
|
|
180
|
+
}),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The card arrives asynchronously; react-admin requires a provider synchronously.
|
|
185
|
+
*
|
|
186
|
+
* Spell the nine delegates instead of hiding them behind a Proxy. Apart from being
|
|
187
|
+
* inspectable and type-checked, this survives object spreading — the prototype used a
|
|
188
|
+
* Proxy and then spread it into `{}`, which enumerated no methods and handed react-admin
|
|
189
|
+
* an empty provider.
|
|
190
|
+
*/
|
|
191
|
+
export function createLazyDataProvider(load) {
|
|
192
|
+
return {
|
|
193
|
+
getList: async (...args) => (await load()).getList(...args),
|
|
194
|
+
getOne: async (...args) => (await load()).getOne(...args),
|
|
195
|
+
getMany: async (...args) => (await load()).getMany(...args),
|
|
196
|
+
getManyReference: async (...args) => (await load()).getManyReference(...args),
|
|
197
|
+
create: async (...args) => (await load()).create(...args),
|
|
198
|
+
update: async (...args) => (await load()).update(...args),
|
|
199
|
+
delete: async (...args) => (await load()).delete(...args),
|
|
200
|
+
updateMany: async (...args) => (await load()).updateMany(...args),
|
|
201
|
+
deleteMany: async (...args) => (await load()).deleteMany(...args),
|
|
202
|
+
invoke: async (...args) => (await load()).invoke(...args),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|