@metaobjectsdev/runtime-web 1.0.5-rc.3 → 1.0.5-rc.6
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/dist/grid-from-metadata.d.ts +2 -2
- package/dist/grid-from-metadata.d.ts.map +1 -1
- package/dist/grid-from-metadata.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/load-meta-model.d.ts +11 -0
- package/dist/load-meta-model.d.ts.map +1 -0
- package/dist/load-meta-model.js +128 -0
- package/dist/load-meta-model.js.map +1 -0
- package/dist/meta-read.d.ts +44 -0
- package/dist/meta-read.d.ts.map +1 -0
- package/dist/meta-read.js +12 -0
- package/dist/meta-read.js.map +1 -0
- package/package.json +2 -2
- package/src/grid-from-metadata.ts +12 -8
- package/src/index.ts +4 -0
- package/src/load-meta-model.ts +144 -0
- package/src/meta-read.ts +59 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MetaRead } from "./meta-read.js";
|
|
2
2
|
import type { GridConfig } from "./fetcher.js";
|
|
3
3
|
/** A neutral, framework-agnostic column descriptor derived from metadata. */
|
|
4
4
|
export interface MetaColumn {
|
|
@@ -21,5 +21,5 @@ export interface MetaGrid {
|
|
|
21
21
|
* Otherwise every field becomes a column with sensible defaults — so it works
|
|
22
22
|
* for ANY object, with or without a declared grid.
|
|
23
23
|
*/
|
|
24
|
-
export declare function buildGrid(meta:
|
|
24
|
+
export declare function buildGrid(meta: MetaRead, gridName?: string): MetaGrid;
|
|
25
25
|
//# sourceMappingURL=grid-from-metadata.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid-from-metadata.d.ts","sourceRoot":"","sources":["../src/grid-from-metadata.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"grid-from-metadata.d.ts","sourceRoot":"","sources":["../src/grid-from-metadata.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,QAAQ,EAA+B,MAAM,gBAAgB,CAAC;AAgB5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM/C,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB;AAwDD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAmCrE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid-from-metadata.js","sourceRoot":"","sources":["../src/grid-from-metadata.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"grid-from-metadata.js","sourceRoot":"","sources":["../src/grid-from-metadata.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export type { EntityFetcher, GridConfig } from "./fetcher.js";
|
|
|
4
4
|
export { joinBaseUrl } from "./join-base-url.js";
|
|
5
5
|
export { buildGrid } from "./grid-from-metadata.js";
|
|
6
6
|
export type { MetaColumn, MetaGrid } from "./grid-from-metadata.js";
|
|
7
|
+
export type { MetaRead, MetaFieldRead, MetaViewRead, MetaLayoutRead, MetaModelRead, AttrReader, } from "./meta-read.js";
|
|
8
|
+
export { loadMetaModel } from "./load-meta-model.js";
|
|
7
9
|
export { canvasToJpegBlob } from "./canvas-to-jpeg-blob.js";
|
|
8
10
|
export { reencodeJpeg } from "./reencode-jpeg.js";
|
|
9
11
|
export type { ImageUploadAdapter, ImageMeta } from "./image-adapter.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACpE,YAAY,EACV,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,GACjF,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { formatCurrency, parseCurrency, minorUnitsFor } from "./currency.js";
|
|
|
3
3
|
export { buildFilterQs } from "./filter-qs.js";
|
|
4
4
|
export { joinBaseUrl } from "./join-base-url.js";
|
|
5
5
|
export { buildGrid } from "./grid-from-metadata.js";
|
|
6
|
+
export { loadMetaModel } from "./load-meta-model.js";
|
|
6
7
|
export { canvasToJpegBlob } from "./canvas-to-jpeg-blob.js";
|
|
7
8
|
export { reencodeJpeg } from "./reencode-jpeg.js";
|
|
8
9
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAKpD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MetaModelRead } from "./meta-read.js";
|
|
2
|
+
/**
|
|
3
|
+
* Read a `GET /_meta` document (effective canonical JSON) into a browser model.
|
|
4
|
+
*
|
|
5
|
+
* Accepts the raw response text or an already-parsed value. The document is
|
|
6
|
+
* assumed valid: the server loaded and validated it, and serving the EFFECTIVE
|
|
7
|
+
* form means every inherited member is already materialized here — so `extends`
|
|
8
|
+
* is deliberately never followed (see attrReaderFor).
|
|
9
|
+
*/
|
|
10
|
+
export declare function loadMetaModel(json: string | unknown): MetaModelRead;
|
|
11
|
+
//# sourceMappingURL=load-meta-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-meta-model.d.ts","sourceRoot":"","sources":["../src/load-meta-model.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACiC,aAAa,EACzD,MAAM,gBAAgB,CAAC;AA4GxB;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,aAAa,CAWnE"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// UI-2 — read the `GET /_meta` document into a model buildGrid can walk.
|
|
2
|
+
//
|
|
3
|
+
// Deliberately NOT the metadata loader: that package's root barrel exports
|
|
4
|
+
// MetaDataLoader, which transitively imports `node:url`, so it cannot be bundled
|
|
5
|
+
// for a browser (#287). This is a reader over an already-validated, already-
|
|
6
|
+
// resolved document — no validation, no registry, no super-resolution.
|
|
7
|
+
//
|
|
8
|
+
// #287: metamodel VALUES come from the browser-safe constants subpath, never the
|
|
9
|
+
// package root — see grid-from-metadata.ts for the full story. The type imports
|
|
10
|
+
// below are fine on the root: `import type` is erased at build time.
|
|
11
|
+
import { TYPE_FIELD, TYPE_LAYOUT, TYPE_VIEW, TYPE_OBJECT, ATTR_PREFIX, RESERVED_KEY_NAME, RESERVED_KEY_PACKAGE, RESERVED_KEY_CHILDREN, TYPE_SUBTYPE_SEPARATOR, } from "@metaobjectsdev/metadata/constants";
|
|
12
|
+
function isRecord(v) {
|
|
13
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
14
|
+
}
|
|
15
|
+
/** A canonical node is a single-key object: { "field.string": { ... } }. */
|
|
16
|
+
function readNode(value) {
|
|
17
|
+
if (!isRecord(value))
|
|
18
|
+
return undefined;
|
|
19
|
+
const keys = Object.keys(value);
|
|
20
|
+
if (keys.length !== 1)
|
|
21
|
+
return undefined;
|
|
22
|
+
const fused = keys[0];
|
|
23
|
+
const body = value[fused];
|
|
24
|
+
if (!isRecord(body))
|
|
25
|
+
return undefined;
|
|
26
|
+
const sep = fused.indexOf(TYPE_SUBTYPE_SEPARATOR);
|
|
27
|
+
if (sep < 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
return { type: fused.slice(0, sep), subType: fused.slice(sep + TYPE_SUBTYPE_SEPARATOR.length), body };
|
|
30
|
+
}
|
|
31
|
+
function childrenOf(node) {
|
|
32
|
+
const raw = node.body[RESERVED_KEY_CHILDREN];
|
|
33
|
+
if (!Array.isArray(raw))
|
|
34
|
+
return [];
|
|
35
|
+
const out = [];
|
|
36
|
+
for (const entry of raw) {
|
|
37
|
+
const child = readNode(entry);
|
|
38
|
+
if (child !== undefined)
|
|
39
|
+
out.push(child);
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Attributes are the `@`-prefixed body keys, read without the prefix.
|
|
45
|
+
*
|
|
46
|
+
* Every OTHER body key — `name`, `package`, `children`, and — the trap — `extends`
|
|
47
|
+
* — is structural and is excluded here by construction (it never starts with
|
|
48
|
+
* `ATTR_PREFIX`), not by naming and skipping it. The effective document still
|
|
49
|
+
* carries `extends`, with the members it once referenced already inlined
|
|
50
|
+
* elsewhere in the body; this reader never follows it, so nothing is resolved
|
|
51
|
+
* and nothing is double-counted.
|
|
52
|
+
*/
|
|
53
|
+
function attrReaderFor(node) {
|
|
54
|
+
const attrs = new Map();
|
|
55
|
+
for (const [key, value] of Object.entries(node.body)) {
|
|
56
|
+
if (key.startsWith(ATTR_PREFIX))
|
|
57
|
+
attrs.set(key.slice(ATTR_PREFIX.length), value);
|
|
58
|
+
}
|
|
59
|
+
return (name) => attrs.get(name);
|
|
60
|
+
}
|
|
61
|
+
function nameOf(node) {
|
|
62
|
+
const raw = node.body[RESERVED_KEY_NAME];
|
|
63
|
+
return typeof raw === "string" ? raw : "";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A node's OWN `package` body key, when present. Objects never inherit a file's
|
|
67
|
+
* default package (only certain root-level fields/validators do — see
|
|
68
|
+
* parser-core.ts), so this is undefined unless the node declared one itself,
|
|
69
|
+
* matching what `MetaData.package` reads server-side.
|
|
70
|
+
*/
|
|
71
|
+
function packageOf(node) {
|
|
72
|
+
const raw = node.body[RESERVED_KEY_PACKAGE];
|
|
73
|
+
return typeof raw === "string" ? raw : undefined;
|
|
74
|
+
}
|
|
75
|
+
function toView(node) {
|
|
76
|
+
return { subType: node.subType, attr: attrReaderFor(node) };
|
|
77
|
+
}
|
|
78
|
+
function toField(node) {
|
|
79
|
+
const views = childrenOf(node).filter((c) => c.type === TYPE_VIEW).map(toView);
|
|
80
|
+
return {
|
|
81
|
+
name: nameOf(node),
|
|
82
|
+
subType: node.subType,
|
|
83
|
+
attr: attrReaderFor(node),
|
|
84
|
+
views: () => views,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function toLayout(node) {
|
|
88
|
+
return { name: nameOf(node), subType: node.subType, attr: attrReaderFor(node) };
|
|
89
|
+
}
|
|
90
|
+
function toObject(node) {
|
|
91
|
+
const kids = childrenOf(node);
|
|
92
|
+
const fields = kids.filter((c) => c.type === TYPE_FIELD).map(toField);
|
|
93
|
+
const layouts = kids.filter((c) => c.type === TYPE_LAYOUT).map(toLayout);
|
|
94
|
+
const pkg = packageOf(node);
|
|
95
|
+
return {
|
|
96
|
+
name: nameOf(node),
|
|
97
|
+
// Conditional spread, not `package: pkg`: exactOptionalPropertyTypes
|
|
98
|
+
// distinguishes "absent" from "present and undefined", and MetaRead.package
|
|
99
|
+
// is meant to read as absent (not `undefined`-valued) for a node with no
|
|
100
|
+
// own package.
|
|
101
|
+
...(pkg !== undefined ? { package: pkg } : {}),
|
|
102
|
+
subType: node.subType,
|
|
103
|
+
attr: attrReaderFor(node),
|
|
104
|
+
fields: () => fields,
|
|
105
|
+
layouts: () => layouts,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Read a `GET /_meta` document (effective canonical JSON) into a browser model.
|
|
110
|
+
*
|
|
111
|
+
* Accepts the raw response text or an already-parsed value. The document is
|
|
112
|
+
* assumed valid: the server loaded and validated it, and serving the EFFECTIVE
|
|
113
|
+
* form means every inherited member is already materialized here — so `extends`
|
|
114
|
+
* is deliberately never followed (see attrReaderFor).
|
|
115
|
+
*/
|
|
116
|
+
export function loadMetaModel(json) {
|
|
117
|
+
const parsed = typeof json === "string" ? JSON.parse(json) : json;
|
|
118
|
+
const root = readNode(parsed);
|
|
119
|
+
const objects = root === undefined
|
|
120
|
+
? []
|
|
121
|
+
: childrenOf(root).filter((c) => c.type === TYPE_OBJECT).map(toObject);
|
|
122
|
+
const byName = new Map(objects.map((o) => [o.name, o]));
|
|
123
|
+
return {
|
|
124
|
+
objects: () => objects,
|
|
125
|
+
object: (name) => byName.get(name),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=load-meta-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-meta-model.js","sourceRoot":"","sources":["../src/load-meta-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** A metadata attribute reader. Returns `undefined` for an unset OR unregistered name. */
|
|
2
|
+
export type AttrReader = (name: string) => unknown;
|
|
3
|
+
export interface MetaViewRead {
|
|
4
|
+
readonly subType: string;
|
|
5
|
+
attr: AttrReader;
|
|
6
|
+
}
|
|
7
|
+
export interface MetaFieldRead {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly subType: string;
|
|
10
|
+
attr: AttrReader;
|
|
11
|
+
views(): MetaViewRead[];
|
|
12
|
+
}
|
|
13
|
+
export interface MetaLayoutRead {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly subType: string;
|
|
16
|
+
attr: AttrReader;
|
|
17
|
+
}
|
|
18
|
+
/** One object (entity / value / projection) as a runtime UI reads it. */
|
|
19
|
+
export interface MetaRead {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
/**
|
|
22
|
+
* The node's OWN package, when declared. Undefined for a node that inherits its
|
|
23
|
+
* package from the declaring file's default rather than declaring one itself —
|
|
24
|
+
* the same "own, not resolved" reading `MetaData.package` carries server-side
|
|
25
|
+
* (objects never auto-inherit a file's default package; see parser-core.ts).
|
|
26
|
+
*
|
|
27
|
+
* `object(name)` and the model's `byName` lookup key on the SHORT name only, so
|
|
28
|
+
* two same-named objects in different packages (a shape this repo models
|
|
29
|
+
* deliberately — see `fixtures/conformance/xpkg-collision-*`) are NOT
|
|
30
|
+
* disambiguated by `object()`. A caller that might see a cross-package
|
|
31
|
+
* collision should walk `objects()` and filter on `package` itself.
|
|
32
|
+
*/
|
|
33
|
+
readonly package?: string;
|
|
34
|
+
readonly subType: string;
|
|
35
|
+
attr: AttrReader;
|
|
36
|
+
fields(): MetaFieldRead[];
|
|
37
|
+
layouts(): MetaLayoutRead[];
|
|
38
|
+
}
|
|
39
|
+
/** The whole model, as returned by `loadMetaModel`. */
|
|
40
|
+
export interface MetaModelRead {
|
|
41
|
+
objects(): MetaRead[];
|
|
42
|
+
object(name: string): MetaRead | undefined;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=meta-read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-read.d.ts","sourceRoot":"","sources":["../src/meta-read.ts"],"names":[],"mappings":"AAWA,0FAA0F;AAC1F,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;AAEnD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,IAAI,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,yEAAyE;AACzE,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,IAAI,aAAa,EAAE,CAAC;IAC1B,OAAO,IAAI,cAAc,EAAE,CAAC;CAC7B;AAED,uDAAuD;AACvD,MAAM,WAAW,aAAa;IAC5B,OAAO,IAAI,QAAQ,EAAE,CAAC;IACtB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;CAC5C"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// The narrow, read-only metadata surface a runtime UI consumer needs.
|
|
2
|
+
//
|
|
3
|
+
// Declared here rather than imported from @metaobjectsdev/metadata because this
|
|
4
|
+
// package must satisfy it with a browser-built model too: the metadata package's
|
|
5
|
+
// root barrel exports MetaDataLoader, which transitively imports `node:url`, so
|
|
6
|
+
// it cannot be bundled for a browser (#287). A real MetaObject satisfies these
|
|
7
|
+
// interfaces structurally, so server-side callers are unaffected.
|
|
8
|
+
//
|
|
9
|
+
// ADR-0039: every accessor here is the RESOLVING one. `attr()` must answer with
|
|
10
|
+
// inherited values, and `views()`/`fields()` must include inherited members.
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=meta-read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-read.js","sourceRoot":"","sources":["../src/meta-read.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,gFAAgF;AAChF,iFAAiF;AACjF,gFAAgF;AAChF,+EAA+E;AAC/E,kEAAkE;AAClE,EAAE;AACF,gFAAgF;AAChF,6EAA6E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/runtime-web",
|
|
3
|
-
"version": "1.0.5-rc.
|
|
3
|
+
"version": "1.0.5-rc.6",
|
|
4
4
|
"description": "Pure framework-agnostic browser core for metaobjects: currency, filter URL serialization, fetcher contract types.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"access": "public"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@metaobjectsdev/metadata": "1.0.5-rc.
|
|
48
|
+
"@metaobjectsdev/metadata": "1.0.5-rc.6",
|
|
49
49
|
"qs": "^6.13.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
@@ -8,8 +8,12 @@
|
|
|
8
8
|
// Mirrors the derivation in codegen-ts-tanstack's columns-file (humanized
|
|
9
9
|
// headers, the field's view subtype as the cell-renderer hint, @columns / grid
|
|
10
10
|
// attrs from the dataGrid layout) so a runtime-built grid matches a generated
|
|
11
|
-
// one. Browser-safe:
|
|
12
|
-
|
|
11
|
+
// one. Browser-safe: types come from the local MetaRead surface (./meta-read.js)
|
|
12
|
+
// below, never from @metaobjectsdev/metadata's classes, and the only values
|
|
13
|
+
// imported from that package come from its browser-safe /constants subpath
|
|
14
|
+
// (#287 — the metadata root barrel cannot be bundled). A real MetaObject
|
|
15
|
+
// satisfies MetaRead structurally, so server callers are unaffected.
|
|
16
|
+
import type { MetaRead, MetaFieldRead, MetaViewRead } from "./meta-read.js";
|
|
13
17
|
// #287: metamodel VALUES come from the browser-safe constants subpath, never the package
|
|
14
18
|
// root. The root exports MetaDataLoader -> library-sources.ts -> `node:url`, so a single
|
|
15
19
|
// constant import from it made every browser bundle fail ("Browser polyfill for module
|
|
@@ -51,14 +55,14 @@ function humanize(s: string): string {
|
|
|
51
55
|
return s.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (c) => c.toUpperCase());
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
function firstView(field:
|
|
58
|
+
function firstView(field: MetaFieldRead): MetaViewRead | undefined {
|
|
55
59
|
// views(), not ownViews(): ADR-0039 — a field that `extends` an abstract parent
|
|
56
60
|
// inherits the parent's view, and an own-only read drops it, so the column would
|
|
57
61
|
// silently lose both its header and its renderer hint.
|
|
58
62
|
return field.views()[0];
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
function header(field:
|
|
65
|
+
function header(field: MetaFieldRead): string {
|
|
62
66
|
// DOC_ATTR_TITLE, not the literal "label". `@label` is registered by NO provider in
|
|
63
67
|
// any port — `@title` is the documentation commonAttr chartered as the display label
|
|
64
68
|
// — so this read was `undefined` for every field that ever set one, and every grid
|
|
@@ -70,7 +74,7 @@ function header(field: MetaField): string {
|
|
|
70
74
|
return typeof label === "string" ? label : humanize(field.name);
|
|
71
75
|
}
|
|
72
76
|
|
|
73
|
-
function viewKind(field:
|
|
77
|
+
function viewKind(field: MetaFieldRead): string {
|
|
74
78
|
return firstView(field)?.subType ?? field.subType;
|
|
75
79
|
}
|
|
76
80
|
|
|
@@ -93,7 +97,7 @@ function viewKind(field: MetaField): string {
|
|
|
93
97
|
*/
|
|
94
98
|
function gridDefaultSortOrder(
|
|
95
99
|
declaredOrder: unknown,
|
|
96
|
-
field:
|
|
100
|
+
field: MetaFieldRead | undefined,
|
|
97
101
|
): "asc" | "desc" {
|
|
98
102
|
if (declaredOrder === "asc" || declaredOrder === "desc") return declaredOrder;
|
|
99
103
|
const fromField = field?.attr(FIELD_ATTR_SORTABLE_DEFAULT_ORDER);
|
|
@@ -108,7 +112,7 @@ function gridDefaultSortOrder(
|
|
|
108
112
|
* Otherwise every field becomes a column with sensible defaults — so it works
|
|
109
113
|
* for ANY object, with or without a declared grid.
|
|
110
114
|
*/
|
|
111
|
-
export function buildGrid(meta:
|
|
115
|
+
export function buildGrid(meta: MetaRead, gridName?: string): MetaGrid {
|
|
112
116
|
const layouts = meta.layouts().filter((l) => l.subType === LAYOUT_SUBTYPE_DATA_GRID);
|
|
113
117
|
const layout = gridName ? layouts.find((l) => l.name === gridName) : layouts[0];
|
|
114
118
|
|
|
@@ -126,7 +130,7 @@ export function buildGrid(meta: MetaObject, gridName?: string): MetaGrid {
|
|
|
126
130
|
|
|
127
131
|
const columns: MetaColumn[] = names
|
|
128
132
|
.map((n) => fieldsByName.get(n))
|
|
129
|
-
.filter((f): f is
|
|
133
|
+
.filter((f): f is MetaFieldRead => f !== undefined)
|
|
130
134
|
.map((f) => ({ field: f.name, header: header(f), viewKind: viewKind(f) }));
|
|
131
135
|
|
|
132
136
|
const pageSizeAttr = layout?.attr(LAYOUT_DATA_GRID_ATTR_PAGE_SIZE);
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,10 @@ export type { EntityFetcher, GridConfig } from "./fetcher.js";
|
|
|
5
5
|
export { joinBaseUrl } from "./join-base-url.js";
|
|
6
6
|
export { buildGrid } from "./grid-from-metadata.js";
|
|
7
7
|
export type { MetaColumn, MetaGrid } from "./grid-from-metadata.js";
|
|
8
|
+
export type {
|
|
9
|
+
MetaRead, MetaFieldRead, MetaViewRead, MetaLayoutRead, MetaModelRead, AttrReader,
|
|
10
|
+
} from "./meta-read.js";
|
|
11
|
+
export { loadMetaModel } from "./load-meta-model.js";
|
|
8
12
|
export { canvasToJpegBlob } from "./canvas-to-jpeg-blob.js";
|
|
9
13
|
export { reencodeJpeg } from "./reencode-jpeg.js";
|
|
10
14
|
export type { ImageUploadAdapter, ImageMeta } from "./image-adapter.js";
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// UI-2 — read the `GET /_meta` document into a model buildGrid can walk.
|
|
2
|
+
//
|
|
3
|
+
// Deliberately NOT the metadata loader: that package's root barrel exports
|
|
4
|
+
// MetaDataLoader, which transitively imports `node:url`, so it cannot be bundled
|
|
5
|
+
// for a browser (#287). This is a reader over an already-validated, already-
|
|
6
|
+
// resolved document — no validation, no registry, no super-resolution.
|
|
7
|
+
//
|
|
8
|
+
// #287: metamodel VALUES come from the browser-safe constants subpath, never the
|
|
9
|
+
// package root — see grid-from-metadata.ts for the full story. The type imports
|
|
10
|
+
// below are fine on the root: `import type` is erased at build time.
|
|
11
|
+
import {
|
|
12
|
+
TYPE_FIELD, TYPE_LAYOUT, TYPE_VIEW, TYPE_OBJECT,
|
|
13
|
+
ATTR_PREFIX, RESERVED_KEY_NAME, RESERVED_KEY_PACKAGE, RESERVED_KEY_CHILDREN, TYPE_SUBTYPE_SEPARATOR,
|
|
14
|
+
} from "@metaobjectsdev/metadata/constants";
|
|
15
|
+
import type {
|
|
16
|
+
AttrReader, MetaFieldRead, MetaLayoutRead, MetaModelRead, MetaRead, MetaViewRead,
|
|
17
|
+
} from "./meta-read.js";
|
|
18
|
+
|
|
19
|
+
/** One parsed node: its fused "type.subType" key and its body. */
|
|
20
|
+
interface RawNode {
|
|
21
|
+
type: string;
|
|
22
|
+
subType: string;
|
|
23
|
+
body: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isRecord(v: unknown): v is Record<string, unknown> {
|
|
27
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** A canonical node is a single-key object: { "field.string": { ... } }. */
|
|
31
|
+
function readNode(value: unknown): RawNode | undefined {
|
|
32
|
+
if (!isRecord(value)) return undefined;
|
|
33
|
+
const keys = Object.keys(value);
|
|
34
|
+
if (keys.length !== 1) return undefined;
|
|
35
|
+
const fused = keys[0]!;
|
|
36
|
+
const body = value[fused];
|
|
37
|
+
if (!isRecord(body)) return undefined;
|
|
38
|
+
const sep = fused.indexOf(TYPE_SUBTYPE_SEPARATOR);
|
|
39
|
+
if (sep < 0) return undefined;
|
|
40
|
+
return { type: fused.slice(0, sep), subType: fused.slice(sep + TYPE_SUBTYPE_SEPARATOR.length), body };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function childrenOf(node: RawNode): RawNode[] {
|
|
44
|
+
const raw = node.body[RESERVED_KEY_CHILDREN];
|
|
45
|
+
if (!Array.isArray(raw)) return [];
|
|
46
|
+
const out: RawNode[] = [];
|
|
47
|
+
for (const entry of raw) {
|
|
48
|
+
const child = readNode(entry);
|
|
49
|
+
if (child !== undefined) out.push(child);
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Attributes are the `@`-prefixed body keys, read without the prefix.
|
|
56
|
+
*
|
|
57
|
+
* Every OTHER body key — `name`, `package`, `children`, and — the trap — `extends`
|
|
58
|
+
* — is structural and is excluded here by construction (it never starts with
|
|
59
|
+
* `ATTR_PREFIX`), not by naming and skipping it. The effective document still
|
|
60
|
+
* carries `extends`, with the members it once referenced already inlined
|
|
61
|
+
* elsewhere in the body; this reader never follows it, so nothing is resolved
|
|
62
|
+
* and nothing is double-counted.
|
|
63
|
+
*/
|
|
64
|
+
function attrReaderFor(node: RawNode): AttrReader {
|
|
65
|
+
const attrs = new Map<string, unknown>();
|
|
66
|
+
for (const [key, value] of Object.entries(node.body)) {
|
|
67
|
+
if (key.startsWith(ATTR_PREFIX)) attrs.set(key.slice(ATTR_PREFIX.length), value);
|
|
68
|
+
}
|
|
69
|
+
return (name: string) => attrs.get(name);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function nameOf(node: RawNode): string {
|
|
73
|
+
const raw = node.body[RESERVED_KEY_NAME];
|
|
74
|
+
return typeof raw === "string" ? raw : "";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A node's OWN `package` body key, when present. Objects never inherit a file's
|
|
79
|
+
* default package (only certain root-level fields/validators do — see
|
|
80
|
+
* parser-core.ts), so this is undefined unless the node declared one itself,
|
|
81
|
+
* matching what `MetaData.package` reads server-side.
|
|
82
|
+
*/
|
|
83
|
+
function packageOf(node: RawNode): string | undefined {
|
|
84
|
+
const raw = node.body[RESERVED_KEY_PACKAGE];
|
|
85
|
+
return typeof raw === "string" ? raw : undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function toView(node: RawNode): MetaViewRead {
|
|
89
|
+
return { subType: node.subType, attr: attrReaderFor(node) };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function toField(node: RawNode): MetaFieldRead {
|
|
93
|
+
const views = childrenOf(node).filter((c) => c.type === TYPE_VIEW).map(toView);
|
|
94
|
+
return {
|
|
95
|
+
name: nameOf(node),
|
|
96
|
+
subType: node.subType,
|
|
97
|
+
attr: attrReaderFor(node),
|
|
98
|
+
views: () => views,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function toLayout(node: RawNode): MetaLayoutRead {
|
|
103
|
+
return { name: nameOf(node), subType: node.subType, attr: attrReaderFor(node) };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function toObject(node: RawNode): MetaRead {
|
|
107
|
+
const kids = childrenOf(node);
|
|
108
|
+
const fields = kids.filter((c) => c.type === TYPE_FIELD).map(toField);
|
|
109
|
+
const layouts = kids.filter((c) => c.type === TYPE_LAYOUT).map(toLayout);
|
|
110
|
+
const pkg = packageOf(node);
|
|
111
|
+
return {
|
|
112
|
+
name: nameOf(node),
|
|
113
|
+
// Conditional spread, not `package: pkg`: exactOptionalPropertyTypes
|
|
114
|
+
// distinguishes "absent" from "present and undefined", and MetaRead.package
|
|
115
|
+
// is meant to read as absent (not `undefined`-valued) for a node with no
|
|
116
|
+
// own package.
|
|
117
|
+
...(pkg !== undefined ? { package: pkg } : {}),
|
|
118
|
+
subType: node.subType,
|
|
119
|
+
attr: attrReaderFor(node),
|
|
120
|
+
fields: () => fields,
|
|
121
|
+
layouts: () => layouts,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Read a `GET /_meta` document (effective canonical JSON) into a browser model.
|
|
127
|
+
*
|
|
128
|
+
* Accepts the raw response text or an already-parsed value. The document is
|
|
129
|
+
* assumed valid: the server loaded and validated it, and serving the EFFECTIVE
|
|
130
|
+
* form means every inherited member is already materialized here — so `extends`
|
|
131
|
+
* is deliberately never followed (see attrReaderFor).
|
|
132
|
+
*/
|
|
133
|
+
export function loadMetaModel(json: string | unknown): MetaModelRead {
|
|
134
|
+
const parsed: unknown = typeof json === "string" ? JSON.parse(json) : json;
|
|
135
|
+
const root = readNode(parsed);
|
|
136
|
+
const objects = root === undefined
|
|
137
|
+
? []
|
|
138
|
+
: childrenOf(root).filter((c) => c.type === TYPE_OBJECT).map(toObject);
|
|
139
|
+
const byName = new Map(objects.map((o) => [o.name, o] as const));
|
|
140
|
+
return {
|
|
141
|
+
objects: () => objects,
|
|
142
|
+
object: (name: string) => byName.get(name),
|
|
143
|
+
};
|
|
144
|
+
}
|
package/src/meta-read.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// The narrow, read-only metadata surface a runtime UI consumer needs.
|
|
2
|
+
//
|
|
3
|
+
// Declared here rather than imported from @metaobjectsdev/metadata because this
|
|
4
|
+
// package must satisfy it with a browser-built model too: the metadata package's
|
|
5
|
+
// root barrel exports MetaDataLoader, which transitively imports `node:url`, so
|
|
6
|
+
// it cannot be bundled for a browser (#287). A real MetaObject satisfies these
|
|
7
|
+
// interfaces structurally, so server-side callers are unaffected.
|
|
8
|
+
//
|
|
9
|
+
// ADR-0039: every accessor here is the RESOLVING one. `attr()` must answer with
|
|
10
|
+
// inherited values, and `views()`/`fields()` must include inherited members.
|
|
11
|
+
|
|
12
|
+
/** A metadata attribute reader. Returns `undefined` for an unset OR unregistered name. */
|
|
13
|
+
export type AttrReader = (name: string) => unknown;
|
|
14
|
+
|
|
15
|
+
export interface MetaViewRead {
|
|
16
|
+
readonly subType: string;
|
|
17
|
+
attr: AttrReader;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface MetaFieldRead {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly subType: string;
|
|
23
|
+
attr: AttrReader;
|
|
24
|
+
views(): MetaViewRead[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MetaLayoutRead {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly subType: string;
|
|
30
|
+
attr: AttrReader;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** One object (entity / value / projection) as a runtime UI reads it. */
|
|
34
|
+
export interface MetaRead {
|
|
35
|
+
readonly name: string;
|
|
36
|
+
/**
|
|
37
|
+
* The node's OWN package, when declared. Undefined for a node that inherits its
|
|
38
|
+
* package from the declaring file's default rather than declaring one itself —
|
|
39
|
+
* the same "own, not resolved" reading `MetaData.package` carries server-side
|
|
40
|
+
* (objects never auto-inherit a file's default package; see parser-core.ts).
|
|
41
|
+
*
|
|
42
|
+
* `object(name)` and the model's `byName` lookup key on the SHORT name only, so
|
|
43
|
+
* two same-named objects in different packages (a shape this repo models
|
|
44
|
+
* deliberately — see `fixtures/conformance/xpkg-collision-*`) are NOT
|
|
45
|
+
* disambiguated by `object()`. A caller that might see a cross-package
|
|
46
|
+
* collision should walk `objects()` and filter on `package` itself.
|
|
47
|
+
*/
|
|
48
|
+
readonly package?: string;
|
|
49
|
+
readonly subType: string;
|
|
50
|
+
attr: AttrReader;
|
|
51
|
+
fields(): MetaFieldRead[];
|
|
52
|
+
layouts(): MetaLayoutRead[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The whole model, as returned by `loadMetaModel`. */
|
|
56
|
+
export interface MetaModelRead {
|
|
57
|
+
objects(): MetaRead[];
|
|
58
|
+
object(name: string): MetaRead | undefined;
|
|
59
|
+
}
|