@jsm-mit/tigi-sharded-package

中文文档
0.12.0发布于 2026/11/01ISCTypeScriptES Module

Tigi网络组的一个包:获取协调器的id,并隐藏协调器、核心和工作器——对于消费者来说,组是一个罐子;加上车手的几个驾驶罐端点(U-14)。

中文使用文档
文档目录5 节
  1. 骑手
  2. 正面:U-43
  3. 前方:驾驶员自备油箱(U-45)
  4. 脚本
  5. 出版

@jsm-mit / tigi-sharded-package

Tigi网络组的一个包:它接受协调器的id并隐藏 协调员、核心和工人——对于一个消费者来说,这个群体是一个罐子。它还 携带着车手的几个驱动罐端点(U-14); getRide, cancelRide, getLicence, getRideRoute),由 protocol/tigi-driver.did,所以消费者永远看不到司机 罐的id。

状态:骑手侧(1夜,U-42);0.2.0(阶段2a, U-43)增加了骑手自己的 评级和在哪里与司机聊天;0.3.0 (U-45)添加 DriverCanistersActor - - - 示范租约及一次性索赔代码;0.4.0(路线,2026-10-24)增加了驾驶员的 地图上的路线-提供的线路,乘车线路-以及确切的针 报价(pinExact);0.5.0(第7阶段,骑行历史,2026-10-24)添加了过去的位置 ride (Skąd, Dokąd)和历史上过去一次骑行的评分。版本0.1.0是 首先发表。

骑手

import { ProfilesActor, RidesActor, TestManagerActor, REQUEST_POLL_MS } from "@jsm-mit/tigi-sharded-package";

const profiles = new ProfilesActor(coordinatorId, identity, { host });
await profiles.setMyProfileAsyncUnsafe("Ala");             // C-2: once, before the first request

const rides = new RidesActor(coordinatorId, identity, { host });
const request = await rides.requestRideAsyncUnsafe({ pickup, destination });
localStorage.setItem("tigi-request", request.token);       // rides.resumeRequest(token) after a reload

const snapshot = await request.getAsyncUnsafe();            // every REQUEST_POLL_MS (1 s)
// snapshot.phase: searching | waitingForDriver ("Zamawiam…", a second or two) | accepted | closed
// snapshot.offers: a FIXED list (U-42) — by first arrival, new ones at the end, nothing
//   re-sorted; an offer that left keeps its entry with `gone` (rejected | driverNotFree |
//   taken | closed): draw a grey row of the same height there
// offer.pin: the driver's exact position (offer.pinExact) — the geohash7 centre from an old driver canister
const line = await request.getOfferRouteAsyncUnsafe(offer.offerId);  // ZO-21, after a tap: { toPickup: Geo[]; ride: Geo[] } | undefined
await request.rejectOfferAsyncUnsafe(offer.offerId);        // ✕ → gone "rejected"; the next nearest driver
await request.refreshOfferAsyncUnsafe(offer.offerId);       // a stale (grey) offer asks again
await request.chooseOfferAsyncUnsafe(offer.offerId);        // ordered (= the ride, accepted at once) | busy | offerExpired
                                                            // | driverNotFree ("Kierowca jest już zajęty") | closed
await request.giveUpAsyncUnsafe();
// After "Kierowca anulował kurs" (ride status cancelledByDriver): "Szukaj ponownie" —
const again = await request.searchAgainAsyncUnsafe();       // a new request, the same pickup and destination

const ride = await request.getRideAsyncUnsafe();            // from the driver canister, every RIDE_POLL_MS (2 s)
await request.getRideRouteAsyncUnsafe();                    // K-13: the ride's line while it is active (once; undefined after the end)
await request.cancelRideAsyncUnsafe();                      // before inProgress, once
// ZT-*: a ride at a chosen time (1 h … 7 days ahead; drivers asked by their base, 3 → 25 km by the workers themselves):
const later = await rides.requestScheduledAsyncUnsafe({ pickup, destination, pickupAtMs }); // keep later.token; rides.resumeScheduled(token)
const s = await later.getAsyncUnsafe();                     // phase collecting | booking | booked | started | rescue | closed; offers cheapest first
await later.chooseAsyncUnsafe(s.offers[0].offerId);         // a booking; read again for booked / lastOrderRefused
// started → rides.resumeRequest(s.rideToken) is the ride; rescue → requestRideAsyncUnsafe with the same A, B from T−10 min (RESCUE_BEFORE_MS)
// P-20: card straight to the driver (Stripe) — when ride.cardAccepted, during inProgress:
const card = await request.startCardPaymentAsyncUnsafe(returnUrl); // open → go to card.url; the same page again while it stands
await request.checkCardPaymentAsyncUnsafe();                // back from Stripe (card=paid|cancelled in the URL); ride.cardPayment shows it too
await request.getLicenceAsyncUnsafe();                      // from accepted to the end of the ride
await request.rateDriverAsyncUnsafe(5);                     // after completed, once

const page = await profiles.getMyRidesAsyncUnsafe();        // the history in the core, 20 a page
// 0.5.0 — a line: pickup?, destination? (KU-4; none before stage 7), ratable (KU-5), myRating?
const past = await profiles.getMyRideAsyncUnsafe(page.rides[0].rideRef);  // one line (a reload); undefined until the core has it
if (past?.ratable) await profiles.ratePastRideAsyncUnsafe(past.rideRef, 5);
//   → "ok" | "alreadyRated" (also after rateDriverAsyncUnsafe) | "notRatable" (not completed,
//     the 24 h are over, not in the core yet) | "notAuthorized" (another rider's ride)
const mine = await profiles.getMyRatingAsyncUnsafe();       // PF-4: { average: 4.87, count, sum } | undefined ("Brak ocen")
await rides.getPastRideAsyncUnsafe(page.rides[0].rideRef);  // its details from the driver canister

这个包对网络的作用(zgloszenie-i-oferty.md):

  • 该请求发送给皮卡车(ZO-1)周围3公里范围内的每个工人 使用协调器的前缀映射(保持30秒);一个工人的 NOT_MY_AREA 让它再问一次);
  • 15秒无报价→6公里,在相同的和新的工人(ZO-8),在里面 getAsyncUnsafe;
  • 在所有工人中一次选择一个(ZO-14);在接受之后, closeRequest otherOfferChosen 在其他工人(ZO-15);
  • 一个停止的罐(升级,IC0508/IC0509)等待大约一分钟。

错误: CanisterError (与 errorKey; refusalKeyOf(err) 给出工人的拒绝: ALREADY_OPEN, NOT_MY_AREA, FULL, CLOSED, ORDERING, DRIVER_NOT_FREE, NOT_COMPLETED, EXPIRED, BLOCKED), CallRefusedAtInspectionStage, CriticalCanisterError.

正面:U-43

每个司机有一个聊天室 chat-sharded (不是每乘一次):它的接受者是 驱动程序,并且每个Tigi帐户都可以在那里打开聊天(聊天询问Tigi) 协调者账户所在的位置和核心 isUser). 核心使房间得以贯通 协调器;这个包只说WHERE -前台发送和读取消息 @jsm-mit/chat-sharded-package 本身(此包不包装聊天)。

import { ChatPlacesActor, driverRoomId } from "@jsm-mit/tigi-sharded-package";
import { ChatsActor, MessagesActor } from "@jsm-mit/chat-sharded-package";

// The rider (PR-25): from `accepted` to the end of the ride.
const place = await request.getChatPlaceAsyncUnsafe();      // { chatCoordinatorId, projectId, roomId } | undefined
if (place) {
    const chats = new ChatsActor(place.chatCoordinatorId, identity, { host });
    const chat = await chats.openChatAsyncUnsafe({ projectId: place.projectId, roomId: place.roomId, customerName: profile.name });
    // … MessagesActor(place.chatCoordinatorId, identity).postAsyncUnsafe / pollNewMessagesAsyncUnsafe
}

// The driver (FK-20): the room of their own canister; they are its recipient.
const driverPlace = await new ChatPlacesActor(coordinatorId, identity, { host }).getDriverChatPlaceAsyncUnsafe(myDriverCanisterId);
// list the chats of driverPlace.roomId (ChatsActor.getMyChatsAsyncUnsafe({ projectId, roomId }))
// and show only the current rider's chat
  • undefined =在这个环境中没有聊天(核心中没有聊天配置)或a 模拟司机(MVP中没有聊天)。
  • 房间id是确定的: driverRoomId(canister) = drv-<canister id> ( 核心的 ChatRoomFunctions.driverRoomId)。
  • 车手需要一个Tigi档案(setMyProfileAsyncUnsafe) -没有一个聊天 拒绝打开它(CanisterError, NotAuthorized)。
  • 每个聊天(房间,乘客,收件人):乘客与司机有一次聊天 他们的游乐设施。由另一个测试人员接管的演示池罐将使该测试人员成为 收件人;与前一个测试人员的聊天记录会留在他们身边。
  • 房间可能会在司机的罐子(核心的发箱)之后几秒钟出现; openChatAsyncUnsafe 答案 NotFound 在那之前,再试一次。

前方:驾驶员自备油箱(U-45)

互联网身份赋予一个人不同的身份。示范租约为 拍摄于骑手的场地;司机在罐子的正面签到 (https://<canister>.icp0.io)。新租约是一次性的 声明代码(15分钟,单次使用;核心只保留其sha256),将罐子交给 谁在那里认领它(fabryka.md F-20)。

import { DriverCanistersActor, claimLinkOf, claimCodeOfHash } from "@jsm-mit/tigi-sharded-package";

// The rider's site (the lease holder).
const canisters = new DriverCanistersActor(coordinatorId, identity, { host });
const lease = await canisters.leaseDemoAsyncUnsafe();       // { kind: "leased", canister, claim? } | { kind: "busy", untilMs }
if (lease.kind === "leased" && lease.claim) {
    link = claimLinkOf(lease.canister.canisterId, lease.claim.code);   // "Otwórz swój kanister"
}
const fresh = await canisters.newClaimCodeAsyncUnsafe();    // "Nowy link": the old code stops working
await canisters.extendDemoAsyncUnsafe();                    // F-8: 15 min more
await canisters.getMineAsyncUnsafe();                       // the held demo canister (or the own one) | undefined

// The driver's front, signed in as a non-owner, with #claim=<code> in the address.
const code = claimCodeOfHash(window.location.hash);
if (code) {
    await new DriverCanistersActor(coordinatorId, identity, { host }).claimAsyncUnsafe(canisterId, code);
    // clear the fragment, reload: the driver is the owner now
}
  • 被拒绝的索赔总是 CanisterError NotAuthorized -错误,过期,使用或不使用 这个罐子的密码是一样的。
  • 租赁权归其所有者所有: getMineAsyncUnsafe, extendDemoAsyncUnsafe 和 newClaimCodeAsyncUnsafe 继续从骑手的位置工作; ownerPrincipal 的名字 司机索赔后。罐子的聊天室得到司机作为它的 接收方。
  • 相同的持有人再次租赁(没有重新安装)没有得到代码: newClaimCodeAsyncUnsafe.
  • 这些视图携带驱动筒的id:它是驱动自己的地址。

测试管理器(阶段和测试模式): TestManagerActor.hasSimulatedDriversAsyncUnsafe, spawnDriversAsyncUnsafe (12驱动程序), setSpeedUpAsyncUnsafe(request, 10) 在选择。 模拟器的报价 simulated: true.

脚本

npm run build              # tsc (src → dist) and the type-check of the tests
npm run test-unit          # no replica needed
npm run test-drivers       # (a) 5 real driver canisters, driver-bots, route-stub
npm run test-simulator     # (b) the simulator, 20 requests, 20 rides
npm run test-split         # (c) an area split during traffic
npm run test-claim         # (d) the demo lease and the claim code (U-45)
npm run test-route-line    # (e) the route line of an offer and of a ride
npm run test-ride-history  # (f) the history's places and the rating of a past ride (0.5.0)
npm run test-smoke         # one whole ride with the simulator (run by publish-public)
npm run load-local         # the load test: 200 simulated drivers, 100 riders (docs/load-test-2026-10-24.md)
npm run publish-public     # human-run: publish from master (see below)
npm run sync-declarations  # after a change of a .did (`… protocol` for the protocol and the simulator only)

端到端套件仅在LOCAL副本上运行(scripts/run-tests-local.sh):新鲜的 组,局部循环,驱动程序和模拟器wasms ../../tigi-driver-rust (建 那里,从未改变)。日志和测量值为 /tmp/claude-1000/tigi/ (TEST_LOG_DIR). 负载测试用它自己的协调器构建它的组 (tigi-sharded-coordinator-load),因此共享的本地组保持不变;它的设置是 环境变量(scripts/load-local.ts, scripts/run-load-local.sh)。

出版

只有人跑:碰撞 version 在 package.json 那么,手工(不要提交) npm run publish-public 在 master. 该脚本拒绝,除非此repo和 ../../tigi-driver-rust 都在戒酒吗 master 等于 origin/master,版本为 npm新增功能,声明匹配当前构建(core、worker、coordinator、protocol、 模拟器)和冒烟测试通过本地副本(dfx start 第一个)。这邮票 tigiSharded.commit 和 tigiDriverRust.commit 成 package.json发布,然后提交 “版本碰撞到”,然后按下。

版本与发布记录

@jsm-mit/tigi-sharded-package 共发布 14 个版本,首次发布于 2026/10/23;过去 12 个月发布了 14 个版本。

版本发布日期距今
0.12.0latest2026/11/011 天前
0.11.02026/11/012 天前
0.10.02026/11/012 天前
0.9.02026/11/012 天前
0.8.02026/10/313 天前
0.7.02026/10/313 天前

关于 @jsm-mit/tigi-sharded-package 的常见问题

@jsm-mit/tigi-sharded-package 是什么?

Tigi网络组的一个包:获取协调器的id,并隐藏协调器、核心和工作器——对于消费者来说,组是一个罐子;加上车手的几个驾驶罐端点(U-14)。

如何安装 @jsm-mit/tigi-sharded-package?

在项目目录运行 npm install @jsm-mit/tigi-sharded-package,或使用 pnpm add / yarn add / bun add。

@jsm-mit/tigi-sharded-package 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@jsm-mit/tigi-sharded-package 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@jsm-mit/tigi-sharded-package 可以商用吗?

@jsm-mit/tigi-sharded-package 使用 ISC 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@jsm-mit/tigi-sharded-package 还在维护吗?

最新版本 0.12.0 发布于 2026/11/01(1 天前);至今共发布 14 个版本,其中过去 12 个月发布了 14 个;首次发布于 2026/10/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。