@tinycloud/cli 1.1.0-beta.19 → 1.1.0-beta.21
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/dist/legacy-entry.js +548 -176
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +4 -4
package/dist/legacy-entry.js
CHANGED
|
@@ -591,8 +591,8 @@ function canonicalizeAddress(address) {
|
|
|
591
591
|
const trimmed = address.trim();
|
|
592
592
|
return trimmed.startsWith("0x") ? `0x${trimmed.slice(2).toLowerCase()}` : trimmed.toLowerCase();
|
|
593
593
|
}
|
|
594
|
-
function normalizePkhIdentifier(
|
|
595
|
-
return
|
|
594
|
+
function normalizePkhIdentifier(identifier2) {
|
|
595
|
+
return identifier2.replace(
|
|
596
596
|
/(eip155:\d+:)(0x[0-9a-fA-F]{40})(?=[:#]|$)/,
|
|
597
597
|
(_match, prefix, address) => prefix + address.toLowerCase()
|
|
598
598
|
);
|
|
@@ -7740,6 +7740,18 @@ var MAX_KV_BATCH_READ_ITEMS = 100;
|
|
|
7740
7740
|
function isJsonObject(value) {
|
|
7741
7741
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7742
7742
|
}
|
|
7743
|
+
var pathByteEncoder = new TextEncoder();
|
|
7744
|
+
function comparePathsByBytes(a, b) {
|
|
7745
|
+
const left = pathByteEncoder.encode(a);
|
|
7746
|
+
const right = pathByteEncoder.encode(b);
|
|
7747
|
+
const length4 = Math.min(left.length, right.length);
|
|
7748
|
+
for (let index = 0; index < length4; index++) {
|
|
7749
|
+
if (left[index] !== right[index]) {
|
|
7750
|
+
return left[index] - right[index];
|
|
7751
|
+
}
|
|
7752
|
+
}
|
|
7753
|
+
return left.length - right.length;
|
|
7754
|
+
}
|
|
7743
7755
|
var REVOKED_BODY = /^(?:Invalid invocation: )?delegation-(?:(revoked): [A-Za-z0-9]+|(ancestor-revoked): ancestor=[A-Za-z0-9]+ invoked=[A-Za-z0-9]+)$/;
|
|
7744
7756
|
function revocationCodeOf(body) {
|
|
7745
7757
|
const match = REVOKED_BODY.exec(body.trim());
|
|
@@ -7973,15 +7985,25 @@ var KVService = class extends BaseService {
|
|
|
7973
7985
|
)?.[1];
|
|
7974
7986
|
return contentType?.includes("application/json") ? JSON.parse(text) : text;
|
|
7975
7987
|
}
|
|
7988
|
+
/**
|
|
7989
|
+
* Validate a batch read response and return its items in the caller's
|
|
7990
|
+
* request order. The node flattens invocation abilities from a `BTreeMap`,
|
|
7991
|
+
* so results arrive sorted by UTF-8 byte order of the path, not in request
|
|
7992
|
+
* order. Items that carry their path in `key` are matched by path; if no
|
|
7993
|
+
* item carries a key, items are assumed to be in the node's byte-sorted
|
|
7994
|
+
* order and the request is aligned the same way. Any mix of keyed and
|
|
7995
|
+
* keyless items, a missing requested path, an unrequested path, or a
|
|
7996
|
+
* malformed item fails closed (returns undefined).
|
|
7997
|
+
*/
|
|
7976
7998
|
normalizeBatchReadResponse(data, paths, requireData) {
|
|
7977
7999
|
if (!data || typeof data !== "object") return void 0;
|
|
7978
8000
|
const response = data;
|
|
7979
|
-
|
|
8001
|
+
const items = response.results;
|
|
8002
|
+
if (!Array.isArray(items) || items.length !== paths.length) {
|
|
7980
8003
|
return void 0;
|
|
7981
8004
|
}
|
|
7982
|
-
for (
|
|
7983
|
-
|
|
7984
|
-
if (!item || typeof item !== "object" || item.key !== paths[index] || typeof item.ok !== "boolean") {
|
|
8005
|
+
for (const item of items) {
|
|
8006
|
+
if (!item || typeof item !== "object" || typeof item.ok !== "boolean") {
|
|
7985
8007
|
return void 0;
|
|
7986
8008
|
}
|
|
7987
8009
|
if (item.ok && (!item.headers || typeof item.headers !== "object" || Object.values(item.headers).some((value) => typeof value !== "string") || requireData && typeof item.dataBase64 !== "string")) {
|
|
@@ -7991,7 +8013,24 @@ var KVService = class extends BaseService {
|
|
|
7991
8013
|
return void 0;
|
|
7992
8014
|
}
|
|
7993
8015
|
}
|
|
7994
|
-
|
|
8016
|
+
if (items.every((item) => typeof item.key === "string")) {
|
|
8017
|
+
const byPath = new Map(items.map((item) => [item.key, item]));
|
|
8018
|
+
if (byPath.size !== items.length) return void 0;
|
|
8019
|
+
const aligned2 = [];
|
|
8020
|
+
for (const path of paths) {
|
|
8021
|
+
const item = byPath.get(path);
|
|
8022
|
+
if (!item) return void 0;
|
|
8023
|
+
aligned2.push(item);
|
|
8024
|
+
}
|
|
8025
|
+
return aligned2;
|
|
8026
|
+
}
|
|
8027
|
+
if (items.some((item) => item.key !== void 0)) return void 0;
|
|
8028
|
+
const requestOrder = paths.map((_, index) => index).sort((a, b) => comparePathsByBytes(paths[a], paths[b]));
|
|
8029
|
+
const aligned = new Array(items.length);
|
|
8030
|
+
for (let position = 0; position < requestOrder.length; position++) {
|
|
8031
|
+
aligned[requestOrder[position]] = items[position];
|
|
8032
|
+
}
|
|
8033
|
+
return aligned;
|
|
7995
8034
|
}
|
|
7996
8035
|
async batchRead(keys, action, options) {
|
|
7997
8036
|
if (!this.requireAuth()) return err(authRequiredError("kv"));
|
|
@@ -8084,19 +8123,19 @@ var KVService = class extends BaseService {
|
|
|
8084
8123
|
{ meta: { status: response.status, statusText: response.statusText } }
|
|
8085
8124
|
));
|
|
8086
8125
|
}
|
|
8087
|
-
const
|
|
8126
|
+
const items = this.normalizeBatchReadResponse(
|
|
8088
8127
|
await response.json(),
|
|
8089
8128
|
paths,
|
|
8090
8129
|
action === KVAction.GET
|
|
8091
8130
|
);
|
|
8092
|
-
if (!
|
|
8131
|
+
if (!items) {
|
|
8093
8132
|
return err(serviceError(
|
|
8094
8133
|
ErrorCodes.NETWORK_ERROR,
|
|
8095
8134
|
"KV batch read response did not match the requested keys",
|
|
8096
8135
|
"kv"
|
|
8097
8136
|
));
|
|
8098
8137
|
}
|
|
8099
|
-
const results =
|
|
8138
|
+
const results = items.map((item, index) => {
|
|
8100
8139
|
if (!item.ok) {
|
|
8101
8140
|
return {
|
|
8102
8141
|
key: keys[index],
|
|
@@ -10000,7 +10039,7 @@ var AsyncQueue = class {
|
|
|
10000
10039
|
}
|
|
10001
10040
|
const waiter = this.waiters.shift();
|
|
10002
10041
|
if (waiter) {
|
|
10003
|
-
waiter({ value, done: false });
|
|
10042
|
+
waiter.resolve({ value, done: false });
|
|
10004
10043
|
return;
|
|
10005
10044
|
}
|
|
10006
10045
|
this.values.push(value);
|
|
@@ -10012,7 +10051,22 @@ var AsyncQueue = class {
|
|
|
10012
10051
|
this.closed = true;
|
|
10013
10052
|
while (this.waiters.length > 0) {
|
|
10014
10053
|
const waiter = this.waiters.shift();
|
|
10015
|
-
waiter?.({ value: void 0, done: true });
|
|
10054
|
+
waiter?.resolve({ value: void 0, done: true });
|
|
10055
|
+
}
|
|
10056
|
+
}
|
|
10057
|
+
/**
|
|
10058
|
+
* Fail the queue: pending and future `next()` calls reject with `error`,
|
|
10059
|
+
* so a `for await` consumer sees the iterator throw.
|
|
10060
|
+
*/
|
|
10061
|
+
fail(error) {
|
|
10062
|
+
if (this.closed) {
|
|
10063
|
+
return;
|
|
10064
|
+
}
|
|
10065
|
+
this.closed = true;
|
|
10066
|
+
this.failure = error;
|
|
10067
|
+
while (this.waiters.length > 0) {
|
|
10068
|
+
const waiter = this.waiters.shift();
|
|
10069
|
+
waiter?.reject(error);
|
|
10016
10070
|
}
|
|
10017
10071
|
}
|
|
10018
10072
|
next() {
|
|
@@ -10020,20 +10074,26 @@ var AsyncQueue = class {
|
|
|
10020
10074
|
const value = this.values.shift();
|
|
10021
10075
|
return Promise.resolve({ value, done: false });
|
|
10022
10076
|
}
|
|
10077
|
+
if (this.failure !== void 0) {
|
|
10078
|
+
return Promise.reject(this.failure);
|
|
10079
|
+
}
|
|
10023
10080
|
if (this.closed) {
|
|
10024
10081
|
return Promise.resolve({ value: void 0, done: true });
|
|
10025
10082
|
}
|
|
10026
|
-
return new Promise((resolve5) => {
|
|
10027
|
-
this.waiters.push(resolve5);
|
|
10083
|
+
return new Promise((resolve5, reject) => {
|
|
10084
|
+
this.waiters.push({ resolve: resolve5, reject });
|
|
10028
10085
|
});
|
|
10029
10086
|
}
|
|
10030
10087
|
};
|
|
10088
|
+
var HOOK_STREAM_RETRY_BASE_DELAY_MS = 250;
|
|
10089
|
+
var HOOK_STREAM_RETRY_MAX_DELAY_MS = 3e4;
|
|
10090
|
+
var HOOK_STREAM_HEALTHY_MS = 5e3;
|
|
10031
10091
|
var HooksService = class extends BaseService {
|
|
10032
10092
|
constructor(config = {}) {
|
|
10033
10093
|
super();
|
|
10034
10094
|
this._subscribers = /* @__PURE__ */ new Set();
|
|
10035
|
-
this.
|
|
10036
|
-
this.
|
|
10095
|
+
this._supervising = false;
|
|
10096
|
+
this._sessionGeneration = 0;
|
|
10037
10097
|
this._config = config;
|
|
10038
10098
|
}
|
|
10039
10099
|
get config() {
|
|
@@ -10049,35 +10109,32 @@ var HooksService = class extends BaseService {
|
|
|
10049
10109
|
if (subscriptions.length === 0) {
|
|
10050
10110
|
throw new Error("At least one hook subscription is required");
|
|
10051
10111
|
}
|
|
10052
|
-
const
|
|
10112
|
+
const requested = subscriptions.map(normalizeSubscription);
|
|
10053
10113
|
const subscriber = {
|
|
10054
|
-
requested
|
|
10114
|
+
requested,
|
|
10115
|
+
signatures: requested.map(subscriptionSignature),
|
|
10055
10116
|
ttlSeconds: options.ttlSeconds,
|
|
10056
10117
|
queue: new AsyncQueue()
|
|
10057
10118
|
};
|
|
10058
|
-
|
|
10059
|
-
const abortHandler = () => {
|
|
10119
|
+
const leave = () => {
|
|
10060
10120
|
this._subscribers.delete(subscriber);
|
|
10061
10121
|
subscriber.queue.close();
|
|
10062
|
-
|
|
10122
|
+
this.wake();
|
|
10063
10123
|
};
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
} else {
|
|
10068
|
-
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
10069
|
-
}
|
|
10124
|
+
const { signal } = options;
|
|
10125
|
+
if (signal?.aborted) {
|
|
10126
|
+
return;
|
|
10070
10127
|
}
|
|
10071
|
-
|
|
10128
|
+
signal?.addEventListener("abort", leave, { once: true });
|
|
10129
|
+
this._subscribers.add(subscriber);
|
|
10130
|
+
this.wake();
|
|
10072
10131
|
try {
|
|
10073
10132
|
for await (const event of subscriber.queue) {
|
|
10074
10133
|
yield event;
|
|
10075
10134
|
}
|
|
10076
10135
|
} finally {
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
}
|
|
10080
|
-
abortHandler();
|
|
10136
|
+
signal?.removeEventListener("abort", leave);
|
|
10137
|
+
leave();
|
|
10081
10138
|
}
|
|
10082
10139
|
}
|
|
10083
10140
|
async register(webhook) {
|
|
@@ -10218,146 +10275,292 @@ var HooksService = class extends BaseService {
|
|
|
10218
10275
|
return err(wrapError2("hooks", error));
|
|
10219
10276
|
}
|
|
10220
10277
|
}
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
|
|
10229
|
-
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
if (state.signature !== this._activeSignature) {
|
|
10233
|
-
this._activeSignature = state.signature;
|
|
10234
|
-
this.abortSharedStream();
|
|
10278
|
+
/**
|
|
10279
|
+
* Called after anything that may change what the supervisor should do:
|
|
10280
|
+
* subscribe, unsubscribe, session change, sign-out. Interrupts the running
|
|
10281
|
+
* step if the plan moved away from it, and starts the supervisor if none
|
|
10282
|
+
* runs. Touches only SDK-owned state, so it never throws into callers such
|
|
10283
|
+
* as `ServiceContext.setSession`.
|
|
10284
|
+
*/
|
|
10285
|
+
wake() {
|
|
10286
|
+
const phase = this._phase;
|
|
10287
|
+
if (phase && phase.key !== this.plan()?.key) {
|
|
10288
|
+
phase.interrupt.abort();
|
|
10235
10289
|
}
|
|
10236
|
-
if (
|
|
10237
|
-
|
|
10238
|
-
if (!isAbortError(error)) {
|
|
10239
|
-
throw error;
|
|
10240
|
-
}
|
|
10241
|
-
}).finally(() => {
|
|
10242
|
-
this._sharedStreamTask = void 0;
|
|
10243
|
-
this._sharedStreamAbort = void 0;
|
|
10244
|
-
if (this._subscribers.size > 0) {
|
|
10245
|
-
void this.scheduleSharedStreamRefresh();
|
|
10246
|
-
}
|
|
10247
|
-
});
|
|
10290
|
+
if (this._supervising || this._subscribers.size === 0) {
|
|
10291
|
+
return;
|
|
10248
10292
|
}
|
|
10293
|
+
this._supervising = true;
|
|
10294
|
+
this.supervise().catch(() => this.collapse());
|
|
10249
10295
|
}
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
|
|
10296
|
+
/**
|
|
10297
|
+
* The only owner of the shared stream. Each iteration is one attempt
|
|
10298
|
+
* (mint → open → drain), then a classification and, unless the attempt was
|
|
10299
|
+
* superseded or released its subscribers, a backoff. Every await is a
|
|
10300
|
+
* `phase` that wake() can interrupt; an interrupted phase yields
|
|
10301
|
+
* `undefined`, so a superseded mint can never act. Outside `attempt` and
|
|
10302
|
+
* `pause`, which each own one catch, the loop touches only SDK state.
|
|
10303
|
+
*/
|
|
10304
|
+
async supervise() {
|
|
10305
|
+
let streak = 0;
|
|
10306
|
+
for (; ; ) {
|
|
10307
|
+
const plan = this.plan();
|
|
10308
|
+
if (!plan) {
|
|
10309
|
+
this._supervising = false;
|
|
10310
|
+
return;
|
|
10311
|
+
}
|
|
10312
|
+
const health = { openedAt: 0, delivered: false };
|
|
10313
|
+
const outcome = await this.phase(
|
|
10314
|
+
plan.key,
|
|
10315
|
+
(signal) => this.attempt(plan, signal, health)
|
|
10316
|
+
);
|
|
10317
|
+
if (health.delivered || health.openedAt > 0 && performance.now() - health.openedAt >= HOOK_STREAM_HEALTHY_MS) {
|
|
10318
|
+
streak = 0;
|
|
10319
|
+
}
|
|
10320
|
+
if (!outcome) {
|
|
10321
|
+
continue;
|
|
10322
|
+
}
|
|
10323
|
+
if (this.plan()?.key !== plan.key) {
|
|
10324
|
+
continue;
|
|
10325
|
+
}
|
|
10326
|
+
if (outcome.kind === "stopped") {
|
|
10327
|
+
this.release();
|
|
10328
|
+
continue;
|
|
10256
10329
|
}
|
|
10257
|
-
|
|
10258
|
-
|
|
10330
|
+
if (outcome.kind === "refused" && outcome.terminal) {
|
|
10331
|
+
this.release(outcome.error);
|
|
10332
|
+
this.report(outcome.error);
|
|
10333
|
+
continue;
|
|
10334
|
+
}
|
|
10335
|
+
if (outcome.kind !== "ended") {
|
|
10336
|
+
this.report(outcome.error);
|
|
10259
10337
|
}
|
|
10338
|
+
streak += 1;
|
|
10339
|
+
await this.phase(
|
|
10340
|
+
plan.key,
|
|
10341
|
+
(signal) => this.pause(streak, signal)
|
|
10342
|
+
);
|
|
10260
10343
|
}
|
|
10261
|
-
const subscriptions = [...merged.values()].sort(
|
|
10262
|
-
(left, right) => subscriptionSignature(left).localeCompare(subscriptionSignature(right))
|
|
10263
|
-
);
|
|
10264
|
-
const ttlSeconds = ttlCandidates.length > 0 ? Math.min(...ttlCandidates) : void 0;
|
|
10265
|
-
const signature = JSON.stringify({
|
|
10266
|
-
subscriptions: subscriptions.map(subscriptionSignature),
|
|
10267
|
-
ttlSeconds
|
|
10268
|
-
});
|
|
10269
|
-
return {
|
|
10270
|
-
subscriptions,
|
|
10271
|
-
ttlSeconds,
|
|
10272
|
-
signature
|
|
10273
|
-
};
|
|
10274
10344
|
}
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10345
|
+
/**
|
|
10346
|
+
* Run one step under an interrupt that wake() fires when the plan key moves
|
|
10347
|
+
* on. An interrupted step's result is discarded. The interrupt is also
|
|
10348
|
+
* fired when the step returns, which releases whatever it left open: the
|
|
10349
|
+
* request signal and its listeners, an unread response body, a timer.
|
|
10350
|
+
*/
|
|
10351
|
+
async phase(key, step) {
|
|
10352
|
+
const interrupt = new AbortController();
|
|
10353
|
+
this._phase = { key, interrupt };
|
|
10278
10354
|
try {
|
|
10279
|
-
|
|
10280
|
-
|
|
10281
|
-
|
|
10282
|
-
|
|
10283
|
-
|
|
10355
|
+
if (this.plan()?.key !== key) {
|
|
10356
|
+
interrupt.abort();
|
|
10357
|
+
}
|
|
10358
|
+
const result = await step(interrupt.signal);
|
|
10359
|
+
return interrupt.signal.aborted ? void 0 : result;
|
|
10360
|
+
} finally {
|
|
10361
|
+
this._phase = void 0;
|
|
10362
|
+
interrupt.abort();
|
|
10363
|
+
}
|
|
10364
|
+
}
|
|
10365
|
+
/**
|
|
10366
|
+
* One mint → open → drain. The single catch site for everything the
|
|
10367
|
+
* attempt touches that the SDK does not own: context accessors, invoke,
|
|
10368
|
+
* fetch, response bodies, the SSE parser. Never rejects.
|
|
10369
|
+
*/
|
|
10370
|
+
async attempt(plan, signal, health) {
|
|
10371
|
+
let phase = "mint";
|
|
10372
|
+
let request;
|
|
10373
|
+
const owns = () => !signal.aborted && !request?.signal.aborted && this.plan()?.key === plan.key;
|
|
10374
|
+
try {
|
|
10375
|
+
const context = this.context;
|
|
10376
|
+
const contextSignal = context.abortSignal;
|
|
10377
|
+
this._lastContextSignal = contextSignal;
|
|
10378
|
+
if (!context.isAuthenticated || contextSignal?.aborted) {
|
|
10379
|
+
return { kind: "stopped" };
|
|
10380
|
+
}
|
|
10381
|
+
request = this.createRequestSignal(signal, 0);
|
|
10382
|
+
const host = this.host;
|
|
10383
|
+
const minted = await awaitOwned(
|
|
10384
|
+
request.signal,
|
|
10385
|
+
() => context.fetch(`${host}/hooks/tickets`, {
|
|
10386
|
+
method: "POST",
|
|
10387
|
+
headers: {
|
|
10388
|
+
...serviceHeadersToRecord(this.createInvokeHeaders(plan.subscriptions)),
|
|
10389
|
+
"content-type": "application/json"
|
|
10390
|
+
},
|
|
10391
|
+
body: JSON.stringify({
|
|
10392
|
+
subscriptions: plan.subscriptions,
|
|
10393
|
+
ttlSeconds: plan.ttlSeconds
|
|
10394
|
+
}),
|
|
10395
|
+
signal: request.signal
|
|
10396
|
+
})
|
|
10284
10397
|
);
|
|
10285
|
-
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
|
|
10398
|
+
if (!owns()) return { kind: "stopped" };
|
|
10399
|
+
if (!minted.ok) {
|
|
10400
|
+
const status = minted.status;
|
|
10401
|
+
const error = await awaitOwned(
|
|
10402
|
+
request.signal,
|
|
10403
|
+
() => responseError("hooks", "failed to mint hook ticket", minted)
|
|
10404
|
+
);
|
|
10405
|
+
if (!owns()) return { kind: "stopped" };
|
|
10406
|
+
return { kind: "refused", terminal: isTerminalMintRefusal(status), error };
|
|
10407
|
+
}
|
|
10408
|
+
const body = await awaitOwned(request.signal, () => minted.json());
|
|
10409
|
+
if (!owns()) return { kind: "stopped" };
|
|
10410
|
+
const ticket = body !== null && typeof body === "object" && "ticket" in body ? body.ticket : void 0;
|
|
10411
|
+
if (typeof ticket !== "string" || ticket.length === 0) {
|
|
10412
|
+
throw new Error("Hook ticket response did not include a ticket");
|
|
10413
|
+
}
|
|
10414
|
+
phase = "open";
|
|
10415
|
+
if (!owns()) return { kind: "stopped" };
|
|
10416
|
+
const stream = await awaitOwned(
|
|
10417
|
+
request.signal,
|
|
10418
|
+
() => context.fetch(`${host}/hooks/events?ticket=${encodeURIComponent(ticket)}`, {
|
|
10419
|
+
method: "GET",
|
|
10420
|
+
headers: { accept: "text/event-stream" },
|
|
10421
|
+
signal: request.signal
|
|
10422
|
+
})
|
|
10289
10423
|
);
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
if (
|
|
10300
|
-
|
|
10301
|
-
|
|
10424
|
+
if (!owns()) return { kind: "stopped" };
|
|
10425
|
+
if (!stream.ok) {
|
|
10426
|
+
return { kind: "refused", terminal: false, error: hookStreamRefusal(stream.status) };
|
|
10427
|
+
}
|
|
10428
|
+
health.openedAt = performance.now();
|
|
10429
|
+
const iterator = parseSseStream(stream.body, request.signal)[Symbol.asyncIterator]();
|
|
10430
|
+
try {
|
|
10431
|
+
for (; ; ) {
|
|
10432
|
+
const next = await awaitOwned(request.signal, () => iterator.next());
|
|
10433
|
+
if (!owns()) return { kind: "stopped" };
|
|
10434
|
+
if (next.done) break;
|
|
10435
|
+
const message = next.value;
|
|
10436
|
+
if (!message.data) continue;
|
|
10437
|
+
const event = parseHookEvent(message);
|
|
10438
|
+
if (!owns()) return { kind: "stopped" };
|
|
10439
|
+
this.dispatch(event);
|
|
10440
|
+
health.delivered = true;
|
|
10302
10441
|
}
|
|
10442
|
+
} finally {
|
|
10443
|
+
const closing = iterator.return?.();
|
|
10444
|
+
if (closing) void Promise.resolve(closing).then(() => void 0, () => void 0);
|
|
10303
10445
|
}
|
|
10446
|
+
return owns() ? { kind: "ended" } : { kind: "stopped" };
|
|
10447
|
+
} catch (thrown) {
|
|
10448
|
+
return request?.signal.aborted || thrown === SUPERSEDED ? { kind: "stopped" } : { kind: "failed", error: hookStreamFailure(phase, thrown) };
|
|
10449
|
+
}
|
|
10450
|
+
}
|
|
10451
|
+
/**
|
|
10452
|
+
* Back off before the next attempt. `config.streamRetry` overrides the
|
|
10453
|
+
* delay and the clock; whatever they do, the pause ends by the delay or the
|
|
10454
|
+
* interrupt, then yields one timer turn so an instant clock cannot starve
|
|
10455
|
+
* due timers or the process.
|
|
10456
|
+
*/
|
|
10457
|
+
async pause(attempt, signal) {
|
|
10458
|
+
try {
|
|
10459
|
+
this._lastContextSignal = this.context.abortSignal;
|
|
10460
|
+
} catch {
|
|
10461
|
+
}
|
|
10462
|
+
const contextSignal = this._lastContextSignal;
|
|
10463
|
+
const waits = [];
|
|
10464
|
+
const aborts = [];
|
|
10465
|
+
try {
|
|
10466
|
+
const retry = this._config.streamRetry;
|
|
10467
|
+
const delayMs = retry?.delay?.(attempt) ?? hookStreamRetryDelay(attempt);
|
|
10468
|
+
waits.push((retry?.wait ?? sleep)(delayMs, attempt, signal));
|
|
10469
|
+
} catch {
|
|
10470
|
+
waits.push(Promise.resolve());
|
|
10471
|
+
}
|
|
10472
|
+
aborts.push(abortPromise(signal));
|
|
10473
|
+
if (contextSignal) aborts.push(abortPromise(contextSignal));
|
|
10474
|
+
try {
|
|
10475
|
+
await Promise.race([...waits, ...aborts.map(({ promise }) => promise)]);
|
|
10476
|
+
} catch {
|
|
10304
10477
|
} finally {
|
|
10305
|
-
|
|
10306
|
-
|
|
10478
|
+
for (const abort of aborts) abort.dispose();
|
|
10479
|
+
}
|
|
10480
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
10481
|
+
}
|
|
10482
|
+
/** The stream the current subscribers need, or undefined when none remain. */
|
|
10483
|
+
plan() {
|
|
10484
|
+
if (this._subscribers.size === 0) {
|
|
10485
|
+
return void 0;
|
|
10486
|
+
}
|
|
10487
|
+
const merged = /* @__PURE__ */ new Map();
|
|
10488
|
+
let ttlSeconds;
|
|
10489
|
+
for (const subscriber of this._subscribers) {
|
|
10490
|
+
subscriber.requested.forEach(
|
|
10491
|
+
(subscription, index) => merged.set(subscriber.signatures[index], subscription)
|
|
10492
|
+
);
|
|
10493
|
+
if (typeof subscriber.ttlSeconds === "number") {
|
|
10494
|
+
ttlSeconds = Math.min(subscriber.ttlSeconds, ttlSeconds ?? Infinity);
|
|
10307
10495
|
}
|
|
10308
10496
|
}
|
|
10497
|
+
const signatures = [...merged.keys()].sort();
|
|
10498
|
+
return {
|
|
10499
|
+
key: JSON.stringify([this._sessionGeneration, signatures, ttlSeconds ?? null]),
|
|
10500
|
+
subscriptions: signatures.map((signature) => merged.get(signature)),
|
|
10501
|
+
ttlSeconds
|
|
10502
|
+
};
|
|
10309
10503
|
}
|
|
10310
|
-
|
|
10311
|
-
this.
|
|
10504
|
+
dispatch(event) {
|
|
10505
|
+
for (const subscriber of this._subscribers) {
|
|
10506
|
+
if (matchesAnySubscription(event, subscriber.requested)) {
|
|
10507
|
+
subscriber.queue.push(event);
|
|
10508
|
+
}
|
|
10509
|
+
}
|
|
10312
10510
|
}
|
|
10313
|
-
|
|
10314
|
-
|
|
10511
|
+
/**
|
|
10512
|
+
* Detach every subscriber. With `error`, their iterators throw it after
|
|
10513
|
+
* draining buffered events; without, they complete.
|
|
10514
|
+
*/
|
|
10515
|
+
release(error) {
|
|
10516
|
+
const subscribers = [...this._subscribers];
|
|
10517
|
+
this._subscribers.clear();
|
|
10518
|
+
for (const { queue } of subscribers) {
|
|
10519
|
+
if (error) {
|
|
10520
|
+
queue.fail(error);
|
|
10521
|
+
} else {
|
|
10522
|
+
queue.close();
|
|
10523
|
+
}
|
|
10524
|
+
}
|
|
10315
10525
|
}
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
const ticketResponse = await this.context.fetch(`${host}/hooks/tickets`, {
|
|
10320
|
-
method: "POST",
|
|
10321
|
-
headers: {
|
|
10322
|
-
...serviceHeadersToRecord(headers),
|
|
10323
|
-
"content-type": "application/json"
|
|
10324
|
-
},
|
|
10325
|
-
body: JSON.stringify({
|
|
10326
|
-
subscriptions,
|
|
10327
|
-
ttlSeconds
|
|
10328
|
-
}),
|
|
10329
|
-
signal
|
|
10330
|
-
});
|
|
10331
|
-
if (!ticketResponse.ok) {
|
|
10332
|
-
throw await responseError(
|
|
10333
|
-
"hooks",
|
|
10334
|
-
"failed to mint hook ticket",
|
|
10335
|
-
ticketResponse
|
|
10336
|
-
);
|
|
10526
|
+
report(error) {
|
|
10527
|
+
if (error.meta && typeof error.meta === "object") {
|
|
10528
|
+
Object.freeze(error.meta);
|
|
10337
10529
|
}
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10530
|
+
Object.freeze(error);
|
|
10531
|
+
try {
|
|
10532
|
+
this.emitError(error, "stream");
|
|
10533
|
+
} catch {
|
|
10341
10534
|
}
|
|
10342
|
-
return ticketJson;
|
|
10343
10535
|
}
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10536
|
+
/**
|
|
10537
|
+
* Unreachable by construction. If the loop ever rejects anyway, fail its
|
|
10538
|
+
* subscribers visibly instead of stranding them or crashing the process.
|
|
10539
|
+
*/
|
|
10540
|
+
collapse() {
|
|
10541
|
+
this._supervising = false;
|
|
10542
|
+
this._phase?.interrupt.abort();
|
|
10543
|
+
this._phase = void 0;
|
|
10544
|
+
const error = serviceError(
|
|
10545
|
+
ErrorCodes.NETWORK_ERROR,
|
|
10546
|
+
"hook stream supervisor stopped unexpectedly",
|
|
10547
|
+
"hooks"
|
|
10352
10548
|
);
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10549
|
+
this.report(error);
|
|
10550
|
+
this.release(error);
|
|
10551
|
+
}
|
|
10552
|
+
onSessionChange(session) {
|
|
10553
|
+
super.onSessionChange(session);
|
|
10554
|
+
this._sessionGeneration += 1;
|
|
10555
|
+
this.wake();
|
|
10556
|
+
}
|
|
10557
|
+
onSignOut() {
|
|
10558
|
+
super.onSignOut();
|
|
10559
|
+
this.release();
|
|
10560
|
+
this.wake();
|
|
10561
|
+
}
|
|
10562
|
+
createHookHeaders(action, path) {
|
|
10563
|
+
return this.context.invoke(this.session, "hooks", path, action);
|
|
10361
10564
|
}
|
|
10362
10565
|
createInvokeHeaders(subscriptions) {
|
|
10363
10566
|
const entries = subscriptions.map((subscription) => ({
|
|
@@ -10388,11 +10591,168 @@ function buildScopePath(service, pathPrefix) {
|
|
|
10388
10591
|
const normalized = normalizePathPrefix(pathPrefix);
|
|
10389
10592
|
return normalized ? `${service}/${normalized}` : service;
|
|
10390
10593
|
}
|
|
10594
|
+
function hookStreamRetryDelay(attempt) {
|
|
10595
|
+
const cap = Math.min(
|
|
10596
|
+
HOOK_STREAM_RETRY_BASE_DELAY_MS * 2 ** Math.max(0, attempt - 1),
|
|
10597
|
+
HOOK_STREAM_RETRY_MAX_DELAY_MS
|
|
10598
|
+
);
|
|
10599
|
+
return cap / 2 + Math.random() * (cap / 2);
|
|
10600
|
+
}
|
|
10601
|
+
function isTerminalMintRefusal(status) {
|
|
10602
|
+
return typeof status === "number" && status >= 400 && status < 500 && status !== 408 && status !== 429;
|
|
10603
|
+
}
|
|
10604
|
+
var SUPERSEDED = /* @__PURE__ */ Symbol("hooks attempt superseded");
|
|
10605
|
+
async function awaitOwned(signal, operation) {
|
|
10606
|
+
if (signal.aborted) {
|
|
10607
|
+
if (typeof operation !== "function") {
|
|
10608
|
+
void operation.then(cancelLateResponse, () => void 0);
|
|
10609
|
+
}
|
|
10610
|
+
throw SUPERSEDED;
|
|
10611
|
+
}
|
|
10612
|
+
const promise = typeof operation === "function" ? operation() : operation;
|
|
10613
|
+
return new Promise((resolve5, reject) => {
|
|
10614
|
+
let settled = false;
|
|
10615
|
+
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
10616
|
+
const onAbort = () => {
|
|
10617
|
+
if (settled) return;
|
|
10618
|
+
settled = true;
|
|
10619
|
+
cleanup();
|
|
10620
|
+
reject(SUPERSEDED);
|
|
10621
|
+
};
|
|
10622
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
10623
|
+
promise.then(
|
|
10624
|
+
(value) => {
|
|
10625
|
+
if (settled) {
|
|
10626
|
+
cancelLateResponse(value);
|
|
10627
|
+
return;
|
|
10628
|
+
}
|
|
10629
|
+
settled = true;
|
|
10630
|
+
cleanup();
|
|
10631
|
+
resolve5(value);
|
|
10632
|
+
},
|
|
10633
|
+
(error) => {
|
|
10634
|
+
if (settled) return;
|
|
10635
|
+
settled = true;
|
|
10636
|
+
cleanup();
|
|
10637
|
+
reject(error);
|
|
10638
|
+
}
|
|
10639
|
+
);
|
|
10640
|
+
});
|
|
10641
|
+
}
|
|
10642
|
+
function cancelLateResponse(value) {
|
|
10643
|
+
if (!(value instanceof Response) || !value.body) return;
|
|
10644
|
+
try {
|
|
10645
|
+
const cancellation = value.body.cancel();
|
|
10646
|
+
void cancellation.then(() => void 0, () => void 0);
|
|
10647
|
+
} catch {
|
|
10648
|
+
}
|
|
10649
|
+
}
|
|
10650
|
+
function hookStreamRefusal(status) {
|
|
10651
|
+
const code3 = typeof status === "number" ? status : void 0;
|
|
10652
|
+
return serviceError(
|
|
10653
|
+
ErrorCodes.NETWORK_ERROR,
|
|
10654
|
+
`hook stream open refused: ${code3 ?? "unknown status"}`,
|
|
10655
|
+
"hooks",
|
|
10656
|
+
{ meta: { phase: "open", status: code3 } }
|
|
10657
|
+
);
|
|
10658
|
+
}
|
|
10659
|
+
var STREAM_ERROR_NAMES = /* @__PURE__ */ new Set([
|
|
10660
|
+
"Error",
|
|
10661
|
+
"TypeError",
|
|
10662
|
+
"AbortError",
|
|
10663
|
+
"TimeoutError",
|
|
10664
|
+
"SyntaxError",
|
|
10665
|
+
"RangeError",
|
|
10666
|
+
"NetworkError"
|
|
10667
|
+
]);
|
|
10668
|
+
function hookStreamFailure(phase, thrown) {
|
|
10669
|
+
const candidateName = readProperty(thrown, "name");
|
|
10670
|
+
const name = typeof candidateName === "string" && STREAM_ERROR_NAMES.has(candidateName) ? candidateName : "Error";
|
|
10671
|
+
const code3 = identifier(readProperty(thrown, "code")) ?? identifier(readProperty(readProperty(thrown, "cause"), "code"));
|
|
10672
|
+
return serviceError(
|
|
10673
|
+
ErrorCodes.NETWORK_ERROR,
|
|
10674
|
+
`hook stream ${phase} failed: ${name}${code3 ? ` (${code3})` : ""}`,
|
|
10675
|
+
"hooks",
|
|
10676
|
+
{ meta: { phase, errorName: name, ...code3 ? { errorCode: code3 } : {} } }
|
|
10677
|
+
);
|
|
10678
|
+
}
|
|
10679
|
+
var STREAM_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
10680
|
+
"ECONNREFUSED",
|
|
10681
|
+
"ECONNRESET",
|
|
10682
|
+
"ECONNABORTED",
|
|
10683
|
+
"ETIMEDOUT",
|
|
10684
|
+
"EPIPE",
|
|
10685
|
+
"ENOTFOUND",
|
|
10686
|
+
"EAI_AGAIN",
|
|
10687
|
+
"EHOSTUNREACH",
|
|
10688
|
+
"ENETUNREACH",
|
|
10689
|
+
"ENETDOWN",
|
|
10690
|
+
"EADDRNOTAVAIL",
|
|
10691
|
+
"UND_ERR_SOCKET",
|
|
10692
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
10693
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
10694
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
10695
|
+
"UND_ERR_CLOSED",
|
|
10696
|
+
"UND_ERR_ABORTED",
|
|
10697
|
+
"UND_ERR_RESPONSE_STATUS_CODE",
|
|
10698
|
+
"ERR_TLS_CERT_ALTNAME_INVALID",
|
|
10699
|
+
"CERT_HAS_EXPIRED",
|
|
10700
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
10701
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
10702
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
10703
|
+
"ERR_SSL_WRONG_VERSION_NUMBER"
|
|
10704
|
+
]);
|
|
10705
|
+
function identifier(value) {
|
|
10706
|
+
return typeof value === "string" && STREAM_ERROR_CODES.has(value) ? value : void 0;
|
|
10707
|
+
}
|
|
10708
|
+
function readProperty(value, key) {
|
|
10709
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") {
|
|
10710
|
+
return void 0;
|
|
10711
|
+
}
|
|
10712
|
+
try {
|
|
10713
|
+
return Reflect.get(value, key);
|
|
10714
|
+
} catch {
|
|
10715
|
+
return void 0;
|
|
10716
|
+
}
|
|
10717
|
+
}
|
|
10718
|
+
function sleep(delayMs, _attempt, signal) {
|
|
10719
|
+
return new Promise((resolve5) => {
|
|
10720
|
+
const finish = () => {
|
|
10721
|
+
clearTimeout(timer);
|
|
10722
|
+
signal.removeEventListener("abort", stop);
|
|
10723
|
+
resolve5();
|
|
10724
|
+
};
|
|
10725
|
+
const timer = setTimeout(finish, delayMs);
|
|
10726
|
+
timer.unref?.();
|
|
10727
|
+
const stop = () => finish();
|
|
10728
|
+
if (signal.aborted) {
|
|
10729
|
+
finish();
|
|
10730
|
+
} else {
|
|
10731
|
+
signal.addEventListener("abort", stop, { once: true });
|
|
10732
|
+
}
|
|
10733
|
+
});
|
|
10734
|
+
}
|
|
10735
|
+
function abortPromise(signal) {
|
|
10736
|
+
let resolve5;
|
|
10737
|
+
const promise = new Promise((done) => {
|
|
10738
|
+
resolve5 = done;
|
|
10739
|
+
});
|
|
10740
|
+
const onAbort = () => resolve5();
|
|
10741
|
+
if (signal.aborted) {
|
|
10742
|
+
resolve5();
|
|
10743
|
+
} else {
|
|
10744
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
10745
|
+
}
|
|
10746
|
+
return {
|
|
10747
|
+
promise,
|
|
10748
|
+
dispose: () => signal.removeEventListener("abort", onAbort)
|
|
10749
|
+
};
|
|
10750
|
+
}
|
|
10391
10751
|
function normalizeSubscription(subscription) {
|
|
10392
10752
|
return {
|
|
10393
10753
|
...subscription,
|
|
10394
10754
|
pathPrefix: normalizePathPrefix(subscription.pathPrefix),
|
|
10395
|
-
abilities: subscription.abilities
|
|
10755
|
+
abilities: subscription.abilities ? [...subscription.abilities] : []
|
|
10396
10756
|
};
|
|
10397
10757
|
}
|
|
10398
10758
|
function subscriptionSignature(subscription) {
|
|
@@ -10458,9 +10818,6 @@ async function responseError(service, message, response) {
|
|
|
10458
10818
|
meta: { ...error.meta, status: response.status, statusText: response.statusText }
|
|
10459
10819
|
};
|
|
10460
10820
|
}
|
|
10461
|
-
function isAbortError(error) {
|
|
10462
|
-
return error instanceof DOMException && error.name === "AbortError";
|
|
10463
|
-
}
|
|
10464
10821
|
async function* parseSseStream(body, signal) {
|
|
10465
10822
|
if (!body) {
|
|
10466
10823
|
throw new Error("Hook stream response does not expose a readable body");
|
|
@@ -10489,11 +10846,24 @@ async function* parseSseStream(body, signal) {
|
|
|
10489
10846
|
async function* readBodyChunks(body, signal) {
|
|
10490
10847
|
const asyncIterable = body;
|
|
10491
10848
|
if (typeof asyncIterable?.[Symbol.asyncIterator] === "function") {
|
|
10492
|
-
|
|
10493
|
-
|
|
10494
|
-
|
|
10849
|
+
const iterator = asyncIterable[Symbol.asyncIterator]();
|
|
10850
|
+
const cancel = () => {
|
|
10851
|
+
try {
|
|
10852
|
+
const closing = iterator.return?.();
|
|
10853
|
+
if (closing) void Promise.resolve(closing).then(() => void 0, () => void 0);
|
|
10854
|
+
} catch {
|
|
10855
|
+
}
|
|
10856
|
+
};
|
|
10857
|
+
signal?.addEventListener("abort", cancel, { once: true });
|
|
10858
|
+
try {
|
|
10859
|
+
while (!signal?.aborted) {
|
|
10860
|
+
const next = await iterator.next();
|
|
10861
|
+
if (next.done) break;
|
|
10862
|
+
yield next.value;
|
|
10495
10863
|
}
|
|
10496
|
-
|
|
10864
|
+
} finally {
|
|
10865
|
+
signal?.removeEventListener("abort", cancel);
|
|
10866
|
+
cancel();
|
|
10497
10867
|
}
|
|
10498
10868
|
return;
|
|
10499
10869
|
}
|
|
@@ -10502,21 +10872,23 @@ async function* readBodyChunks(body, signal) {
|
|
|
10502
10872
|
throw new Error("Unsupported hook stream body type");
|
|
10503
10873
|
}
|
|
10504
10874
|
const reader = stream.getReader();
|
|
10875
|
+
const cancelReader = () => {
|
|
10876
|
+
try {
|
|
10877
|
+
const cancellation = reader.cancel?.();
|
|
10878
|
+
if (cancellation) void cancellation.then(() => void 0, () => void 0);
|
|
10879
|
+
} catch {
|
|
10880
|
+
}
|
|
10881
|
+
};
|
|
10882
|
+
signal?.addEventListener("abort", cancelReader, { once: true });
|
|
10505
10883
|
try {
|
|
10506
10884
|
while (!signal?.aborted) {
|
|
10507
10885
|
const { done, value } = await reader.read();
|
|
10508
|
-
if (done)
|
|
10509
|
-
|
|
10510
|
-
}
|
|
10511
|
-
if (value) {
|
|
10512
|
-
yield value;
|
|
10513
|
-
}
|
|
10886
|
+
if (done) break;
|
|
10887
|
+
if (value) yield value;
|
|
10514
10888
|
}
|
|
10515
10889
|
} finally {
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
} catch {
|
|
10519
|
-
}
|
|
10890
|
+
signal?.removeEventListener("abort", cancelReader);
|
|
10891
|
+
cancelReader();
|
|
10520
10892
|
reader.releaseLock?.();
|
|
10521
10893
|
}
|
|
10522
10894
|
}
|
|
@@ -12897,10 +13269,10 @@ var EncryptionService = class extends BaseService {
|
|
|
12897
13269
|
get crypto() {
|
|
12898
13270
|
return this._config.crypto;
|
|
12899
13271
|
}
|
|
12900
|
-
async discoverNetwork(
|
|
13272
|
+
async discoverNetwork(identifier2, ownerDid) {
|
|
12901
13273
|
this.assertActive();
|
|
12902
13274
|
const result = await discoverNetwork({
|
|
12903
|
-
identifier,
|
|
13275
|
+
identifier: identifier2,
|
|
12904
13276
|
...ownerDid !== void 0 ? { ownerDid } : {},
|
|
12905
13277
|
...this._config.node !== void 0 ? { node: this._config.node } : {},
|
|
12906
13278
|
...this._config.wellKnown !== void 0 ? { wellKnown: this._config.wellKnown } : {}
|
|
@@ -30222,17 +30594,17 @@ function validRecipientDid(value) {
|
|
|
30222
30594
|
if (value.length === 0 || value.length > 2048 || /[\u0000-\u0020\u007f]/.test(value)) return false;
|
|
30223
30595
|
const parts = value.split(":");
|
|
30224
30596
|
if (parts.length < 3 || parts[0] !== "did" || !/^[a-z0-9]+$/.test(parts[1] ?? "")) return false;
|
|
30225
|
-
const
|
|
30226
|
-
if (
|
|
30597
|
+
const identifier2 = parts.slice(2);
|
|
30598
|
+
if (identifier2.some((part) => part.length === 0)) return false;
|
|
30227
30599
|
if (parts[1] === "web") {
|
|
30228
|
-
const host =
|
|
30600
|
+
const host = identifier2[0] ?? "";
|
|
30229
30601
|
if (host.length > 253 || host.split(".").some((label) => !label || label.length > 63 || !/^[A-Za-z0-9-]+$/.test(label) || label.startsWith("-") || label.endsWith("-"))) return false;
|
|
30230
|
-
return
|
|
30602
|
+
return identifier2.slice(1).every((part) => /^[A-Za-z0-9._%-]+$/.test(part));
|
|
30231
30603
|
}
|
|
30232
|
-
if (parts[1] === "pkh") return
|
|
30604
|
+
if (parts[1] === "pkh") return identifier2.length >= 3 && identifier2.every((part) => /^[A-Za-z0-9._%-]+$/.test(part));
|
|
30233
30605
|
if (parts[1] === "key") {
|
|
30234
30606
|
try {
|
|
30235
|
-
const bytes3 = base58btc2.decode(
|
|
30607
|
+
const bytes3 = base58btc2.decode(identifier2.join(":"));
|
|
30236
30608
|
return bytes3.length === 34 && bytes3[0] === 237 && bytes3[1] === 1;
|
|
30237
30609
|
} catch {
|
|
30238
30610
|
return false;
|