@volter/world-core 3.0.20 → 3.0.22

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.
@@ -1398,6 +1398,71 @@
1398
1398
  },
1399
1399
  "serveExport": "createLinearTwinServer"
1400
1400
  },
1401
+ "linkedin": {
1402
+ "adoption": {
1403
+ "sdks": [
1404
+ "linkedin-api-client"
1405
+ ],
1406
+ "pypi": [
1407
+ "linkedin-api-client"
1408
+ ],
1409
+ "envStems": [
1410
+ "LINKEDIN"
1411
+ ]
1412
+ },
1413
+ "hosts": [
1414
+ {
1415
+ "host": "api.linkedin.com",
1416
+ "pathPattern": "^/(rest|v2)/|^/_twin/"
1417
+ },
1418
+ {
1419
+ "host": "www.linkedin.com",
1420
+ "pathPattern": "^/$|^/(dms-uploads|dms-mpf-uploads|feed|company|login|uas/login|m/logout|signup/cold-join|signup/api/cors/createAccount|checkpoint/lg/login-submit)(/|$)|^/_twin/"
1421
+ },
1422
+ {
1423
+ "host": "linkedin.com",
1424
+ "pathPattern": "^/$|^/(dms-uploads|dms-mpf-uploads|feed|company|login|uas/login|m/logout|signup/cold-join|signup/api/cors/createAccount|checkpoint/lg/login-submit)(/|$)|^/_twin/"
1425
+ },
1426
+ {
1427
+ "host": "media.licdn.com",
1428
+ "pathPattern": "^/dms/image/"
1429
+ },
1430
+ {
1431
+ "host": "dms.licdn.com",
1432
+ "pathPattern": "^/playlist/"
1433
+ }
1434
+ ],
1435
+ "archetype": "crud",
1436
+ "protocol": {
1437
+ "declared": "3",
1438
+ "major": 3,
1439
+ "standing": "current"
1440
+ },
1441
+ "transport": "rest",
1442
+ "resources": [
1443
+ "post",
1444
+ "image",
1445
+ "video",
1446
+ "organization",
1447
+ "member",
1448
+ "organization_acl",
1449
+ "developer_app",
1450
+ "_access_token",
1451
+ "_usage",
1452
+ "_upload",
1453
+ "_person",
1454
+ "_web_session"
1455
+ ],
1456
+ "endpointEnvNone": "LinkedIn's clients (linkedin-api-client, plain fetch) read no base-URL variable; the World claims api.linkedin.com and www.linkedin.com",
1457
+ "credentialDoor": {
1458
+ "path": "/_twin/app-credentials",
1459
+ "body": {},
1460
+ "fill": {
1461
+ "LINKEDIN_ACCESS_TOKEN": "access_token"
1462
+ }
1463
+ },
1464
+ "serveExport": "createLinkedinTwinServer"
1465
+ },
1401
1466
  "livekit": {
1402
1467
  "serverBacking": {
1403
1468
  "command": "livekit-server"
@@ -1960,7 +2025,7 @@
1960
2025
  "credentialDoor": {
1961
2026
  "path": "/_twin/app-credentials",
1962
2027
  "body": {
1963
- "username": "volter"
2028
+ "username": "FamouslyDefault"
1964
2029
  },
1965
2030
  "fill": {
1966
2031
  "REDDIT_ACCESS_TOKEN": "access_token"
package/dist/inject.cjs CHANGED
@@ -21,7 +21,13 @@
21
21
  // (stripe-node → https; @octokit → fetch; @slack/web-api & jira.js → axios →
22
22
  // http/https; @linear/sdk → fetch), so no per-SDK shim is needed.
23
23
  //
24
- // A client can still open its own sockets (npm `undici`'s Agent, which
24
+ // Attached to a World (VOLTER_WORLD), a request a bundled undici (wrangler's) sends to the
25
+ // World's own paths through undici's shared global dispatcher carries the World's key too:
26
+ // Object.defineProperty is watched for that global, and the dispatcher set there is stood
27
+ // in for by a view whose dispatch keys a hop whose origin and path are the World's
28
+ // (keyedView, below), sent with maxRedirections 0 so no follower below it takes the key on.
29
+ //
30
+ // A client can still open its own sockets (npm `undici`'s own Agent, which
25
31
  // @turbopuffer/turbopuffer uses under Node, honours neither patch nor HTTPS_PROXY).
26
32
  // `net.connect`/`tls.connect` are the seam every Node client ends at, so they carry
27
33
  // the backstop: a connect to a twinned vendor host went around the rewrite and is
@@ -435,6 +441,74 @@ function withWorldKey(args) {
435
441
  return out;
436
442
  }
437
443
 
444
+ // A client that bundles its own undici (wrangler) calls neither the global `fetch` nor http(s), so its requests to the
445
+ // World's own paths would arrive without the key. Every undici copy, Node's own included, keeps its dispatcher on one
446
+ // shared global (`Symbol.for('undici.globalDispatcher.<n>')`), set with Object.defineProperty when that copy loads. A
447
+ // dispatcher defined there is stood in for by a view of it whose `dispatch` adds the key to one hop, only when that
448
+ // hop's origin is the World's and its path one of the World's own, compared as strings (a path is never resolved as a
449
+ // URL, so `//other-host/…` names no other origin); the dispatcher itself is never changed, and restore() puts it back.
450
+ const UNDICI_DISPATCHERS = [Symbol.for('undici.globalDispatcher.1'), Symbol.for('undici.globalDispatcher.2')];
451
+ const KEYED_VIEWS = new WeakMap(); // view -> the dispatcher it stands in for
452
+ function keyedUndiciHeaders(headers, key) {
453
+ if (headers == null) return { 'x-twins-key': key };
454
+ if (Array.isArray(headers)) {
455
+ const flat = headers.length === 0 || !Array.isArray(headers[0]);
456
+ const names = flat ? headers.filter((_, i) => i % 2 === 0) : headers.map((h) => h[0]);
457
+ if (names.some((n) => String(n).toLowerCase() === 'x-twins-key')) return headers;
458
+ return flat ? [...headers, 'x-twins-key', key] : [...headers, ['x-twins-key', key]];
459
+ }
460
+ const entries = typeof headers[Symbol.iterator] === 'function' ? [...headers] : Object.entries(headers);
461
+ if (entries.some(([n]) => String(n).toLowerCase() === 'x-twins-key')) return headers;
462
+ return Object.assign(Object.fromEntries(entries), { 'x-twins-key': key });
463
+ }
464
+ /** A dispatch hop is the World's: its origin is the World's own and its path, as written, under the World's paths. */
465
+ function hopToWorld(opts) {
466
+ const base = worldBase();
467
+ if (!base || !base.path || !opts || opts.origin == null) return false;
468
+ let origin;
469
+ try { origin = new URL(String(opts.origin)).origin; } catch { return false; }
470
+ const at = String(opts.path == null ? '/' : opts.path);
471
+ return origin === base.origin && (at.startsWith(`${base.path}/`) || at.startsWith(`/-${base.path}/`));
472
+ }
473
+ function keyedView(dispatcher) {
474
+ if (!dispatcher || typeof dispatcher.dispatch !== 'function' || KEYED_VIEWS.has(dispatcher)) return dispatcher;
475
+ const view = new Proxy(dispatcher, {
476
+ get(target, name) {
477
+ if (name === 'dispatch') {
478
+ return function keyedDispatch(opts, handler) {
479
+ const key = ORIGINAL_DEFINE_PROPERTY ? twinsKey() : null;
480
+ // a hop the caller asked undici to follow is not keyed; a keyed hop is sent with maxRedirections 0, so no
481
+ // redirect follower below this view (a composed interceptor, a RetryAgent or proxy agent around one, undici
482
+ // 6's option or interceptors) carries its headers to the next origin: the redirect comes back to the caller
483
+ const keyed = key && !(opts && Number(opts.maxRedirections) > 0) && hopToWorld(opts);
484
+ return target.dispatch(keyed ? Object.assign({}, opts, { headers: keyedUndiciHeaders(opts.headers, key), maxRedirections: 0 }) : opts, handler);
485
+ };
486
+ }
487
+ const value = Reflect.get(target, name, target);
488
+ return typeof value === 'function' ? value.bind(target) : value;
489
+ },
490
+ });
491
+ KEYED_VIEWS.set(view, dispatcher);
492
+ return view;
493
+ }
494
+ let ORIGINAL_DEFINE_PROPERTY = null;
495
+ function keyUndiciDispatches() {
496
+ if (ORIGINAL_DEFINE_PROPERTY || !worldBase() || Object.defineProperty.name === 'keyingDefineProperty') return;
497
+ const define = ORIGINAL_DEFINE_PROPERTY = Object.defineProperty;
498
+ Object.defineProperty = function keyingDefineProperty(target, name, descriptor) {
499
+ if (ORIGINAL_DEFINE_PROPERTY && target === globalThis && UNDICI_DISPATCHERS.includes(name) && descriptor && 'value' in descriptor) descriptor = Object.assign({}, descriptor, { value: keyedView(descriptor.value) });
500
+ return define(target, name, descriptor);
501
+ };
502
+ for (const name of UNDICI_DISPATCHERS) if (globalThis[name]) globalThis[name] = keyedView(globalThis[name]);
503
+ }
504
+ function unkeyUndiciDispatches() {
505
+ if (!ORIGINAL_DEFINE_PROPERTY) return;
506
+ // only what this put in place is put back: a wrapper installed over it since stays
507
+ if (Object.defineProperty.name === 'keyingDefineProperty') Object.defineProperty = ORIGINAL_DEFINE_PROPERTY;
508
+ ORIGINAL_DEFINE_PROPERTY = null;
509
+ for (const name of UNDICI_DISPATCHERS) if (KEYED_VIEWS.has(globalThis[name])) globalThis[name] = KEYED_VIEWS.get(globalThis[name]);
510
+ }
511
+
438
512
  // S4 review F3: undici's default redirect-follow forwards CUSTOM headers across origins — a
439
513
  // twin (or the World) answering 30x to an external URL would carry x-twins-key to a REAL host. A
440
514
  // keyed fetch follows redirects itself: a hop that is a twin or the World re-enters the patched
@@ -979,6 +1053,7 @@ function install(mapOverride) {
979
1053
  tls.connect = patchedConnect(ORIGINALS.tlsConnect, true);
980
1054
  dns.lookup = patchedLookup(ORIGINALS.dnsLookup);
981
1055
  dns.promises.lookup = patchedPromisesLookup(ORIGINALS.dnsPromisesLookup);
1056
+ keyUndiciDispatches();
982
1057
  // gRPC (architecture, "Other wires": gRPC): an HTTP/2 session to a host a twin serves (grpc-js on google-gax: TLS to
983
1058
  // webrisk.googleapis.com) connects to that twin's origin in plain text, where the kernel's HTTP/2 server answers it;
984
1059
  // the client carries on unchanged. Any other authority is the real one's.
@@ -1139,6 +1214,7 @@ function restore() {
1139
1214
  dns.lookup = ORIGINALS.dnsLookup;
1140
1215
  dns.promises.lookup = ORIGINALS.dnsPromisesLookup;
1141
1216
  http2.connect = ORIGINALS.http2Connect;
1217
+ unkeyUndiciDispatches();
1142
1218
  installed = false;
1143
1219
  MAP = {};
1144
1220
  }
@@ -126,9 +126,10 @@ export type Workspace = {
126
126
  id: string;
127
127
  path: string;
128
128
  };
129
- /** A frame of the World's board (architecture, "The board"): one thing of the vendor's where a person sees it, as the
130
- * vendor's pack lays out its default board (`semantics/board.ts`), the editorial choice of which views of the vendor a
131
- * person keeps open (a Worker's site, a repository's page, a Jira board). `GET /twin/board` lists them. */
129
+ /** A page of the World's board (architecture, "The board"): a page there is to see at the vendor, a record's own (an
130
+ * account's profile, each of its posts, a site and each page it serves), as the vendor's pack lists them from its state
131
+ * (`semantics/board.ts`). Every field is a fact of the World's state: which of them a person looks at is the board's
132
+ * choice, never the pack's. `GET /twin/board` lists them. */
132
133
  export type BoardFrame = {
133
134
  /** stable within the vendor, whatever the World's ids: what a saved board names the frame by */
134
135
  id: string;
@@ -149,6 +150,11 @@ export type BoardFrame = {
149
150
  type: string;
150
151
  id: string;
151
152
  }>;
153
+ /** the id of the page it sits under at the vendor (a post under its author's profile, a site's page under its site):
154
+ * a board showing everything shows a page with no parent and opens the pages under it from there */
155
+ parent?: string;
156
+ /** the World's own: an account the World's applications or people act as, a page of one, a site the World serves */
157
+ ours?: boolean;
152
158
  /** set by the kernel, never the pack: whether a built screen of the vendor's (any of its lanes') draws the url */
153
159
  drawn?: boolean;
154
160
  };
@@ -1398,6 +1398,71 @@
1398
1398
  },
1399
1399
  "serveExport": "createLinearTwinServer"
1400
1400
  },
1401
+ "linkedin": {
1402
+ "adoption": {
1403
+ "sdks": [
1404
+ "linkedin-api-client"
1405
+ ],
1406
+ "pypi": [
1407
+ "linkedin-api-client"
1408
+ ],
1409
+ "envStems": [
1410
+ "LINKEDIN"
1411
+ ]
1412
+ },
1413
+ "hosts": [
1414
+ {
1415
+ "host": "api.linkedin.com",
1416
+ "pathPattern": "^/(rest|v2)/|^/_twin/"
1417
+ },
1418
+ {
1419
+ "host": "www.linkedin.com",
1420
+ "pathPattern": "^/$|^/(dms-uploads|dms-mpf-uploads|feed|company|login|uas/login|m/logout|signup/cold-join|signup/api/cors/createAccount|checkpoint/lg/login-submit)(/|$)|^/_twin/"
1421
+ },
1422
+ {
1423
+ "host": "linkedin.com",
1424
+ "pathPattern": "^/$|^/(dms-uploads|dms-mpf-uploads|feed|company|login|uas/login|m/logout|signup/cold-join|signup/api/cors/createAccount|checkpoint/lg/login-submit)(/|$)|^/_twin/"
1425
+ },
1426
+ {
1427
+ "host": "media.licdn.com",
1428
+ "pathPattern": "^/dms/image/"
1429
+ },
1430
+ {
1431
+ "host": "dms.licdn.com",
1432
+ "pathPattern": "^/playlist/"
1433
+ }
1434
+ ],
1435
+ "archetype": "crud",
1436
+ "protocol": {
1437
+ "declared": "3",
1438
+ "major": 3,
1439
+ "standing": "current"
1440
+ },
1441
+ "transport": "rest",
1442
+ "resources": [
1443
+ "post",
1444
+ "image",
1445
+ "video",
1446
+ "organization",
1447
+ "member",
1448
+ "organization_acl",
1449
+ "developer_app",
1450
+ "_access_token",
1451
+ "_usage",
1452
+ "_upload",
1453
+ "_person",
1454
+ "_web_session"
1455
+ ],
1456
+ "endpointEnvNone": "LinkedIn's clients (linkedin-api-client, plain fetch) read no base-URL variable; the World claims api.linkedin.com and www.linkedin.com",
1457
+ "credentialDoor": {
1458
+ "path": "/_twin/app-credentials",
1459
+ "body": {},
1460
+ "fill": {
1461
+ "LINKEDIN_ACCESS_TOKEN": "access_token"
1462
+ }
1463
+ },
1464
+ "serveExport": "createLinkedinTwinServer"
1465
+ },
1401
1466
  "livekit": {
1402
1467
  "serverBacking": {
1403
1468
  "command": "livekit-server"
@@ -1960,7 +2025,7 @@
1960
2025
  "credentialDoor": {
1961
2026
  "path": "/_twin/app-credentials",
1962
2027
  "body": {
1963
- "username": "volter"
2028
+ "username": "FamouslyDefault"
1964
2029
  },
1965
2030
  "fill": {
1966
2031
  "REDDIT_ACCESS_TOKEN": "access_token"
package/inject.cjs CHANGED
@@ -21,7 +21,13 @@
21
21
  // (stripe-node → https; @octokit → fetch; @slack/web-api & jira.js → axios →
22
22
  // http/https; @linear/sdk → fetch), so no per-SDK shim is needed.
23
23
  //
24
- // A client can still open its own sockets (npm `undici`'s Agent, which
24
+ // Attached to a World (VOLTER_WORLD), a request a bundled undici (wrangler's) sends to the
25
+ // World's own paths through undici's shared global dispatcher carries the World's key too:
26
+ // Object.defineProperty is watched for that global, and the dispatcher set there is stood
27
+ // in for by a view whose dispatch keys a hop whose origin and path are the World's
28
+ // (keyedView, below), sent with maxRedirections 0 so no follower below it takes the key on.
29
+ //
30
+ // A client can still open its own sockets (npm `undici`'s own Agent, which
25
31
  // @turbopuffer/turbopuffer uses under Node, honours neither patch nor HTTPS_PROXY).
26
32
  // `net.connect`/`tls.connect` are the seam every Node client ends at, so they carry
27
33
  // the backstop: a connect to a twinned vendor host went around the rewrite and is
@@ -435,6 +441,74 @@ function withWorldKey(args) {
435
441
  return out;
436
442
  }
437
443
 
444
+ // A client that bundles its own undici (wrangler) calls neither the global `fetch` nor http(s), so its requests to the
445
+ // World's own paths would arrive without the key. Every undici copy, Node's own included, keeps its dispatcher on one
446
+ // shared global (`Symbol.for('undici.globalDispatcher.<n>')`), set with Object.defineProperty when that copy loads. A
447
+ // dispatcher defined there is stood in for by a view of it whose `dispatch` adds the key to one hop, only when that
448
+ // hop's origin is the World's and its path one of the World's own, compared as strings (a path is never resolved as a
449
+ // URL, so `//other-host/…` names no other origin); the dispatcher itself is never changed, and restore() puts it back.
450
+ const UNDICI_DISPATCHERS = [Symbol.for('undici.globalDispatcher.1'), Symbol.for('undici.globalDispatcher.2')];
451
+ const KEYED_VIEWS = new WeakMap(); // view -> the dispatcher it stands in for
452
+ function keyedUndiciHeaders(headers, key) {
453
+ if (headers == null) return { 'x-twins-key': key };
454
+ if (Array.isArray(headers)) {
455
+ const flat = headers.length === 0 || !Array.isArray(headers[0]);
456
+ const names = flat ? headers.filter((_, i) => i % 2 === 0) : headers.map((h) => h[0]);
457
+ if (names.some((n) => String(n).toLowerCase() === 'x-twins-key')) return headers;
458
+ return flat ? [...headers, 'x-twins-key', key] : [...headers, ['x-twins-key', key]];
459
+ }
460
+ const entries = typeof headers[Symbol.iterator] === 'function' ? [...headers] : Object.entries(headers);
461
+ if (entries.some(([n]) => String(n).toLowerCase() === 'x-twins-key')) return headers;
462
+ return Object.assign(Object.fromEntries(entries), { 'x-twins-key': key });
463
+ }
464
+ /** A dispatch hop is the World's: its origin is the World's own and its path, as written, under the World's paths. */
465
+ function hopToWorld(opts) {
466
+ const base = worldBase();
467
+ if (!base || !base.path || !opts || opts.origin == null) return false;
468
+ let origin;
469
+ try { origin = new URL(String(opts.origin)).origin; } catch { return false; }
470
+ const at = String(opts.path == null ? '/' : opts.path);
471
+ return origin === base.origin && (at.startsWith(`${base.path}/`) || at.startsWith(`/-${base.path}/`));
472
+ }
473
+ function keyedView(dispatcher) {
474
+ if (!dispatcher || typeof dispatcher.dispatch !== 'function' || KEYED_VIEWS.has(dispatcher)) return dispatcher;
475
+ const view = new Proxy(dispatcher, {
476
+ get(target, name) {
477
+ if (name === 'dispatch') {
478
+ return function keyedDispatch(opts, handler) {
479
+ const key = ORIGINAL_DEFINE_PROPERTY ? twinsKey() : null;
480
+ // a hop the caller asked undici to follow is not keyed; a keyed hop is sent with maxRedirections 0, so no
481
+ // redirect follower below this view (a composed interceptor, a RetryAgent or proxy agent around one, undici
482
+ // 6's option or interceptors) carries its headers to the next origin: the redirect comes back to the caller
483
+ const keyed = key && !(opts && Number(opts.maxRedirections) > 0) && hopToWorld(opts);
484
+ return target.dispatch(keyed ? Object.assign({}, opts, { headers: keyedUndiciHeaders(opts.headers, key), maxRedirections: 0 }) : opts, handler);
485
+ };
486
+ }
487
+ const value = Reflect.get(target, name, target);
488
+ return typeof value === 'function' ? value.bind(target) : value;
489
+ },
490
+ });
491
+ KEYED_VIEWS.set(view, dispatcher);
492
+ return view;
493
+ }
494
+ let ORIGINAL_DEFINE_PROPERTY = null;
495
+ function keyUndiciDispatches() {
496
+ if (ORIGINAL_DEFINE_PROPERTY || !worldBase() || Object.defineProperty.name === 'keyingDefineProperty') return;
497
+ const define = ORIGINAL_DEFINE_PROPERTY = Object.defineProperty;
498
+ Object.defineProperty = function keyingDefineProperty(target, name, descriptor) {
499
+ if (ORIGINAL_DEFINE_PROPERTY && target === globalThis && UNDICI_DISPATCHERS.includes(name) && descriptor && 'value' in descriptor) descriptor = Object.assign({}, descriptor, { value: keyedView(descriptor.value) });
500
+ return define(target, name, descriptor);
501
+ };
502
+ for (const name of UNDICI_DISPATCHERS) if (globalThis[name]) globalThis[name] = keyedView(globalThis[name]);
503
+ }
504
+ function unkeyUndiciDispatches() {
505
+ if (!ORIGINAL_DEFINE_PROPERTY) return;
506
+ // only what this put in place is put back: a wrapper installed over it since stays
507
+ if (Object.defineProperty.name === 'keyingDefineProperty') Object.defineProperty = ORIGINAL_DEFINE_PROPERTY;
508
+ ORIGINAL_DEFINE_PROPERTY = null;
509
+ for (const name of UNDICI_DISPATCHERS) if (KEYED_VIEWS.has(globalThis[name])) globalThis[name] = KEYED_VIEWS.get(globalThis[name]);
510
+ }
511
+
438
512
  // S4 review F3: undici's default redirect-follow forwards CUSTOM headers across origins — a
439
513
  // twin (or the World) answering 30x to an external URL would carry x-twins-key to a REAL host. A
440
514
  // keyed fetch follows redirects itself: a hop that is a twin or the World re-enters the patched
@@ -979,6 +1053,7 @@ function install(mapOverride) {
979
1053
  tls.connect = patchedConnect(ORIGINALS.tlsConnect, true);
980
1054
  dns.lookup = patchedLookup(ORIGINALS.dnsLookup);
981
1055
  dns.promises.lookup = patchedPromisesLookup(ORIGINALS.dnsPromisesLookup);
1056
+ keyUndiciDispatches();
982
1057
  // gRPC (architecture, "Other wires": gRPC): an HTTP/2 session to a host a twin serves (grpc-js on google-gax: TLS to
983
1058
  // webrisk.googleapis.com) connects to that twin's origin in plain text, where the kernel's HTTP/2 server answers it;
984
1059
  // the client carries on unchanged. Any other authority is the real one's.
@@ -1139,6 +1214,7 @@ function restore() {
1139
1214
  dns.lookup = ORIGINALS.dnsLookup;
1140
1215
  dns.promises.lookup = ORIGINALS.dnsPromisesLookup;
1141
1216
  http2.connect = ORIGINALS.http2Connect;
1217
+ unkeyUndiciDispatches();
1142
1218
  installed = false;
1143
1219
  MAP = {};
1144
1220
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/world-core",
3
- "version": "3.0.20",
3
+ "version": "3.0.22",
4
4
  "description": "The kernel of Volter World: one log per twin, branches as pointers, checkpoints, the fold that keeps a twin current, the head that performs a write against the vendor, references, and the git plane. A twin package builds on it; the runtime serves it.",
5
5
  "keywords": [
6
6
  "twin",
package/src/derived.ts CHANGED
@@ -210,9 +210,10 @@ export type DerivedFetch = ((request: Request) => Promise<Response>) & {
210
210
  /** A workspace screen a pack draws, as `GET /twin` lists it: its id and path (Viewing a World). */
211
211
  export type Workspace = { id: string; path: string };
212
212
 
213
- /** A frame of the World's board (architecture, "The board"): one thing of the vendor's where a person sees it, as the
214
- * vendor's pack lays out its default board (`semantics/board.ts`), the editorial choice of which views of the vendor a
215
- * person keeps open (a Worker's site, a repository's page, a Jira board). `GET /twin/board` lists them. */
213
+ /** A page of the World's board (architecture, "The board"): a page there is to see at the vendor, a record's own (an
214
+ * account's profile, each of its posts, a site and each page it serves), as the vendor's pack lists them from its state
215
+ * (`semantics/board.ts`). Every field is a fact of the World's state: which of them a person looks at is the board's
216
+ * choice, never the pack's. `GET /twin/board` lists them. */
216
217
  export type BoardFrame = {
217
218
  /** stable within the vendor, whatever the World's ids: what a saved board names the frame by */
218
219
  id: string;
@@ -226,6 +227,11 @@ export type BoardFrame = {
226
227
  url: string;
227
228
  /** the records whose changes are this frame's changes */
228
229
  covers: Array<{ type: string; id: string }>;
230
+ /** the id of the page it sits under at the vendor (a post under its author's profile, a site's page under its site):
231
+ * a board showing everything shows a page with no parent and opens the pages under it from there */
232
+ parent?: string;
233
+ /** the World's own: an account the World's applications or people act as, a page of one, a site the World serves */
234
+ ours?: boolean;
229
235
  /** set by the kernel, never the pack: whether a built screen of the vendor's (any of its lanes') draws the url */
230
236
  drawn?: boolean;
231
237
  };