@quo-systems/quo 0.2.5 → 0.2.7

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/SPEC.md CHANGED
@@ -121,8 +121,8 @@ in the stance at birth, once, for life
121
121
  out her answer(asker, method?, args?) -> object | silence the one method the ward calls
122
122
  ```
123
123
 
124
- The stance is her cells, her standings to ask, and the calls to invite,
125
- knock, take and remove. The ward builds it and hands it to the harbor's
124
+ The stance is her cells, her standings to ask, the calls to invite,
125
+ knock, take and remove, and boot. The ward builds it and hands it to the harbor's
126
126
  instantiate with a class name, and receives her. No method is the empty ask,
127
127
  and what she answers to it is her blueprint.
128
128
 
@@ -142,6 +142,8 @@ stance
142
142
  standings[id].ask(method?, args?, wanted?) -> object | silence | word
143
143
  standings.remove(id) -> nothing
144
144
 
145
+ boot(class, key) -> key | null a new being of her ward, by class name
146
+
145
147
  wanted = { time? } what this one ask may spend, in milliseconds. optional,
146
148
  and so is saying anything at all
147
149
  ```
@@ -164,6 +166,15 @@ Return table.
164
166
  | ask(m, a, w) | object | she answered |
165
167
  | | silence | she chose to say nothing, or bytes came back that are not Quo's |
166
168
  | | word | why not: removed, absent, repeated, threw, unreached, late, dropped |
169
+ | boot(class, key) | key | she exists from now, with fresh cells, and a restart finds her |
170
+ | | null | the key is already booted, the harbor holds no such class, or the class threw at birth |
171
+
172
+ A being may make, and only the owner reaches into another. Boot touches
173
+ nobody: the being made has empty cells and no relation, hers included, until
174
+ one is invited and taken like any other. What the owner has that she does
175
+ not is every ask that reaches into a being from outside: public, invite and
176
+ knock for her, remove, unboot. Who may ask her to boot is her gate, as who
177
+ may ask her anything is.
167
178
 
168
179
  Ask, knock, invite and take are awaitable. Async where the language has it,
169
180
  blocking where it does not. Every ask she makes is a new call.
@@ -967,9 +978,10 @@ become one.
967
978
  The root is not an occupant. It holds no heir, no key, no invitation. The
968
979
  ward names every arrival on the ask `{ id: 'OWNER' }`, the third asker of the
969
980
  ward-to-being edge, and answers it as such; an owner at the door arrives as
970
- her id. What an owner can do is exactly what a being cannot: boot, mark the
971
- public being, place a relation into a being of the ward, take one out of
972
- her, and take her out of the ward. The root's asks carry no allowance of
981
+ her id. What an owner can do is reach into a being from outside, which no
982
+ being can: mark the public being, place a relation into a being of the
983
+ ward, take one out of her, and take her out of the ward. Boot it shares
984
+ with every being of the ward. The root's asks carry no allowance of
973
985
  their own. The ward is a being to her owner: the empty ask is her describe,
974
986
  and each ask in it carries a description and an input naming its fields, as
975
987
  a being's asks do, so that a side renders the owner's asks the way it
@@ -1044,9 +1056,10 @@ The ward:
1044
1056
  - Derives its pk from its seed and names itself outward by that and nothing
1045
1057
  else. Mints every other key from the ground's random, one per side of one
1046
1058
  relation.
1047
- - Instantiates beings when the owner asks, by class name. It builds the
1048
- stance, names the class, and receives the object from the harbor's
1049
- instantiate call. It never sees a class body, its own included.
1059
+ - Instantiates beings when the owner or a being of the ward asks, by class
1060
+ name. It builds the stance, names the class, and receives the object from
1061
+ the harbor's instantiate call. It never sees a class body, its own
1062
+ included.
1050
1063
  - Keeps, per being, the cells, the standings and the occupants, in the
1051
1064
  partition, and outside her cells the bind table. Never reads notes.
1052
1065
  - Mints a heir for every id a being mints, keeps its pk beside the id, and
@@ -16,6 +16,7 @@ export declare class Being {
16
16
  invite(id: string): Promise<Invitation | null>;
17
17
  knock(invitation: Invitation, method?: string, args?: JsonObject, wanted?: Wanted): Promise<Answer>;
18
18
  take(id: string, invitation: Invitation): Promise<string | null>;
19
+ boot(className: string, key: string): string | null;
19
20
  occupant(asker: Asker): OccupantRecord | undefined;
20
21
  describe(asker: Asker): Blueprint;
21
22
  answer(asker: Asker, method?: string, args?: JsonObject): Promise<Reply>;
@@ -1,5 +1,5 @@
1
1
  // Names a subclass may not use for an ask, because they are the base's own.
2
- const RESERVED = new Set(['answer', 'describe', 'stance', 'cells', 'standings', 'occupants', 'occupant', 'invite', 'knock', 'take', 'constructor']);
2
+ const RESERVED = new Set(['answer', 'describe', 'stance', 'cells', 'standings', 'occupants', 'occupant', 'invite', 'knock', 'take', 'boot', 'constructor']);
3
3
  export class Being {
4
4
  // Her cells' defaults. Merged in at birth, only where a key is missing, so
5
5
  // a restart keeps what she wrote.
@@ -38,6 +38,10 @@ export class Being {
38
38
  take(id, invitation) {
39
39
  return this.stance.standings.take(id, invitation);
40
40
  }
41
+ // A new being of her ward, by class name, under a key she chooses.
42
+ boot(className, key) {
43
+ return this.stance.boot(className, key);
44
+ }
41
45
  // The occupant record for whoever is at the door. Undefined at a public being.
42
46
  occupant(asker) {
43
47
  return asker.id === undefined ? undefined : this.cells.occupants[asker.id];
@@ -77,6 +77,7 @@ export type Stance = {
77
77
  readonly cells: Cells;
78
78
  readonly occupants: Occupants;
79
79
  readonly standings: Standings;
80
+ boot(className: string, key: string): string | null;
80
81
  };
81
82
  export interface BeingLike {
82
83
  answer(asker: Asker, method?: string, args?: JsonObject): Reply | Promise<Reply>;
@@ -10,6 +10,7 @@ export type Inside = {
10
10
  openHeir(heir: string, being: string, id: string): void;
11
11
  closeHeir(heir: string): void;
12
12
  send(bind: Bind, keys: StandingKeys, announce: boolean, method: string | undefined, args: JsonObject, wanted: Wanted | undefined): Promise<ReplyPayload | Silence | Word>;
13
+ instantiate(key: string, className: string): string | null;
13
14
  };
14
15
  export type SealedInvitation = {
15
16
  ward: string;
@@ -234,6 +234,12 @@ export function buildStance(inside, key, cells, bind) {
234
234
  });
235
235
  return {
236
236
  cells,
237
+ // A being may make. The ward refuses what it refuses the owner: a key
238
+ // already booted, a class the harbor does not hold, and a throw at birth
239
+ // leaves the partition as it was. Reaching into the being made is not
240
+ // offered here: that is the owner's, and she has no relation to her
241
+ // until one is invited and taken like any other.
242
+ boot: (className, key) => inside.instantiate(key, className),
237
243
  occupants: {
238
244
  // rotation one: the ward mints the occupant's first key and gives the secret away.
239
245
  // it keeps the pk beside the id and nothing else. the invitation IS the key.
package/dist/ward/ward.js CHANGED
@@ -135,6 +135,16 @@ class Self {
135
135
  openHeir: (heir, being, id) => this.heirs.open(heir, being, id),
136
136
  closeHeir: (heir) => this.heirs.close(heir),
137
137
  send: (b, keys, announce, method, args, wanted) => this.#send(b, keys, announce, method, args, wanted),
138
+ // A throw at birth is null to her, not a throw in her method: she asked
139
+ // for a being and got none, and her own answer is still hers to give.
140
+ instantiate: (k, className) => {
141
+ try {
142
+ return this.#instantiate(k, className)?.key ?? null;
143
+ }
144
+ catch {
145
+ return null;
146
+ }
147
+ },
138
148
  }, key, cells, bind);
139
149
  const being = classNameOrSelf === this ? this : this.g.instantiate(classNameOrSelf, stance);
140
150
  if (!being)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quo-systems/quo",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Quo: an object asks another object and gets an answer, without knowing where it is. Being, Ward, Harbor.",
5
5
  "keywords": [
6
6
  "quo",
@@ -39,7 +39,7 @@
39
39
  "build": "rm -rf dist && tsc -p tsconfig.build.json",
40
40
  "test": "node --test \"test/*.test.ts\"",
41
41
  "check:terrain": "node --test \"test/terrain/*.test.ts\"",
42
- "prepublishOnly": "cd ../.. && npm run check && npm run check:terrain"
42
+ "prepublishOnly": "test \"$QUO_GATED\" = 1 || { echo 'publish from the root, gated once: npm run release' >&2; exit 1; }"
43
43
  },
44
44
  "publishConfig": {
45
45
  "access": "public"
@@ -16,7 +16,7 @@ export type AskSpec = {
16
16
  };
17
17
 
18
18
  // Names a subclass may not use for an ask, because they are the base's own.
19
- const RESERVED = new Set(['answer', 'describe', 'stance', 'cells', 'standings', 'occupants', 'occupant', 'invite', 'knock', 'take', 'constructor']);
19
+ const RESERVED = new Set(['answer', 'describe', 'stance', 'cells', 'standings', 'occupants', 'occupant', 'invite', 'knock', 'take', 'boot', 'constructor']);
20
20
 
21
21
  export class Being {
22
22
  // Her cells' defaults. Merged in at birth, only where a key is missing, so
@@ -55,6 +55,10 @@ export class Being {
55
55
  take(id: string, invitation: Invitation): Promise<string | null> {
56
56
  return this.stance.standings.take(id, invitation);
57
57
  }
58
+ // A new being of her ward, by class name, under a key she chooses.
59
+ boot(className: string, key: string): string | null {
60
+ return this.stance.boot(className, key);
61
+ }
58
62
  // The occupant record for whoever is at the door. Undefined at a public being.
59
63
  occupant(asker: Asker): OccupantRecord | undefined {
60
64
  return asker.id === undefined ? undefined : this.cells.occupants[asker.id];
@@ -89,6 +89,10 @@ export type Stance = {
89
89
  readonly cells: Cells;
90
90
  readonly occupants: Occupants;
91
91
  readonly standings: Standings;
92
+ // A new being of her ward, by class name, under a key she chooses. The key
93
+ // back, or null: the key is taken or the harbor holds no such class. A
94
+ // being may make; only the owner reaches into another.
95
+ boot(className: string, key: string): string | null;
92
96
  };
93
97
 
94
98
  // The raw shape of a being. Anything with these two is a being.
@@ -18,6 +18,8 @@ export type Inside = {
18
18
  // one send for every destination. the far door's reply, silence, or unreached.
19
19
  // signs as keys.current, announces keys.next (minting one if asked), and rotates keys on an answer.
20
20
  send(bind: Bind, keys: StandingKeys, announce: boolean, method: string | undefined, args: JsonObject, wanted: Wanted | undefined): Promise<ReplyPayload | Silence | Word>;
21
+ // a new being of this ward, as the owner's boot makes one. the key, or null.
22
+ instantiate(key: string, className: string): string | null;
21
23
  };
22
24
 
23
25
  // An invitation as it travels: the far ward's pk, the heir pk, the heir's secret seed. Or just the ward, for its public being.
@@ -247,6 +249,12 @@ export function buildStance(inside: Inside, key: string, cells: Cells, bind: Bin
247
249
 
248
250
  return {
249
251
  cells,
252
+ // A being may make. The ward refuses what it refuses the owner: a key
253
+ // already booted, a class the harbor does not hold, and a throw at birth
254
+ // leaves the partition as it was. Reaching into the being made is not
255
+ // offered here: that is the owner's, and she has no relation to her
256
+ // until one is invited and taken like any other.
257
+ boot: (className: string, key: string): string | null => inside.instantiate(key, className),
250
258
  occupants: {
251
259
  // rotation one: the ward mints the occupant's first key and gives the secret away.
252
260
  // it keeps the pk beside the id and nothing else. the invitation IS the key.
package/src/ward/ward.ts CHANGED
@@ -145,6 +145,15 @@ class Self implements BeingLike {
145
145
  openHeir: (heir, being, id) => this.heirs.open(heir, being, id),
146
146
  closeHeir: (heir) => this.heirs.close(heir),
147
147
  send: (b, keys, announce, method, args, wanted) => this.#send(b, keys, announce, method, args, wanted),
148
+ // A throw at birth is null to her, not a throw in her method: she asked
149
+ // for a being and got none, and her own answer is still hers to give.
150
+ instantiate: (k, className) => {
151
+ try {
152
+ return this.#instantiate(k, className)?.key ?? null;
153
+ } catch {
154
+ return null;
155
+ }
156
+ },
148
157
  },
149
158
  key,
150
159
  cells,