@nervur-org/dock 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/README.md +7 -4
  2. package/api/route.ts +4 -72
  3. package/beings/avatar.ts +11 -7
  4. package/beings/carry.ts +129 -116
  5. package/beings/clock.ts +11 -7
  6. package/beings/desk.ts +7 -6
  7. package/beings/doorbell.ts +36 -7
  8. package/beings/index.ts +17 -4
  9. package/beings/join.ts +7 -4
  10. package/beings/link.ts +1 -1
  11. package/beings/porter.ts +5 -1
  12. package/beings/presence.ts +15 -5
  13. package/beings/setup.ts +27 -21
  14. package/beings/user.ts +26 -17
  15. package/beings/way.ts +1 -1
  16. package/{dock → box}/agents.ts +37 -44
  17. package/{dock → box}/apns.ts +4 -4
  18. package/box/box.ts +31 -0
  19. package/box/contract.ts +41 -0
  20. package/box/dialers.ts +88 -0
  21. package/box/dock.ts +148 -0
  22. package/box/envoy.ts +96 -0
  23. package/box/faculty.ts +111 -0
  24. package/{dock → box}/fcm.ts +1 -1
  25. package/box/grants.ts +90 -0
  26. package/box/http.ts +93 -0
  27. package/{dock → box}/http2.ts +7 -7
  28. package/box/index.ts +201 -0
  29. package/box/join.ts +78 -0
  30. package/box/local.ts +37 -0
  31. package/{dock → box}/location.ts +15 -3
  32. package/{dock → box}/notifications.ts +17 -5
  33. package/box/push.ts +85 -0
  34. package/box/ring.ts +85 -0
  35. package/{dock → box}/service.ts +2 -2
  36. package/box/timer.ts +150 -0
  37. package/box/tree.ts +22 -0
  38. package/{dock → box}/webpush.ts +2 -2
  39. package/cli/client.ts +1 -1
  40. package/cli/daemon.ts +101 -103
  41. package/cli/estate.ts +2 -3
  42. package/cli/nervur.ts +44 -25
  43. package/cli/stand.ts +1 -1
  44. package/dist/api/route.d.ts +1 -2
  45. package/dist/api/route.js +4 -76
  46. package/dist/beings/avatar.d.ts +4 -3
  47. package/dist/beings/avatar.js +9 -6
  48. package/dist/beings/carry.d.ts +10 -12
  49. package/dist/beings/carry.js +119 -127
  50. package/dist/beings/clock.js +10 -7
  51. package/dist/beings/desk.js +8 -7
  52. package/dist/beings/doorbell.d.ts +3 -1
  53. package/dist/beings/doorbell.js +37 -4
  54. package/dist/beings/index.d.ts +24 -4
  55. package/dist/beings/index.js +15 -4
  56. package/dist/beings/join.d.ts +1 -0
  57. package/dist/beings/join.js +6 -4
  58. package/dist/beings/link.d.ts +0 -1
  59. package/dist/beings/link.js +1 -1
  60. package/dist/beings/porter.js +5 -1
  61. package/dist/beings/presence.d.ts +1 -0
  62. package/dist/beings/presence.js +12 -3
  63. package/dist/beings/setup.d.ts +5 -2
  64. package/dist/beings/setup.js +16 -15
  65. package/dist/beings/user.d.ts +4 -3
  66. package/dist/beings/user.js +18 -11
  67. package/dist/{dock → box}/agents.d.ts +7 -4
  68. package/dist/{dock → box}/agents.js +30 -37
  69. package/dist/{dock → box}/apns.d.ts +1 -1
  70. package/dist/{dock → box}/apns.js +3 -3
  71. package/dist/box/box.d.ts +9 -0
  72. package/dist/box/box.js +18 -0
  73. package/dist/box/contract.d.ts +6 -0
  74. package/dist/box/contract.js +35 -0
  75. package/dist/{dock → box}/dialers.d.ts +7 -4
  76. package/dist/box/dialers.js +76 -0
  77. package/dist/box/dock.d.ts +182 -0
  78. package/dist/box/dock.js +128 -0
  79. package/dist/box/envoy.d.ts +44 -0
  80. package/dist/box/envoy.js +75 -0
  81. package/dist/box/faculty.d.ts +68 -0
  82. package/dist/box/faculty.js +97 -0
  83. package/dist/{dock → box}/fcm.d.ts +1 -1
  84. package/dist/{dock → box}/grants.d.ts +4 -3
  85. package/dist/box/grants.js +78 -0
  86. package/dist/box/http.d.ts +26 -0
  87. package/dist/box/http.js +89 -0
  88. package/dist/{dock → box}/http2.d.ts +1 -1
  89. package/dist/{dock → box}/http2.js +6 -6
  90. package/dist/box/index.d.ts +46 -0
  91. package/dist/box/index.js +179 -0
  92. package/dist/box/join.d.ts +19 -0
  93. package/dist/box/join.js +67 -0
  94. package/dist/{dock → box}/local.d.ts +3 -2
  95. package/dist/box/local.js +27 -0
  96. package/dist/{dock → box}/location.d.ts +3 -0
  97. package/dist/box/location.js +21 -0
  98. package/dist/{dock → box}/notifications.d.ts +3 -0
  99. package/dist/{dock → box}/notifications.js +14 -4
  100. package/dist/{dock → box}/push.d.ts +12 -18
  101. package/dist/box/push.js +83 -0
  102. package/dist/box/ring.d.ts +18 -0
  103. package/dist/box/ring.js +55 -0
  104. package/dist/{dock → box}/service.d.ts +1 -1
  105. package/dist/{dock → box}/service.js +1 -1
  106. package/dist/{dock → box}/timer.d.ts +2 -0
  107. package/dist/box/timer.js +127 -0
  108. package/dist/box/tree.d.ts +5 -0
  109. package/dist/box/tree.js +14 -0
  110. package/dist/{dock → box}/webpush.d.ts +1 -1
  111. package/dist/cli/client.d.ts +1 -0
  112. package/dist/cli/daemon.d.ts +2 -8
  113. package/dist/cli/daemon.js +102 -107
  114. package/dist/cli/estate.js +2 -3
  115. package/dist/cli/nervur.js +44 -24
  116. package/dist/cli/stand.d.ts +0 -14
  117. package/dist/cli/stand.js +1 -1
  118. package/dist/harbor/browser.d.ts +8 -5
  119. package/dist/harbor/browser.js +20 -21
  120. package/dist/harbor/capacitor.d.ts +3 -3
  121. package/dist/harbor/capacitor.js +9 -8
  122. package/dist/harbor/disk.d.ts +9 -9
  123. package/dist/harbor/disk.js +56 -51
  124. package/dist/harbor/edge/alarm.d.ts +4 -2
  125. package/dist/harbor/edge/alarm.js +9 -19
  126. package/dist/harbor/edge/edge.d.ts +7 -6
  127. package/dist/harbor/edge/edge.js +58 -53
  128. package/dist/harbor/edge/exercise.js +4 -4
  129. package/dist/harbor/edge/given.d.ts +0 -1
  130. package/dist/harbor/edge/given.js +0 -29
  131. package/dist/harbor/edge/storage.d.ts +0 -2
  132. package/dist/harbor/edge/storage.js +2 -2
  133. package/dist/harbor/http.js +4 -2
  134. package/dist/harbor/sealed.d.ts +0 -5
  135. package/dist/harbor/sealed.js +0 -39
  136. package/dist/harbor/tauri.d.ts +2 -2
  137. package/dist/harbor/tauri.js +13 -10
  138. package/dist/human/app.d.ts +1 -1
  139. package/dist/human/app.js +10 -12
  140. package/dist/human/door.d.ts +0 -1
  141. package/dist/human/door.js +1 -1
  142. package/dist/human/html.js +1 -1
  143. package/dist/human/move.js +1 -1
  144. package/dist/human/tab.bundle.txt +3629 -2824
  145. package/dist/human/wake.d.ts +5 -2
  146. package/dist/human/wake.js +23 -14
  147. package/dist/human/web.d.ts +0 -1
  148. package/dist/human/web.js +3 -3
  149. package/dist/human/worker.bundle.txt +2945 -2118
  150. package/dist/human/worlds.js +3 -3
  151. package/dist/mcp/agent.d.ts +0 -1
  152. package/dist/mcp/agent.js +1 -1
  153. package/dist/mcp/allow.js +3 -2
  154. package/dist/mcp/http.d.ts +0 -1
  155. package/dist/mcp/http.js +1 -1
  156. package/dist/mcp/oauth.d.ts +3 -4
  157. package/dist/mcp/oauth.js +4 -4
  158. package/dist/mcp/route.d.ts +1 -1
  159. package/dist/mcp/runner.d.ts +0 -1
  160. package/dist/mcp/runner.js +1 -1
  161. package/dist/mcp/server.d.ts +0 -1
  162. package/dist/mcp/server.js +1 -1
  163. package/harbor/browser.ts +22 -24
  164. package/harbor/capacitor.ts +9 -8
  165. package/harbor/disk.ts +53 -48
  166. package/harbor/edge/alarm.ts +15 -12
  167. package/harbor/edge/edge.ts +61 -55
  168. package/harbor/edge/exercise.ts +4 -4
  169. package/harbor/edge/given.ts +3 -33
  170. package/harbor/edge/storage.ts +2 -2
  171. package/harbor/http.ts +5 -3
  172. package/harbor/sealed.ts +0 -35
  173. package/harbor/tauri.ts +13 -10
  174. package/human/app.ts +12 -13
  175. package/human/door.ts +1 -1
  176. package/human/html.ts +1 -1
  177. package/human/local.ts +1 -1
  178. package/human/move.ts +1 -1
  179. package/human/tab.bundle.txt +3629 -2824
  180. package/human/wake.ts +36 -16
  181. package/human/web.ts +4 -4
  182. package/human/worker.bundle.txt +2945 -2118
  183. package/human/worlds.ts +3 -3
  184. package/mcp/agent.ts +1 -1
  185. package/mcp/allow.ts +2 -1
  186. package/mcp/http.ts +1 -1
  187. package/mcp/oauth.ts +4 -4
  188. package/mcp/route.ts +1 -1
  189. package/mcp/runner.ts +1 -1
  190. package/mcp/server.ts +1 -1
  191. package/package.json +9 -8
  192. package/beings/envoy.ts +0 -218
  193. package/dist/beings/envoy.d.ts +0 -54
  194. package/dist/beings/envoy.js +0 -211
  195. package/dist/dock/console.d.ts +0 -6
  196. package/dist/dock/console.js +0 -20
  197. package/dist/dock/dialers.js +0 -75
  198. package/dist/dock/faculty.d.ts +0 -24
  199. package/dist/dock/faculty.js +0 -142
  200. package/dist/dock/grants.js +0 -99
  201. package/dist/dock/index.d.ts +0 -41
  202. package/dist/dock/index.js +0 -113
  203. package/dist/dock/local.js +0 -44
  204. package/dist/dock/location.js +0 -11
  205. package/dist/dock/push.js +0 -132
  206. package/dist/dock/timer.js +0 -149
  207. package/dock/console.ts +0 -23
  208. package/dock/dialers.ts +0 -85
  209. package/dock/faculty.ts +0 -148
  210. package/dock/grants.ts +0 -111
  211. package/dock/index.ts +0 -133
  212. package/dock/local.ts +0 -48
  213. package/dock/push.ts +0 -161
  214. package/dock/timer.ts +0 -174
  215. /package/dist/{dock → box}/fcm.js +0 -0
  216. /package/dist/{dock → box}/webpush.js +0 -0
@@ -1,47 +1,46 @@
1
- import { User, Desk, Avatar, Presence, Courier } from '../beings/index.js';
1
+ import { BEINGS } from '../beings/index.js';
2
2
  import { Harbor, dial } from 'nervur/harbor';
3
- import { FACULTY_CLASSES, settled, stand } from '../dock/index.js';
3
+ import { BOX_SEED, harborageOver, keyed, settled, standHarbor } from '../box/index.js';
4
4
  import { Idb } from './idb.js';
5
- export const BUILT_IN = { User, Desk, Avatar, Presence, Courier, ...FACULTY_CLASSES };
6
5
  export class BrowserHarbor extends Harbor {
7
6
  name;
8
7
  dialers = [];
9
8
  #locked;
10
9
  #release;
11
- // `lend` is what this box lends the beings of the wards it hosts: a name
12
- // to an invitation on a being of its own ward. `stand` builds one; a bare
13
- // tab hands none and every name answers null.
14
- constructor(store = 'nervur', classes = {}, lend) {
10
+ constructor(store = 'nervur', classes = {}) {
15
11
  const s = typeof store === 'string' ? new Idb(store) : store;
16
- super(s, async () => ({ ...BUILT_IN, ...classes }), lend);
12
+ super(s, async () => keyed({ ...BEINGS, classes }));
17
13
  this.name = typeof store === 'string' ? store : 'nervur';
18
14
  this.#locked = typeof store === 'string';
19
15
  }
20
- // What this box lends, by name. A bare tab says nothing, stands no ward of
21
- // its own and lends nothing.
22
- lent;
23
- // What this box lends is said before it boots, and standing the ward is
24
- // part of booting: a being born in any other ward lends from her first
25
- // line, and a ground that could not reach it yet would leave her holding
26
- // nothing until her next birth.
27
- async stand(lent) {
28
- this.lent = lent;
16
+ // The classes this terrain brings to its own ward, as a tree. A bare tab
17
+ // brings none, stands no ward of its own and lends nothing.
18
+ faculties;
19
+ // What this harbor is to its faculties, up once it has booted.
20
+ box = harborageOver(() => this.wards.get(BOX_SEED)?.save() ?? Promise.resolve());
21
+ // What this box brings is said before it boots, and standing its own ward
22
+ // is act one of booting: a being born in any other ward takes the box at
23
+ // her ward's birth, and a box that was not standing yet would leave that
24
+ // ward holding nothing until its next boot.
25
+ async stand(faculties) {
26
+ this.faculties = faculties;
29
27
  if (this.wards.size > 0)
30
- this.lend = await stand(this, lent);
28
+ await standHarbor(this, faculties, this.box);
31
29
  }
32
30
  // Take the lock and boot every ward kept here. A lock already held is
33
31
  // another tab's harbor over this seed, and this one refuses to boot.
34
32
  async boot() {
35
33
  if (this.#locked)
36
34
  await this.#lease();
37
- if (this.lent)
38
- this.lend = await stand(this, this.lent);
35
+ if (this.faculties)
36
+ await standHarbor(this, this.faculties, this.box);
39
37
  await super.boot();
40
38
  // Every being who takes what this box lends does it at her birth, and
41
39
  // birth is synchronous while taking a standing is not. A box is up when
42
40
  // they have landed, not before, here as on a daemon: a page painted
43
41
  // ahead of them shows a device that can do nothing.
44
- await settled();
42
+ await settled(this);
43
+ await this.box.raise();
45
44
  }
46
45
  // Hold one socket to a world's quo. route.
47
46
  dial(url) {
@@ -1,19 +1,19 @@
1
1
  import { Directory } from '@capacitor/filesystem';
2
2
  import type { BeingClass, JsonObject } from 'nervur';
3
- import { Location, Notifications, type Token, type Where } from '../dock/index.ts';
3
+ import { Location, Notifications, type Token, type Where } from '../box/index.ts';
4
4
  import { BrowserHarbor } from './browser.ts';
5
5
  import { Sealed, type Files, type Secrets } from './sealed.ts';
6
6
  export declare class PhoneLocation extends Location {
7
+ static stands(): boolean;
7
8
  where(): Promise<Where | JsonObject>;
8
9
  }
9
10
  export declare class PhoneNotifications extends Notifications {
11
+ static stands(): boolean;
10
12
  notify(args: JsonObject): Promise<JsonObject>;
11
13
  protected plant(pair: {
12
14
  secret: Uint8Array;
13
15
  }): Promise<void>;
14
16
  }
15
- export declare const PHONE_FACULTIES: Record<string, BeingClass>;
16
- export declare const phoneLent: Record<string, string>;
17
17
  export declare function phoneEar(sandbox?: boolean, within?: number, named?: {
18
18
  bell?: string;
19
19
  app?: string;
@@ -16,7 +16,7 @@ import { SecureStorage, KeychainAccess } from '@aparajita/capacitor-secure-stora
16
16
  import { Geolocation } from '@capacitor/geolocation';
17
17
  import { LocalNotifications } from '@capacitor/local-notifications';
18
18
  import { PushNotifications } from '@capacitor/push-notifications';
19
- import { LOCATION, NOTIFICATIONS, Location, Notifications } from '../dock/index.js';
19
+ import { Location, Notifications } from '../box/index.js';
20
20
  import { BrowserHarbor } from './browser.js';
21
21
  import { Sealed } from './sealed.js';
22
22
  // The phone's faculties, as its plugins give them: the two defaults extended,
@@ -31,6 +31,9 @@ const BANNER = 'banner';
31
31
  const asked = (what) => new Error(`the human is being asked to allow ${what} on this device; ask again`);
32
32
  const refused = (what) => new Error(`${what} is not allowed on this device`);
33
33
  export class PhoneLocation extends Location {
34
+ static stands() {
35
+ return Capacitor.isPluginAvailable('Geolocation');
36
+ }
34
37
  async where() {
35
38
  const { location } = await Geolocation.checkPermissions();
36
39
  if (location === 'prompt' || location === 'prompt-with-rationale') {
@@ -44,6 +47,9 @@ export class PhoneLocation extends Location {
44
47
  }
45
48
  }
46
49
  export class PhoneNotifications extends Notifications {
50
+ static stands() {
51
+ return Capacitor.isPluginAvailable('LocalNotifications');
52
+ }
47
53
  async notify(args) {
48
54
  const title = typeof args.title === 'string' ? args.title : '';
49
55
  const body = typeof args.body === 'string' ? args.body : '';
@@ -98,11 +104,6 @@ export class PhoneNotifications extends Notifications {
98
104
  await PushNotifications.requestPermissions().catch(() => ({ receive: 'denied' }));
99
105
  }
100
106
  }
101
- // What a phone lends: the two faculties above under the names every box
102
- // lends them under, and nothing a phone has not. The harbor holds these
103
- // class bodies for its own ward, and boots one being of each in it.
104
- export const PHONE_FACULTIES = { Location: PhoneLocation, Notifications: PhoneNotifications };
105
- export const phoneLent = { [LOCATION]: 'Location', [NOTIFICATIONS]: 'Notifications' };
106
107
  // The phone's ear, as `human/app.ts` asks for one: the token this device is
107
108
  // reachable at while it sleeps, or null.
108
109
  //
@@ -159,8 +160,8 @@ export function phoneEar(sandbox = false, within = 10_000, named = {}) {
159
160
  // told `wake` every time the app comes to the foreground, since a phone
160
161
  // asleep loses its sockets silently.
161
162
  export async function nativeHarbor(name = 'nervur', classes = {}) {
162
- const h = new BrowserHarbor(await Native.open(name), { ...PHONE_FACULTIES, ...classes });
163
- await h.stand(phoneLent);
163
+ const h = new BrowserHarbor(await Native.open(name), classes);
164
+ await h.stand({ harbor: { PhoneLocation, PhoneNotifications } });
164
165
  await h.boot();
165
166
  await App.addListener('appStateChange', ({ isActive }) => {
166
167
  if (isActive)
@@ -1,21 +1,21 @@
1
- import type { BeingClass } from 'nervur';
2
- import { Harbor, type Hosted, type Lend } from 'nervur/harbor';
1
+ import { Harbor, type Hosted } from 'nervur/harbor';
2
+ import { type Tree } from '../box/index.ts';
3
3
  export type { Hosted, Bound } from 'nervur/harbor';
4
4
  export type { WardRecord } from 'nervur/harbor';
5
- export declare const BUILT_IN: Record<string, BeingClass>;
6
5
  export declare function defaultWard(marked: string[], hosted: string[]): string;
7
6
  export declare class DiskHarbor extends Harbor {
8
7
  #private;
9
8
  readonly dir: string;
10
9
  defaultWard: string;
11
- static setDefault(dir: string, name: string): Promise<void>;
10
+ static say(dir: string, method: 'domain' | 'apps' | 'default', args: Record<string, unknown>): Promise<Record<string, unknown>>;
12
11
  static setShown(dir: string, name: string, shown: boolean): Promise<void>;
13
12
  static isShown(dir: string, name: string): boolean;
14
- static marked(dir: string): Promise<string[]>;
15
- static defaultOf(dir: string): Promise<string>;
16
- constructor(dir: string, lend?: Lend, own?: Record<string, BeingClass>);
17
- lent: Record<string, string> | undefined;
18
- static init(dir: string, name?: string, user?: string, cls?: string, mark?: boolean, shown?: boolean): Promise<{
13
+ constructor(dir: string);
14
+ faculties: Tree | undefined;
15
+ readonly box: import("../box/box.ts").Harborage & {
16
+ raise(): Promise<void>;
17
+ };
18
+ static init(dir: string, name?: string, user?: string, cls?: string, shown?: boolean): Promise<{
19
19
  dir: string;
20
20
  name: string;
21
21
  pk: string;
@@ -10,9 +10,8 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
10
10
  // The node daemon's harbor: the core over a folder on disk. The store is
11
11
  // `files.ts`, one folder per ward; the class bodies come from a module the
12
12
  // ward record names, loaded from the folder; the lease is a pid file. The
13
- // estate's own beings, the user being, the front desk and the avatar, are
14
- // held by every harbor here: they are what every side needs, and a folder's
15
- // class source adds to them.
13
+ // dock's beings are held by every harbor here under `beings/`, and a
14
+ // folder's class source adds to them under the folder it sits in.
16
15
  //
17
16
  // <dir>/
18
17
  // lease pid of the one process that holds this harbor
@@ -24,21 +23,20 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
24
23
  // reach.json the directory's hints
25
24
  import { mkdir, readFile, writeFile, unlink, stat } from 'node:fs/promises';
26
25
  import { existsSync } from 'node:fs';
27
- import { join, resolve, isAbsolute } from 'node:path';
26
+ import { dirname, join, resolve, isAbsolute, relative } from 'node:path';
28
27
  import { pathToFileURL } from 'node:url';
29
28
  import { register } from 'node:module';
30
29
  import process from 'node:process';
31
- import { User, Desk, Avatar, Presence, Clock, Courier } from '../beings/index.js';
30
+ import { BEINGS } from '../beings/index.js';
32
31
  import { setup } from '../beings/setup.js';
33
- import { Harbor, DEFAULT_CODE } from 'nervur/harbor';
34
- import { FACULTY_CLASSES, settled, stand } from '../dock/index.js';
32
+ import { Harbor, DEFAULT_CODE, BOX_SEED } from 'nervur/harbor';
33
+ import { BOX, harborageOver, keyed, settled, standHarbor } from '../box/index.js';
35
34
  import { Files } from './files.js';
36
- export const BUILT_IN = { User, Desk, Avatar, Presence, Clock, Courier, ...FACULTY_CLASSES };
37
35
  // The default ward, the one a caller means when naming none: the one marked,
38
36
  // or the one ward hosted when there is exactly one, which is every harbor an
39
37
  // adopter stands with one `nervur init`. Several wards and none marked is
40
38
  // refused by name, since agents and a door would otherwise hang off a
41
- // guess. The dock ward is furniture, never a candidate.
39
+ // guess. The box ward is furniture, never a candidate.
42
40
  export function defaultWard(marked, hosted) {
43
41
  if (marked.length > 1)
44
42
  throw new Error(`this device marks ${marked.join(', ')} as default, and one at most may be`);
@@ -48,7 +46,7 @@ export function defaultWard(marked, hosted) {
48
46
  throw new Error(`this device says its default ward is ${one}, and hosts no such ward`);
49
47
  return one;
50
48
  }
51
- const [first, ...more] = hosted.filter((n) => n !== 'dock');
49
+ const [first, ...more] = hosted.filter((n) => n !== BOX_SEED);
52
50
  if (first !== undefined && more.length === 0)
53
51
  return first;
54
52
  const names = first === undefined ? [] : [first, ...more];
@@ -96,20 +94,32 @@ export class DiskHarbor extends Harbor {
96
94
  // ward and nothing in Quo knows of it: a harbor is not a being and has no
97
95
  // self to be, so it can only point at one of its wards. Its agents boot
98
96
  // there, and its door answers for it when a caller names no ward. The one
99
- // ward it hosts when there is one, the one marked otherwise, resolved by
100
- // the daemon at boot; empty until then.
97
+ // ward it hosts when there is one, the one the Dock's cells say otherwise,
98
+ // placed by the root's `default` ask and resolved by the daemon at boot;
99
+ // empty until then.
101
100
  defaultWard = '';
102
- // The mark is a file in the ward's own folder, `wards/<name>/default`, so
103
- // it moves with the ward and nothing beside the wards says it. One at most:
104
- // marking one takes the mark off every other.
105
- static async setDefault(dir, name) {
101
+ // The root saying what the box is, with no daemon over the folder: the
102
+ // Dock's own asks, `domain`, `apps` and `default`, on the box ward stood
103
+ // alone, which the lease refuses while a daemon serves. The daemon reads
104
+ // what she keeps at its next boot, so a domain said is stop, say, start,
105
+ // like an init.
106
+ static async say(dir, method, args) {
106
107
  const h = new DiskHarbor(dir);
107
- const names = await h.store.list();
108
- if (!names.includes(name))
109
- throw new Error(`no ward ${name} in ${h.dir}`);
110
- for (const n of names)
111
- await unlink(join(h.dir, 'wards', n, 'default')).catch(() => { });
112
- await writeFile(join(h.dir, 'wards', name, 'default'), '');
108
+ h.faculties = {};
109
+ await h.boot();
110
+ try {
111
+ const ward = h.wards.get(BOX_SEED);
112
+ if (!ward)
113
+ throw new Error('this box stands no ward of its own');
114
+ const out = (await ward.ask('ask', { being: BOX, method, args }));
115
+ if (typeof out.error === 'string')
116
+ throw new Error(out.error);
117
+ await ward.save();
118
+ return out;
119
+ }
120
+ finally {
121
+ await h.close();
122
+ }
113
123
  }
114
124
  // Whether a ward is shown at the web route: listed at its root and its
115
125
  // public being's page painted for a stranger. Hidden, it is not listed
@@ -130,51 +140,44 @@ export class DiskHarbor extends Harbor {
130
140
  static isShown(dir, name) {
131
141
  return existsSync(join(resolve(dir), 'wards', name, 'shown'));
132
142
  }
133
- static async marked(dir) {
134
- const h = new DiskHarbor(dir);
135
- return (await h.store.list()).filter((n) => existsSync(join(h.dir, 'wards', n, 'default')));
136
- }
137
- // The default ward read from the folder alone, for a command with no
138
- // daemon in front of it and no `--ward` given.
139
- static async defaultOf(dir) {
140
- const h = new DiskHarbor(dir);
141
- return defaultWard(await DiskHarbor.marked(dir), await h.store.list());
142
- }
143
143
  #held = false;
144
- // `lend` is what this box lends the beings of every ward here: a name to
145
- // an invitation on a being of its own ward, which `stand` puts up. A
146
- // folder booted by a command lends nothing.
147
- // `own` are this terrain's own class bodies, the faculty subclasses a box
148
- // has: they stand in front of the built-ins, so a daemon that can ring
149
- // hands its own `Push` and the dock ward boots that one.
150
- constructor(dir, lend, own = {}) {
144
+ constructor(dir) {
151
145
  const abs = resolve(dir);
152
146
  // The key the environment gives, the edge's name for it: from the
153
147
  // Keychain through the app that spawned this daemon, or nothing on a
154
148
  // droplet, whose folder stays plain under its file modes.
155
- super(new Files(abs, process.env.NERVUR_SEED_KEY), async (rec) => ({ ...BUILT_IN, ...own, ...(await loadClasses(isAbsolute(rec.code) ? rec.code : join(abs, rec.code))) }), lend);
149
+ // A class is keyed where it lives: the dock's under `beings/`, the
150
+ // folder's under the folder its source sits in, `classes/Corpus`.
151
+ const lookup = async (rec) => {
152
+ const path = isAbsolute(rec.code) ? rec.code : join(abs, rec.code);
153
+ return keyed({ ...BEINGS, [relative(abs, dirname(path))]: await loadClasses(path) });
154
+ };
155
+ super(new Files(abs, process.env.NERVUR_SEED_KEY), lookup);
156
156
  this.dir = abs;
157
157
  }
158
- // What this box lends, by name. Set before `boot` and read there; a box
159
- // that says nothing stands no ward of its own and lends nothing.
160
- lent;
158
+ // The classes this terrain brings to its own ward, as a tree. Set before
159
+ // `boot` and read there; a box that brings none stands no ward of its own
160
+ // and lends nothing. The folder's own classes join them under the folder
161
+ // they live in, so an estate's faculty stands beside the terrain's.
162
+ faculties;
163
+ // What this harbor is to its faculties. The terrain raises it once the box
164
+ // is up, which on a daemon is after its routes are mounted.
165
+ box = harborageOver(() => this.wards.get(BOX_SEED)?.save() ?? Promise.resolve());
161
166
  // Create a harbor folder with one ward, and the root's setup in it: the
162
167
  // front desk, public, and one user being, with the desk holding a standing
163
168
  // to her placed by the root's knock. Boots the ward once, with no lease, to
164
169
  // do that and to learn its pk, and writes the record. Refuses a folder that
165
170
  // has one.
166
- static async init(dir, name = 'main', user = 'me', cls = 'User', mark = false, shown = false) {
171
+ static async init(dir, name = 'main', user = 'me', cls, shown = false) {
167
172
  const h = new DiskHarbor(dir);
168
173
  if ((await h.store.list()).includes(name))
169
174
  throw new Error(`ward ${name} already exists in ${h.dir}`);
170
175
  await mkdir(join(h.dir, 'classes'), { recursive: true });
171
176
  const classes = join(h.dir, DEFAULT_CODE);
172
177
  if (!existsSync(classes))
173
- await writeFile(classes, '// The classes this harbor holds beside the built-in ones. Export each one by name.\nexport {};\n');
178
+ await writeFile(classes, "// The classes this harbor holds beside the dock's beings. Each export is classes/<its name>.\nexport {};\n");
174
179
  const hosted = await h.create(name, user);
175
- await setup(hosted, user, cls);
176
- if (mark)
177
- await DiskHarbor.setDefault(h.dir, name);
180
+ await setup(hosted, user, cls ? { home: cls } : {});
178
181
  if (shown)
179
182
  await DiskHarbor.setShown(h.dir, name, true);
180
183
  return { dir: h.dir, name, pk: hosted.pk, user };
@@ -188,13 +191,15 @@ export class DiskHarbor extends Harbor {
188
191
  // it yet would leave her holding nothing until her next birth.
189
192
  async boot() {
190
193
  await this.#lease();
191
- if (this.lent)
192
- this.lend = await stand(this, this.lent);
194
+ if (this.faculties) {
195
+ const tree = { ...this.faculties, [dirname(DEFAULT_CODE)]: await loadClasses(join(this.dir, DEFAULT_CODE)) };
196
+ await standHarbor(this, tree, this.box);
197
+ }
193
198
  await super.boot();
194
199
  // Every being who takes what this box lends does it at her birth, and
195
200
  // birth is synchronous while taking a standing is not. A box is up when
196
201
  // they have landed, not before.
197
- await settled();
202
+ await settled(this);
198
203
  }
199
204
  // Every ward's writing finished, then the lease. A ward says it wrote and
200
205
  // the harbor writes it after, so a close that only dropped the lease would
@@ -1,10 +1,12 @@
1
- import { Timer } from '../../dock/index.ts';
1
+ import type { Stance } from 'nervur';
2
+ import { Timer, type Harborage } from '../../box/index.ts';
2
3
  import type { Storage } from './platform.d.ts';
3
4
  export type Alarms = (at: number | null) => Promise<void>;
4
5
  export declare const alarmsIn: (storage: Storage) => Alarms;
5
6
  export declare class Alarm extends Timer {
6
7
  #private;
7
- alarms: Alarms | undefined;
8
+ readonly alarms: Alarms | undefined;
9
+ constructor(stance: Stance, box?: Harborage, alarms?: Alarms);
8
10
  get setting(): Promise<void>;
9
11
  protected arm(when: number | null): void;
10
12
  }
@@ -1,17 +1,4 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The box's timer on this terrain. One method of hers knows a platform, the
3
- // arming, and this replaces it: the object's own storage alarm in place of a
4
- // `setTimeout` that would die with the request. Everything else about her is
5
- // the same class, the same door and the same suite, because when a box wakes
6
- // is a terrain's answer and what it does when it wakes is not.
7
- //
8
- // One harbor is one Durable Object, and every ward of it lives in that
9
- // object, so the alarm wakes the whole harbor and a tick from her to a home's
10
- // Clock is in-process here as it is on a disk. Only the object may set its
11
- // own alarm, so the two calls are handed to her after the box ward stands,
12
- // the way the grants are handed their save: a faculty is built by her ward
13
- // with a stance and nothing else.
14
- import { Timer } from '../../dock/index.js';
1
+ import { Timer } from '../../box/index.js';
15
2
  export const alarmsIn = (storage) => async (at) => {
16
3
  if (at === null)
17
4
  await storage.deleteAlarm();
@@ -19,10 +6,14 @@ export const alarmsIn = (storage) => async (at) => {
19
6
  await storage.setAlarm(at);
20
7
  };
21
8
  export class Alarm extends Timer {
22
- // Handed in after the box ward stands. A timer holding none arms nothing
23
- // and wakes nobody, which is a harbor standing no box ward.
9
+ // Handed at birth by the object that brings her. A timer holding none arms
10
+ // nothing and wakes nobody.
24
11
  alarms;
25
12
  #setting = Promise.resolve();
13
+ constructor(stance, box, alarms) {
14
+ super(stance, box);
15
+ this.alarms = alarms;
16
+ }
26
17
  // The arming written, for the device's own code and a suite: on this
27
18
  // terrain an arming is a write to storage, and a reader of the alarm waits
28
19
  // for it. It crosses no door.
@@ -30,9 +21,8 @@ export class Alarm extends Timer {
30
21
  return this.#setting;
31
22
  }
32
23
  arm(when) {
33
- // A faculty arms inside whatever ask learned the moment, and a write is
34
- // not something that ask can answer for, so this is left to finish on its
35
- // own. An alarm the platform refused is armed again by the next round.
24
+ // An arming is left to finish on its own; an alarm the platform refused is
25
+ // armed again by the next round.
36
26
  this.#setting = (this.alarms?.(when) ?? Promise.resolve()).catch(() => { });
37
27
  }
38
28
  }
@@ -1,13 +1,11 @@
1
- import type { BeingClass } from 'nervur';
2
1
  import { Harbor, Socket as Held } from 'nervur/harbor';
3
2
  import { type Folder } from '../../human/web.ts';
4
- import { Grants } from '../../dock/grants.ts';
3
+ import { Grants, type Harborage, type Tree } from '../../box/index.ts';
5
4
  import { type Routes } from '../../mcp/route.ts';
6
5
  import { Exchange } from '../../mcp/allow.ts';
7
6
  import { DurableStorage, danceInStorage, wearInStorage } from './storage.ts';
8
7
  import { type Deployed, type Given, type SettingsStore } from './given.ts';
9
8
  import type { State, Env } from './platform.d.ts';
10
- export declare const BUILT_IN: Record<string, BeingClass>;
11
9
  export declare class EdgeHarbor extends Harbor {
12
10
  #private;
13
11
  readonly sockets: Set<Held>;
@@ -20,18 +18,21 @@ export declare class EdgeHarbor extends Harbor {
20
18
  static open(state: State, env: Env, deployed?: Deployed): Promise<EdgeHarbor>;
21
19
  constructor(store: DurableStorage, root: string | undefined, deployed?: Deployed);
22
20
  given(): Promise<Given>;
23
- lent: Record<string, string> | undefined;
21
+ faculties: Tree | undefined;
22
+ readonly box: Harborage & {
23
+ raise(): Promise<void>;
24
+ };
24
25
  ready(): Promise<void>;
25
26
  boot(): Promise<void>;
26
27
  rang(): Promise<void>;
27
- get grants(): Grants | undefined;
28
+ grants(): Promise<Grants | undefined>;
28
29
  web(request: Request, path: string, base: string): Promise<Response>;
29
30
  routes(base: string): Promise<Routes & {
30
31
  quo: string;
31
32
  api: string;
32
33
  }>;
33
34
  here(): string;
34
- api(request: Request, path: string, base: string): Promise<Response>;
35
+ api(request: Request, path: string): Promise<Response>;
35
36
  mcp(request: Request, path: string, base: string): Promise<Response>;
36
37
  wellKnown(request: Request, path: string, base: string): Promise<Response>;
37
38
  exchange(base: string): Promise<Exchange | undefined>;