@jsm-mit/tigi-sharded-package 0.2.0 → 0.4.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 (37) hide show
  1. package/README.md +51 -3
  2. package/declarations/coordinator/coordinator.did +39 -0
  3. package/declarations/coordinator/coordinator.did.d.ts +68 -0
  4. package/declarations/coordinator/coordinator.did.js +23 -0
  5. package/declarations/core/core.did +210 -45
  6. package/declarations/core/core.did.d.ts +191 -43
  7. package/declarations/core/core.did.js +146 -42
  8. package/declarations/tigi-driver/tigi-driver.did +15 -0
  9. package/declarations/tigi-driver/tigi-driver.did.d.ts +19 -0
  10. package/declarations/tigi-driver/tigi-driver.did.js +6 -0
  11. package/declarations/tigi-simulator/tigi-simulator.did +46 -4
  12. package/declarations/tigi-simulator/tigi-simulator.did.d.ts +70 -4
  13. package/declarations/tigi-simulator/tigi-simulator.did.js +39 -10
  14. package/declarations/worker/worker.did +23 -6
  15. package/declarations/worker/worker.did.d.ts +18 -6
  16. package/declarations/worker/worker.did.js +14 -7
  17. package/dist/actors/driver-canisters-actor.d.ts +77 -0
  18. package/dist/actors/driver-canisters-actor.d.ts.map +1 -0
  19. package/dist/actors/driver-canisters-actor.js +147 -0
  20. package/dist/actors/rides-actor.d.ts +21 -1
  21. package/dist/actors/rides-actor.d.ts.map +1 -1
  22. package/dist/actors/rides-actor.js +44 -2
  23. package/dist/geo.d.ts +5 -0
  24. package/dist/geo.d.ts.map +1 -1
  25. package/dist/geo.js +41 -0
  26. package/dist/group/canister-actor.d.ts +7 -0
  27. package/dist/group/canister-actor.d.ts.map +1 -1
  28. package/dist/group/canister-actor.js +18 -0
  29. package/dist/index.d.ts +3 -2
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +5 -2
  32. package/dist/interfaces.d.ts +43 -1
  33. package/dist/interfaces.d.ts.map +1 -1
  34. package/dist/mappers.d.ts +7 -2
  35. package/dist/mappers.d.ts.map +1 -1
  36. package/dist/mappers.js +17 -2
  37. package/package.json +7 -4
package/README.md CHANGED
@@ -3,11 +3,14 @@
3
3
  The one package of the Tigi network group: it takes the coordinator's id and hides the
4
4
  coordinator, the core and the workers — for a consumer the group is one canister. It also
5
5
  carries the rider's few driver-canister endpoints (U-14: `getRide`, `cancelRide`,
6
- `getLicence`), built from `protocol/tigi-driver.did`, so the consumer never sees a driver
6
+ `getLicence`, `getRideRoute`), built from `protocol/tigi-driver.did`, so the consumer never sees a driver
7
7
  canister's id.
8
8
 
9
9
  Status: the rider's side (night 1, U-42); 0.2.0 (stage 2a, U-43) adds the rider's own
10
- rating and WHERE the chat with a driver is. Version 0.1.0 was the first to publish.
10
+ rating and WHERE the chat with a driver is; 0.3.0 (U-45) adds `DriverCanistersActor` — the
11
+ demo lease and the one-time claim code; 0.4.0 (route line, 2026-10-24) adds the driver's
12
+ route on the map — the line of a tapped offer, the line of the ride — and the exact pin of
13
+ an offer (`pinExact`). Version 0.1.0 was the first to publish.
11
14
 
12
15
  ## The rider
13
16
 
@@ -26,6 +29,8 @@ const snapshot = await request.getAsyncUnsafe(); // every REQUEST_POL
26
29
  // snapshot.offers: a FIXED list (U-42) — by first arrival, new ones at the end, nothing
27
30
  // re-sorted; an offer that left keeps its entry with `gone` (rejected | driverNotFree |
28
31
  // taken | closed): draw a grey row of the same height there
32
+ // offer.pin: the driver's exact position (offer.pinExact) — the geohash7 centre from an old driver canister
33
+ const line = await request.getOfferRouteAsyncUnsafe(offer.offerId); // ZO-21, after a tap: { toPickup: Geo[]; ride: Geo[] } | undefined
29
34
  await request.rejectOfferAsyncUnsafe(offer.offerId); // ✕ → gone "rejected"; the next nearest driver
30
35
  await request.refreshOfferAsyncUnsafe(offer.offerId); // a stale (grey) offer asks again
31
36
  await request.chooseOfferAsyncUnsafe(offer.offerId); // ordered (= the ride, accepted at once) | busy | offerExpired
@@ -35,6 +40,7 @@ await request.giveUpAsyncUnsafe();
35
40
  const again = await request.searchAgainAsyncUnsafe(); // a new request, the same pickup and destination
36
41
 
37
42
  const ride = await request.getRideAsyncUnsafe(); // from the driver canister, every RIDE_POLL_MS (2 s)
43
+ await request.getRideRouteAsyncUnsafe(); // K-13: the ride's line while it is active (once; undefined after the end)
38
44
  await request.cancelRideAsyncUnsafe(); // before inProgress, once
39
45
  await request.getLicenceAsyncUnsafe(); // from accepted to the end of the ride
40
46
  await request.rateDriverAsyncUnsafe(5); // after completed, once
@@ -96,6 +102,44 @@ const driverPlace = await new ChatPlacesActor(coordinatorId, identity, { host })
96
102
  - The room may appear a few seconds after the driver's canister (the core's outbox);
97
103
  `openChatAsyncUnsafe` answers `NotFound` until then — try again.
98
104
 
105
+ ## For the fronts: the driver's own canister (U-45)
106
+
107
+ Internet Identity gives one person a different principal on every origin. The demo lease is
108
+ taken on the rider's site; the driver signs in on the canister's own front
109
+ (`https://<canister>.icp0.io`) with another principal. A fresh lease answers a one-time
110
+ claim code (15 min, single use; the core keeps only its sha256) that hands the canister to
111
+ whoever claims it there (`fabryka.md` F-20).
112
+
113
+ ```ts
114
+ import { DriverCanistersActor, claimLinkOf, claimCodeOfHash } from "@jsm-mit/tigi-sharded-package";
115
+
116
+ // The rider's site (the lease holder).
117
+ const canisters = new DriverCanistersActor(coordinatorId, identity, { host });
118
+ const lease = await canisters.leaseDemoAsyncUnsafe(); // { kind: "leased", canister, claim? } | { kind: "busy", untilMs }
119
+ if (lease.kind === "leased" && lease.claim) {
120
+ link = claimLinkOf(lease.canister.canisterId, lease.claim.code); // "Otwórz swój kanister"
121
+ }
122
+ const fresh = await canisters.newClaimCodeAsyncUnsafe(); // "Nowy link": the old code stops working
123
+ await canisters.extendDemoAsyncUnsafe(); // F-8: 15 min more
124
+ await canisters.getMineAsyncUnsafe(); // the held demo canister (or the own one) | undefined
125
+
126
+ // The driver's front, signed in as a non-owner, with #claim=<code> in the address.
127
+ const code = claimCodeOfHash(window.location.hash);
128
+ if (code) {
129
+ await new DriverCanistersActor(coordinatorId, identity, { host }).claimAsyncUnsafe(canisterId, code);
130
+ // clear the fragment, reload: the driver is the owner now
131
+ }
132
+ ```
133
+
134
+ - A refused claim is always `CanisterError` `NotAuthorized` — wrong, expired, used or not
135
+ this canister's code alike.
136
+ - The lease stays with its holder: `getMineAsyncUnsafe`, `extendDemoAsyncUnsafe` and
137
+ `newClaimCodeAsyncUnsafe` keep working from the rider's site; `ownerPrincipal` names
138
+ the driver after the claim. The chat room of the canister gets the driver as its
139
+ recipient.
140
+ - The same holder leasing again (no reinstall) gets no code: `newClaimCodeAsyncUnsafe`.
141
+ - These views carry the driver canister's id: it is the driver's own address.
142
+
99
143
  The Test Manager (stage and the test modes): `TestManagerActor.hasSimulatedDriversAsyncUnsafe`,
100
144
  `spawnDriversAsyncUnsafe` (12 drivers), `setSpeedUpAsyncUnsafe(request, 10)` before choosing.
101
145
  Offers of the simulator have `simulated: true`.
@@ -108,7 +152,9 @@ npm run test-unit # no replica needed
108
152
  npm run test-drivers # (a) 5 real driver canisters, driver-bots, route-stub
109
153
  npm run test-simulator # (b) the simulator, 20 requests, 20 rides
110
154
  npm run test-split # (c) an area split during traffic
155
+ npm run test-claim # (d) the demo lease and the claim code (U-45)
111
156
  npm run test-smoke # one whole ride with the simulator (run by publish-public)
157
+ npm run load-local # the load test: 200 simulated drivers, 100 riders (docs/load-test-2026-10-24.md)
112
158
  npm run publish-public # human-run: publish from master (see below)
113
159
  npm run sync-declarations # after a change of a .did (`… protocol` for the protocol and the simulator only)
114
160
  ```
@@ -116,7 +162,9 @@ npm run sync-declarations # after a change of a .did (`… protocol` for the pr
116
162
  The end-to-end suites run on the LOCAL replica only (`scripts/run-tests-local.sh`): a fresh
117
163
  group, local cycles, the driver and simulator wasms of `../../tigi-driver-rust` (built
118
164
  there, never changed). Logs and measurements go to `/tmp/claude-1000/tigi/`
119
- (`TEST_LOG_DIR`).
165
+ (`TEST_LOG_DIR`). The load test builds its group with its own coordinator
166
+ (`tigi-sharded-coordinator-load`), so the shared local group stays; its settings are
167
+ environment variables (`scripts/load-local.ts`, `scripts/run-load-local.sh`).
120
168
 
121
169
  ## Publishing
122
170
 
@@ -48,8 +48,16 @@ type Config = record {
48
48
  wasm : opt WasmInfo;
49
49
  // Somebody who asked for the superadmin role (LIVE; one request a day for the group).
50
50
  roleRequest : opt RoleRequest;
51
+ // KO-19 (U-46): the cycles per canister of the group below which the fee multiplier
52
+ // goes up and above which it goes down. None = the defaults.
53
+ feeThresholds : opt FeeThresholds;
54
+ // KO-20: every fee cycle the core forwarded, in total.
55
+ feesReceived : opt nat;
51
56
  // The UTC day of the last request: a second request the same day is refused in inspect.
52
57
  lastRequestDay : nat64;
58
+ // KO-19: the multiplier of the drivers' daily fee, recomputed at most once a UTC day.
59
+ // None = never computed (the start value counts).
60
+ feeMultiplier : opt FeeMultiplierState;
53
61
  // What a new worker starts with, and what a worker or the core gets when it reports a
54
62
  // low balance. A worker asks below half of it.
55
63
  cyclesPerWorker : nat;
@@ -135,6 +143,26 @@ type Event = record {
135
143
  kind : text;
136
144
  "text" : text;
137
145
  };
146
+ type FeeMultiplier = record { day : nat64; permille : nat32 };
147
+ // KO-19: the core asks for the multiplier of its fee day.
148
+ type FeeMultiplierArgs = record { day : nat64 };
149
+ // KO-19: the multiplier of one UTC day and what it was computed from.
150
+ type FeeMultiplierState = record {
151
+ day : nat64;
152
+ // In thousandths: 1200 = 1.2.
153
+ permille : nat32;
154
+ // The canisters whose balance was not read at that reading ("<id>: <error>"), left out
155
+ // of the sum and of N. Not empty = an alarm on the front.
156
+ unread : opt vec text;
157
+ // The balances of the coordinator, the core and the workers read that day.
158
+ groupSum : opt nat;
159
+ computedAtNs : opt nat64;
160
+ // N: the canisters read (workers + core + coordinator).
161
+ canisters : opt nat32;
162
+ };
163
+ // KO-19: per canister of the group (the coordinator, the core, every worker). Defaults:
164
+ // `DEFAULT_FEE_MIN_PER_CANISTER`, `DEFAULT_FEE_MAX_PER_CANISTER`.
165
+ type FeeThresholds = record { minPerCanister : nat; maxPerCanister : nat };
138
166
  // The arguments of the method are the core's, Candid-encoded (`to_candid` in Motoko) —
139
167
  // the coordinator does not read them and keeps no copy of the chat group's types.
140
168
  type ForwardChatAdminArgs = record {
@@ -545,6 +573,12 @@ service : (InitArgs) -> {
545
573
  forwardChatAdmin : (ForwardChatAdminArgs) -> (Result_7);
546
574
  // What the core says of itself right now — one composite query for the front.
547
575
  getCoreStatus : () -> (Result_8) composite_query;
576
+ // KO-19 (U-46): the multiplier of the drivers' daily fee for the core's fee day —
577
+ // recomputed lazily, at most once a UTC day, when the core asks at the day's first
578
+ // settlement (no timer). The sum of the balances of the coordinator, the core and every
579
+ // worker against N × the thresholds (N = the canisters read). A reading already running
580
+ // answers the multiplier as it is. Only the core of this group (a trap otherwise).
581
+ getFeeMultiplier : (FeeMultiplierArgs) -> (FeeMultiplier);
548
582
  // The core of the group.
549
583
  getHomeCanister : () -> (opt principal) query;
550
584
  // The whole prefix map: which worker serves which area.
@@ -624,6 +658,8 @@ service : (InitArgs) -> {
624
658
  // `maxWorkers`, the query thresholds. Only the fields given are changed.
625
659
  setConfig : (SetConfigArgs) -> (Result_16);
626
660
  setCoreLoggingEnabled : (CoreLoggingArgs) -> (Result_11);
661
+ // KO-19: the superadmin sets the thresholds of the fee multiplier (cycles per canister).
662
+ setFeeThresholds : (FeeThresholds) -> (Result_16);
627
663
  // The config of the workers of the group: the load thresholds, logging. Only the fields
628
664
  // given are changed. The result is stored, given to every worker that has its code, and
629
665
  // to every worker created later.
@@ -637,6 +673,9 @@ service : (InitArgs) -> {
637
673
  // A worker of the group sends its cycles here before it is deleted (`giveBackCycles`).
638
674
  // The workers only: somebody else's cycles are refused (they go back to the sender).
639
675
  takeCycles : () -> (nat);
676
+ // KO-20 (U-46): the drivers' fees the core took, forwarded here — every attached cycle is
677
+ // accepted and counted. No per-driver data: the bills are the core's. Only the core.
678
+ takeFeeCycles : () -> (nat);
640
679
  // Cycles for the core, by hand: `cyclesPerWorker`, like a worker's top-up.
641
680
  topUpCore : () -> (Result_20);
642
681
  // Cycles for a worker, by hand.
@@ -88,10 +88,24 @@ export interface Config {
88
88
  * Somebody who asked for the superadmin role (LIVE; one request a day for the group).
89
89
  */
90
90
  'roleRequest' : [] | [RoleRequest],
91
+ /**
92
+ * KO-19 (U-46): the cycles per canister of the group below which the fee multiplier
93
+ * goes up and above which it goes down. None = the defaults.
94
+ */
95
+ 'feeThresholds' : [] | [FeeThresholds],
96
+ /**
97
+ * KO-20: every fee cycle the core forwarded, in total.
98
+ */
99
+ 'feesReceived' : [] | [bigint],
91
100
  /**
92
101
  * The UTC day of the last request: a second request the same day is refused in inspect.
93
102
  */
94
103
  'lastRequestDay' : bigint,
104
+ /**
105
+ * KO-19: the multiplier of the drivers' daily fee, recomputed at most once a UTC day.
106
+ * None = never computed (the start value counts).
107
+ */
108
+ 'feeMultiplier' : [] | [FeeMultiplierState],
95
109
  /**
96
110
  * What a new worker starts with, and what a worker or the core gets when it reports a
97
111
  * low balance. A worker asks below half of it.
@@ -227,6 +241,43 @@ export interface Event {
227
241
  'kind' : string,
228
242
  'text' : string,
229
243
  }
244
+ export interface FeeMultiplier { 'day' : bigint, 'permille' : number }
245
+ /**
246
+ * KO-19: the core asks for the multiplier of its fee day.
247
+ */
248
+ export interface FeeMultiplierArgs { 'day' : bigint }
249
+ /**
250
+ * KO-19: the multiplier of one UTC day and what it was computed from.
251
+ */
252
+ export interface FeeMultiplierState {
253
+ 'day' : bigint,
254
+ /**
255
+ * In thousandths: 1200 = 1.2.
256
+ */
257
+ 'permille' : number,
258
+ /**
259
+ * The canisters whose balance was not read at that reading ("<id>: <error>"), left out
260
+ * of the sum and of N. Not empty = an alarm on the front.
261
+ */
262
+ 'unread' : [] | [Array<string>],
263
+ /**
264
+ * The balances of the coordinator, the core and the workers read that day.
265
+ */
266
+ 'groupSum' : [] | [bigint],
267
+ 'computedAtNs' : [] | [bigint],
268
+ /**
269
+ * N: the canisters read (workers + core + coordinator).
270
+ */
271
+ 'canisters' : [] | [number],
272
+ }
273
+ /**
274
+ * KO-19: per canister of the group (the coordinator, the core, every worker). Defaults:
275
+ * `DEFAULT_FEE_MIN_PER_CANISTER`, `DEFAULT_FEE_MAX_PER_CANISTER`.
276
+ */
277
+ export interface FeeThresholds {
278
+ 'minPerCanister' : bigint,
279
+ 'maxPerCanister' : bigint,
280
+ }
230
281
  /**
231
282
  * The arguments of the method are the core's, Candid-encoded (`to_candid` in Motoko) —
232
283
  * the coordinator does not read them and keeps no copy of the chat group's types.
@@ -824,6 +875,14 @@ export interface _SERVICE {
824
875
  * What the core says of itself right now — one composite query for the front.
825
876
  */
826
877
  'getCoreStatus' : ActorMethod<[], Result_8>,
878
+ /**
879
+ * KO-19 (U-46): the multiplier of the drivers' daily fee for the core's fee day —
880
+ * recomputed lazily, at most once a UTC day, when the core asks at the day's first
881
+ * settlement (no timer). The sum of the balances of the coordinator, the core and every
882
+ * worker against N × the thresholds (N = the canisters read). A reading already running
883
+ * answers the multiplier as it is. Only the core of this group (a trap otherwise).
884
+ */
885
+ 'getFeeMultiplier' : ActorMethod<[FeeMultiplierArgs], FeeMultiplier>,
827
886
  /**
828
887
  * The core of the group.
829
888
  */
@@ -955,6 +1014,10 @@ export interface _SERVICE {
955
1014
  */
956
1015
  'setConfig' : ActorMethod<[SetConfigArgs], Result_16>,
957
1016
  'setCoreLoggingEnabled' : ActorMethod<[CoreLoggingArgs], Result_11>,
1017
+ /**
1018
+ * KO-19: the superadmin sets the thresholds of the fee multiplier (cycles per canister).
1019
+ */
1020
+ 'setFeeThresholds' : ActorMethod<[FeeThresholds], Result_16>,
958
1021
  /**
959
1022
  * The config of the workers of the group: the load thresholds, logging. Only the fields
960
1023
  * given are changed. The result is stored, given to every worker that has its code, and
@@ -976,6 +1039,11 @@ export interface _SERVICE {
976
1039
  * The workers only: somebody else's cycles are refused (they go back to the sender).
977
1040
  */
978
1041
  'takeCycles' : ActorMethod<[], bigint>,
1042
+ /**
1043
+ * KO-20 (U-46): the drivers' fees the core took, forwarded here — every attached cycle is
1044
+ * accepted and counted. No per-driver data: the bills are the core's. Only the core.
1045
+ */
1046
+ 'takeFeeCycles' : ActorMethod<[], bigint>,
979
1047
  /**
980
1048
  * Cycles for the core, by hand: `cyclesPerWorker`, like a worker's top-up.
981
1049
  */
@@ -116,6 +116,11 @@ export const idlFactory = ({ IDL }) => {
116
116
  'admins' : IDL.Opt(IDL.Vec(IDL.Principal)),
117
117
  });
118
118
  const Result_8 = IDL.Variant({ 'Ok' : CoreLive, 'Err' : CoordinatorError });
119
+ const FeeMultiplierArgs = IDL.Record({ 'day' : IDL.Nat64 });
120
+ const FeeMultiplier = IDL.Record({
121
+ 'day' : IDL.Nat64,
122
+ 'permille' : IDL.Nat32,
123
+ });
119
124
  const Event = IDL.Record({
120
125
  'id' : IDL.Nat64,
121
126
  'atNs' : IDL.Nat64,
@@ -191,6 +196,18 @@ export const idlFactory = ({ IDL }) => {
191
196
  'load' : IDL.Opt(LoadThresholds),
192
197
  'loggingEnabled' : IDL.Opt(IDL.Bool),
193
198
  });
199
+ const FeeThresholds = IDL.Record({
200
+ 'minPerCanister' : IDL.Nat,
201
+ 'maxPerCanister' : IDL.Nat,
202
+ });
203
+ const FeeMultiplierState = IDL.Record({
204
+ 'day' : IDL.Nat64,
205
+ 'permille' : IDL.Nat32,
206
+ 'unread' : IDL.Opt(IDL.Vec(IDL.Text)),
207
+ 'groupSum' : IDL.Opt(IDL.Nat),
208
+ 'computedAtNs' : IDL.Opt(IDL.Nat64),
209
+ 'canisters' : IDL.Opt(IDL.Nat32),
210
+ });
194
211
  const Config = IDL.Record({
195
212
  'maxWorkers' : IDL.Opt(IDL.Nat64),
196
213
  'coreWasm' : IDL.Opt(WasmInfo),
@@ -203,7 +220,10 @@ export const idlFactory = ({ IDL }) => {
203
220
  'workerConfig' : IDL.Opt(WorkerConfigArgs),
204
221
  'wasm' : IDL.Opt(WasmInfo),
205
222
  'roleRequest' : IDL.Opt(RoleRequest),
223
+ 'feeThresholds' : IDL.Opt(FeeThresholds),
224
+ 'feesReceived' : IDL.Opt(IDL.Nat),
206
225
  'lastRequestDay' : IDL.Nat64,
226
+ 'feeMultiplier' : IDL.Opt(FeeMultiplierState),
207
227
  'cyclesPerWorker' : IDL.Nat,
208
228
  'spread' : IDL.Bool,
209
229
  'nextEventId' : IDL.Nat64,
@@ -367,6 +387,7 @@ export const idlFactory = ({ IDL }) => {
367
387
  'drainWorker' : IDL.Func([DrainWorkerArgs], [Result_6], []),
368
388
  'forwardChatAdmin' : IDL.Func([ForwardChatAdminArgs], [Result_7], []),
369
389
  'getCoreStatus' : IDL.Func([], [Result_8], ['composite_query']),
390
+ 'getFeeMultiplier' : IDL.Func([FeeMultiplierArgs], [FeeMultiplier], []),
370
391
  'getHomeCanister' : IDL.Func([], [IDL.Opt(IDL.Principal)], ['query']),
371
392
  'getPrefixMap' : IDL.Func([], [IDL.Vec(PrefixEntry)], ['query']),
372
393
  'getRoles' : IDL.Func([], [Roles], ['query']),
@@ -400,10 +421,12 @@ export const idlFactory = ({ IDL }) => {
400
421
  'resetGroup' : IDL.Func([], [Result_15], []),
401
422
  'setConfig' : IDL.Func([SetConfigArgs], [Result_16], []),
402
423
  'setCoreLoggingEnabled' : IDL.Func([CoreLoggingArgs], [Result_11], []),
424
+ 'setFeeThresholds' : IDL.Func([FeeThresholds], [Result_16], []),
403
425
  'setWorkerConfig' : IDL.Func([WorkerConfigArgs], [Result_17], []),
404
426
  'setWorkerOpen' : IDL.Func([SetWorkerOpenArgs], [Result_18], []),
405
427
  'splitWorker' : IDL.Func([CanisterArgs], [Result_19], []),
406
428
  'takeCycles' : IDL.Func([], [IDL.Nat], []),
429
+ 'takeFeeCycles' : IDL.Func([], [IDL.Nat], []),
407
430
  'topUpCore' : IDL.Func([], [Result_20], []),
408
431
  'topUpWorker' : IDL.Func([CanisterArgs], [Result_20], []),
409
432
  'upgradeCore' : IDL.Func([], [Result_3], []),