@peripheries/runtime 0.1.0 → 0.3.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/README.md +6 -1
- package/dist/index.js.map +1 -1
- package/dist/rank.js +86 -0
- package/dist/rank.js.map +1 -0
- package/dist/types/config.d.ts +5 -1
- package/dist/types/graph.d.ts +1 -1
- package/dist/types/rank.d.ts +129 -0
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -11,8 +11,13 @@ The contract between the host and federated component bundles
|
|
|
11
11
|
`DocumentCellProps`, `WriteResult`, `Size`, `CellContext`).
|
|
12
12
|
- **`config`**: the cell, page and structure configuration shapes.
|
|
13
13
|
- **`graph`**: `NodeRecord`, the state names, and the traversal configuration.
|
|
14
|
+
- **`@peripheries/runtime/rank`** (its own entry): sibling order on
|
|
15
|
+
`graph_node.rank` (`compareRank`, `sortByRank`, `rankBetween`,
|
|
16
|
+
`initialRanks`, `rankAfterLast`, `minimalReRank`, `isValidRank`), over
|
|
17
|
+
`fractional-indexing`. For the host, scripts and the agency; components get
|
|
18
|
+
sorted data and don't import it.
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
The main entry is types and plain data only. It replaces the component repos' scripts that copy
|
|
16
21
|
host files by relative path.
|
|
17
22
|
|
|
18
23
|
**Publishing:** on npm as `@peripheries/runtime`, built output only, with
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/config.ts","../src/graph.ts","../src/pinned.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/config.ts","../src/graph.ts","../src/pinned.ts"],"mappings":";AA0GA,IAAa,gBAAgB;CAAC;CAAY;CAAW;CAAY;AAAW;;;ACvG5E,IAAa,cAAc;CAAC;CAAQ;CAAO;AAAK;AAChD,IAAa,cAAc;CAAC;CAAQ;CAAW;CAAS;AAAW;AASnE,IAAa,cAAc,YAAY,SAAS,MAAM,YAAY,KAAK,MAAM,GAAG,EAAE,GAAG,GAAgB,CAAC;AACtG,IAAa,gBAA2B;AACxC,IAAa,eAAe,MAA+B,OAAO,MAAM,YAAa,YAAyB,SAAS,CAAC;AAiExH,IAAa,gBAAgB;CAAE,WAAW;CAAY,UAAU;CAAG,UAAU;CAAI,iBAAiB;AAAI;;;ACpEtG,IAAa,wBAAwB;AAGrC,IAAa,iBAAiB;AAC9B,IAAa,wBAAwB;AAErC,IAAa,eAAe;AAC5B,IAAa,iBAAiB;AAS9B,IAAa,kBAAkB;CAC7B,OAAO;EAAE,SAAS;EAAW,KAAK;CAAoB;CACtD,qBAAqB;EAAE,SAAS;EAAW,KAAK;CAAgC;CAChF,yBAAyB;EAAE,SAAS;EAAW,KAAK;CAAoC;CACxF,aAAa;EAAE,SAAS;EAAW,KAAK;CAAwB;CAChE,oBAAoB;EAAE,SAAS;EAAW,KAAK;CAA+B;CAC9E,SAAS;EAAE,SAAS;EAAU,KAAK;CAAsB;CAEzD,gBAAgB;EAAE,SAAS;EAAK,KAAK;CAA0B;AACjE;AAKA,IAAa,oBAAsD,EACjE,aAAa,eACf;AAMA,IAAa,sBAAmG,EAC9G,gBAAgB;CAAE,OAAO;EAAC;EAAY;EAAc;EAAY;CAAW;CAAG,YAAY;AAAM,EAClG;AAUA,SAAgB,iBAAmC;CACjD,MAAM,UAAkC,CAAC;CACzC,MAAM,OAA+B,CAAC;CACtC,KAAK,MAAM,CAAC,WAAW,QAAQ,OAAO,QAAQ,eAAe,GAAG;EAC9D,QAAQ,aAAa,IAAI;EACzB,KAAK,aAAa,IAAI;CACxB;CACA,KAAK,MAAM,CAAC,OAAO,WAAW,OAAO,QAAQ,iBAAiB,GAAG;EAC/D,QAAQ,SAAS,gBAAgB,OAAO,CAAC;EACzC,KAAK,SAAS,gBAAgB,OAAO,CAAC;CACxC;CACA,OAAO;EAAE,SAAS;EAAuB;EAAS;CAAK;AACzD"}
|
package/dist/rank.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { generateKeyBetween, generateNKeysBetween } from "fractional-indexing";
|
|
2
|
+
//#region packages/runtime/src/rank.ts
|
|
3
|
+
var RANK_COLUMN = "rank";
|
|
4
|
+
function compareRank(a, b) {
|
|
5
|
+
const ra = a.rank ?? "";
|
|
6
|
+
const rb = b.rank ?? "";
|
|
7
|
+
if (!ra && rb) return 1;
|
|
8
|
+
if (ra && !rb) return -1;
|
|
9
|
+
if (ra !== rb) return ra < rb ? -1 : 1;
|
|
10
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
11
|
+
}
|
|
12
|
+
function sortByRank(items, read) {
|
|
13
|
+
return items.slice().sort((x, y) => compareRank(read(x), read(y)));
|
|
14
|
+
}
|
|
15
|
+
function rankBetween(before, after, opts = {}) {
|
|
16
|
+
const key = generateKeyBetween(before ?? null, after ?? null);
|
|
17
|
+
if (!opts.jitter) return key;
|
|
18
|
+
const jittered = key + "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"[Math.floor(Math.random() * 61)];
|
|
19
|
+
return after && jittered >= after ? key : jittered;
|
|
20
|
+
}
|
|
21
|
+
function initialRanks(n) {
|
|
22
|
+
if (n <= 0) return [];
|
|
23
|
+
return generateNKeysBetween(null, null, n);
|
|
24
|
+
}
|
|
25
|
+
function rankAfterLast(existing) {
|
|
26
|
+
const keys = existing.filter((k) => !!k).sort();
|
|
27
|
+
return rankBetween(keys[keys.length - 1] ?? null, null);
|
|
28
|
+
}
|
|
29
|
+
function minimalReRank(orderedIds, currentRanks, opts = {}) {
|
|
30
|
+
const ranked = orderedIds.map((id) => ({
|
|
31
|
+
id,
|
|
32
|
+
rank: currentRanks[id] ?? null
|
|
33
|
+
}));
|
|
34
|
+
const n = ranked.length;
|
|
35
|
+
const best = new Array(n).fill(0);
|
|
36
|
+
const prev = new Array(n).fill(-1);
|
|
37
|
+
let bestEnd = -1;
|
|
38
|
+
for (let i = 0; i < n; i++) {
|
|
39
|
+
if (!ranked[i].rank) continue;
|
|
40
|
+
best[i] = 1;
|
|
41
|
+
for (let j = 0; j < i; j++) if (ranked[j].rank && ranked[j].rank < ranked[i].rank && best[j] + 1 > best[i]) {
|
|
42
|
+
best[i] = best[j] + 1;
|
|
43
|
+
prev[i] = j;
|
|
44
|
+
}
|
|
45
|
+
if (bestEnd === -1 || best[i] > best[bestEnd]) bestEnd = i;
|
|
46
|
+
}
|
|
47
|
+
const keep = new Set();
|
|
48
|
+
for (let i = bestEnd; i !== -1; i = prev[i]) keep.add(i);
|
|
49
|
+
const out = [];
|
|
50
|
+
const finalRanks = ranked.map((r, i) => keep.has(i) ? r.rank : null);
|
|
51
|
+
let i = 0;
|
|
52
|
+
while (i < n) {
|
|
53
|
+
if (keep.has(i)) {
|
|
54
|
+
i += 1;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
let j = i;
|
|
58
|
+
while (j < n && !keep.has(j)) j += 1;
|
|
59
|
+
const before = i > 0 ? finalRanks[i - 1] : null;
|
|
60
|
+
const after = j < n ? finalRanks[j] : null;
|
|
61
|
+
const runLength = j - i;
|
|
62
|
+
const keys = runLength === 1 ? [rankBetween(before, after, opts)] : generateNKeysBetween(before, after, runLength);
|
|
63
|
+
for (let k = 0; k < runLength; k++) {
|
|
64
|
+
finalRanks[i + k] = keys[k];
|
|
65
|
+
out.push({
|
|
66
|
+
id: ranked[i + k].id,
|
|
67
|
+
rank: keys[k]
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
i = j;
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
function isValidRank(key) {
|
|
75
|
+
if (typeof key !== "string" || key.length === 0) return false;
|
|
76
|
+
try {
|
|
77
|
+
generateKeyBetween(key, null);
|
|
78
|
+
return true;
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { RANK_COLUMN, compareRank, initialRanks, isValidRank, minimalReRank, rankAfterLast, rankBetween, sortByRank };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=rank.js.map
|
package/dist/rank.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rank.js","names":[],"sources":["../src/rank.ts"],"mappings":"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"}
|
package/dist/types/config.d.ts
CHANGED
|
@@ -97,7 +97,11 @@ export type PageCell = {
|
|
|
97
97
|
traversal?: TraversalConfig;
|
|
98
98
|
data?: CellData;
|
|
99
99
|
bundle_ref?: unknown;
|
|
100
|
-
|
|
100
|
+
/** name → the mutation it means: `'pages.update'`, or `{ registry, entityKey }`. */
|
|
101
|
+
actions?: Record<string, string | {
|
|
102
|
+
registry: string;
|
|
103
|
+
entityKey?: string;
|
|
104
|
+
}>;
|
|
101
105
|
selections?: SelectionDeclarations;
|
|
102
106
|
viewState?: ViewStateDeclarations;
|
|
103
107
|
live?: LiveDeclarations;
|
package/dist/types/graph.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export interface TraversalConfig {
|
|
|
57
57
|
direction?: 'incoming' | 'outgoing' | 'both';
|
|
58
58
|
/** Required: the edge types the walk follows. */
|
|
59
59
|
edgeTypes: string[];
|
|
60
|
-
/** The walk enters only these types, so nothing past another type is reached. */
|
|
60
|
+
/** The walk enters only these types, so nothing past another type is reached. Empty or absent: no filter. */
|
|
61
61
|
nodeTypes?: string[];
|
|
62
62
|
/** Default 3. */
|
|
63
63
|
maxDepth?: number;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a node's order among its siblings lives: the `graph_node.rank` column.
|
|
3
|
+
*
|
|
4
|
+
* ── Why the membership row ──────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* Order is GRAPH-SCOPED, so it belongs on the row representing a node's membership in
|
|
7
|
+
* a graph — not on the node. `graph_node` has no unique constraint on `node` (just a
|
|
8
|
+
* PK on `id` and three FKs), so the schema deliberately permits one node in several
|
|
9
|
+
* graphs; were membership 1:1, `node` would carry `graph` directly and this table
|
|
10
|
+
* would not exist. It happens to be 1:1 today (5,901 nodes, zero in more than one
|
|
11
|
+
* graph) but only because `duplicate_graph` clones fresh `node` rows per graph — a
|
|
12
|
+
* consequence of the clone strategy, not an invariant. "Order within what?" has no
|
|
13
|
+
* answer on a `node` row.
|
|
14
|
+
*
|
|
15
|
+
* Every other candidate was considered and rejected:
|
|
16
|
+
*
|
|
17
|
+
* `system_node_state` — a new NULL on the next append. Nothing carries state
|
|
18
|
+
* column or its existing metadata forward; each writer builds its own, and
|
|
19
|
+
* `metadata` jsonb `insertNodeState` names exactly seven columns.
|
|
20
|
+
* Measured on the worst node locally: 71 appends,
|
|
21
|
+
* 6 with metadata, 65 NULL, writers interleaved.
|
|
22
|
+
* DSNS `data.rank` Survives (writers spread `{ ...current }`), but
|
|
23
|
+
* state is append-only, so every drag appends a row,
|
|
24
|
+
* bumps the appraisal fingerprint, and tells the
|
|
25
|
+
* controller an objective changed because somebody
|
|
26
|
+
* reordered a list.
|
|
27
|
+
* `node.metadata.rank` Survives everything including the clone, but
|
|
28
|
+
* asserts that order is a property of the node.
|
|
29
|
+
* `graph_node.position.rank` Free and already cloned, but `position` is
|
|
30
|
+
* React-Flow canvas x/y. Nothing persists it today;
|
|
31
|
+
* the moment something does it will write `{x, y}`
|
|
32
|
+
* wholesale and silently delete every rank, with no
|
|
33
|
+
* reason to suspect list order was in there.
|
|
34
|
+
* `edge.metadata` / `graph_edge` The purest model — order among a parent's children
|
|
35
|
+
* is a property of that edge, and multi-parent is
|
|
36
|
+
* permitted. But the WRONG GRANULARITY for these
|
|
37
|
+
* lists: the backlog orders every feature in the
|
|
38
|
+
* walk filtered by kind, not one release's children,
|
|
39
|
+
* so two features under different releases would
|
|
40
|
+
* hold keys in incomparable namespaces. Cells also
|
|
41
|
+
* cannot read edge metadata today —
|
|
42
|
+
* `p_include_edge_metadata` defaults false and the
|
|
43
|
+
* host never passes it.
|
|
44
|
+
*
|
|
45
|
+
* One comparable namespace per graph, and each cell sorts within whatever filtered set
|
|
46
|
+
* it displays. If per-parent ordering is ever needed — an independently ordered
|
|
47
|
+
* "Scope" list on a release page — an edge-scoped key can be added alongside without
|
|
48
|
+
* migrating this one, because fractional keys only need to be comparable within the
|
|
49
|
+
* set being sorted.
|
|
50
|
+
*
|
|
51
|
+
* Rank is deliberately NOT state: nothing appraises it, nothing derives from it, no
|
|
52
|
+
* guard applies to it. It is presentation ordering on a mutable row.
|
|
53
|
+
*/
|
|
54
|
+
export declare const RANK_COLUMN = "rank";
|
|
55
|
+
/**
|
|
56
|
+
* Compare two rank keys, falling back to the id.
|
|
57
|
+
*
|
|
58
|
+
* The tiebreaker is not decoration: two offline clients inserting at the same
|
|
59
|
+
* position produce the same key, and an unranked item has no key at all. Without a
|
|
60
|
+
* deterministic second term the list order flickers between renders.
|
|
61
|
+
*
|
|
62
|
+
* Deliberately `<` / `>` and NOT `localeCompare` — see the collation note above.
|
|
63
|
+
* A locale comparator would interleave case and scramble the order.
|
|
64
|
+
*/
|
|
65
|
+
export declare function compareRank(a: {
|
|
66
|
+
rank?: string | null;
|
|
67
|
+
id: string;
|
|
68
|
+
}, b: {
|
|
69
|
+
rank?: string | null;
|
|
70
|
+
id: string;
|
|
71
|
+
}): number;
|
|
72
|
+
/** Sort into display order. Stable, and total even with missing or equal keys. */
|
|
73
|
+
export declare function sortByRank<T>(items: readonly T[], read: (item: T) => {
|
|
74
|
+
rank?: string | null;
|
|
75
|
+
id: string;
|
|
76
|
+
}): T[];
|
|
77
|
+
/**
|
|
78
|
+
* A key strictly between two neighbours. Either side may be null for "the end".
|
|
79
|
+
*
|
|
80
|
+
* `jitter` appends a random low-order character instead of taking the exact
|
|
81
|
+
* midpoint, which slows key growth under repeated inserts into one gap and makes
|
|
82
|
+
* two concurrent inserts less likely to collide. Off by default: it lengthens every
|
|
83
|
+
* key by one character, which is the wrong trade for the common single-drag case.
|
|
84
|
+
*/
|
|
85
|
+
export declare function rankBetween(before: string | null | undefined, after: string | null | undefined, opts?: {
|
|
86
|
+
jitter?: boolean;
|
|
87
|
+
}): string;
|
|
88
|
+
/** `n` fresh keys for a sibling set that has none — the initial backfill. */
|
|
89
|
+
export declare function initialRanks(n: number): string[];
|
|
90
|
+
/** A key that appends after everything in `existing`. Use when creating a record. */
|
|
91
|
+
export declare function rankAfterLast(existing: ReadonlyArray<string | null | undefined>): string;
|
|
92
|
+
export interface RankAssignment {
|
|
93
|
+
id: string;
|
|
94
|
+
rank: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Given the order the user wants and the ranks that exist, return ONLY the items
|
|
98
|
+
* whose key has to change.
|
|
99
|
+
*
|
|
100
|
+
* This is what keeps a reorder to one write. The naive implementation reassigns
|
|
101
|
+
* every sibling — N state rows per drag, which is exactly what D3d forbids and
|
|
102
|
+
* would also bump N appraisal fingerprints for a cosmetic change.
|
|
103
|
+
*
|
|
104
|
+
* Minimal by construction: it keeps the longest subsequence of items whose current
|
|
105
|
+
* keys are ALREADY in the desired relative order, and re-keys only the rest,
|
|
106
|
+
* generating each run of changed items between the two kept anchors that bracket
|
|
107
|
+
* it. For a single drag that is exactly one item. Items with no key are always
|
|
108
|
+
* assigned, which is how the lazy first-reorder backfill happens.
|
|
109
|
+
*
|
|
110
|
+
* Equal keys count as out of order, so a collision from two offline clients heals
|
|
111
|
+
* on the next reorder rather than persisting as a flickering list.
|
|
112
|
+
*/
|
|
113
|
+
export declare function minimalReRank(orderedIds: readonly string[], currentRanks: Readonly<Record<string, string | null | undefined>>, opts?: {
|
|
114
|
+
jitter?: boolean;
|
|
115
|
+
}): RankAssignment[];
|
|
116
|
+
/**
|
|
117
|
+
* Is this a well-formed key? For validating one that arrived from outside the host.
|
|
118
|
+
*
|
|
119
|
+
* DELEGATES to the generator rather than re-checking the invariant, and that is the
|
|
120
|
+
* point. The obvious implementation — reject anything ending in the lowest
|
|
121
|
+
* character — is WRONG, and wrong in a way that passes casual review: the invariant
|
|
122
|
+
* applies to the FRACTIONAL part, but the first character is a length marker, so
|
|
123
|
+
* "a0" is integer 0 with no fraction and is perfectly valid, while "a10" is integer
|
|
124
|
+
* 1 with a redundant trailing-zero fraction and is not. Telling those apart means
|
|
125
|
+
* decoding the length marker — exactly the fiddly edge case that argues for using a
|
|
126
|
+
* tested generator instead of hand-rolling one. `generateKeyBetween` already runs
|
|
127
|
+
* the real validator on its inputs, so asking it is both shorter and correct.
|
|
128
|
+
*/
|
|
129
|
+
export declare function isValidRank(key: unknown): key is string;
|
package/package.json
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peripheries/runtime",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The host's runtime contract for federated component bundles: pinned shared specifiers, the import map,
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "The host's runtime contract for federated component bundles: pinned shared specifiers, the import map, the types of what a cell component receives, and sibling order (`/rank`).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
9
|
"types": "./dist/types/index.d.ts",
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./rank": {
|
|
13
|
+
"types": "./dist/types/rank.d.ts",
|
|
14
|
+
"default": "./dist/rank.js"
|
|
11
15
|
}
|
|
12
16
|
},
|
|
13
17
|
"files": [
|
|
14
18
|
"dist",
|
|
15
19
|
"README.md"
|
|
16
20
|
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"fractional-indexing": "4.0.0"
|
|
23
|
+
},
|
|
17
24
|
"license": "UNLICENSED",
|
|
18
25
|
"repository": {
|
|
19
26
|
"type": "git",
|