@openclaw/imessage 2026.8.1-beta.3 → 2026.8.1
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/{accounts-D00Njxg2.js → accounts-CCirBEIm.js} +4 -5
- package/dist/{actions.runtime-B_xJiBXF.js → actions.runtime-CAl3WcqD.js} +2 -3
- package/dist/api.js +6 -5
- package/dist/{approval-handler.runtime-BKku8aUf.js → approval-handler.runtime-UBd1j2Sg.js} +7 -7
- package/dist/{approval-reactions-BME5eA6T.js → approval-reactions-LL5yn1Rc.js} +5 -9
- package/dist/{channel-BFQTpvrG.js → channel-DNI9DKGN.js} +17 -16
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-BjkXwX9_.js → channel.runtime-HSkrmWfW.js} +58 -11
- package/dist/{config-schema-DV0KP5nC.js → config-schema-DuGbA8Kp.js} +8 -7
- package/dist/contract-api.js +1 -1
- package/dist/doctor-contract-api.js +8 -281
- package/dist/{group-policy-Ca3c5KES.js → group-policy-Dvc-BcWo.js} +12 -17
- package/dist/{media-contract-BEgdvYhW.js → media-contract-D-FhGE98.js} +1 -1
- package/dist/media-contract-api.js +1 -1
- package/dist/{message-tool-api-TZiNYA17.js → message-tool-api-CvIgJpOo.js} +3 -3
- package/dist/message-tool-api.js +1 -1
- package/dist/{monitor-Uv5RaRTk.js → monitor-BywJn1QW.js} +289 -28
- package/dist/{question-reactions-to0i8vxE.js → question-reactions-viP7ZcyG.js} +1 -1
- package/dist/{reaction-context-DaNrZqYo.js → reaction-context-BAYI7pz0.js} +2 -2
- package/dist/{remote-file-CDuk0joc.js → remote-file-6mCj2ZBy.js} +2 -2
- package/dist/runtime-Cza4CY5T.js +8 -0
- package/dist/runtime-api.js +10 -9
- package/dist/{sanitize-outbound-DaLmdkbf.js → sanitize-outbound-Du-r1lNQ.js} +344 -57
- package/dist/{send-Dake4Awx.js → send-mgWPKqSV.js} +132 -11
- package/dist/state-contract-4TK_cDDb.js +56 -0
- package/dist/state-migrations-SFdKp3GQ.js +277 -0
- package/node_modules/typebox/build/format/date.d.mts +1 -1
- package/node_modules/typebox/build/format/date.mjs +1 -1
- package/node_modules/typebox/build/format/date_time.d.mts +2 -2
- package/node_modules/typebox/build/format/date_time.mjs +3 -3
- package/node_modules/typebox/build/format/email.d.mts +1 -1
- package/node_modules/typebox/build/format/email.mjs +2 -2
- package/node_modules/typebox/build/format/hostname.mjs +2 -10
- package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
- package/node_modules/typebox/build/format/idn_email.mjs +3 -3
- package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
- package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
- package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
- package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
- package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
- package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
- package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +23 -0
- package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
- package/node_modules/typebox/build/format/idna/index.mjs +2 -0
- package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
- package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
- package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
- package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
- package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
- package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
- package/node_modules/typebox/build/format/ipv4.mjs +2 -32
- package/node_modules/typebox/build/format/ipv6.mjs +2 -61
- package/node_modules/typebox/build/format/iri.d.mts +2 -2
- package/node_modules/typebox/build/format/iri.mjs +26 -8
- package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
- package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
- package/node_modules/typebox/build/format/regex.d.mts +1 -2
- package/node_modules/typebox/build/format/regex.mjs +2 -6
- package/node_modules/typebox/build/format/time.d.mts +2 -2
- package/node_modules/typebox/build/format/time.mjs +19 -14
- package/node_modules/typebox/build/format/uri.d.mts +1 -1
- package/node_modules/typebox/build/format/uri.mjs +3 -130
- package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
- package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
- package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
- package/node_modules/typebox/build/format/uri_template.mjs +4 -5
- package/node_modules/typebox/build/format/url.d.mts +3 -3
- package/node_modules/typebox/build/format/url.mjs +4 -5
- package/node_modules/typebox/build/guard/emit.d.mts +3 -1
- package/node_modules/typebox/build/guard/emit.mjs +12 -5
- package/node_modules/typebox/build/guard/guard.d.mts +8 -2
- package/node_modules/typebox/build/guard/guard.mjs +25 -4
- package/node_modules/typebox/build/guard/string.d.mts +2 -2
- package/node_modules/typebox/build/guard/string.mjs +7 -7
- package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
- package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
- package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
- package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
- package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
- package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
- package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
- package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
- package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
- package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
- package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
- package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
- package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
- package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
- package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
- package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
- package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
- package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
- package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
- package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
- package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
- package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
- package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
- package/node_modules/typebox/build/schema/types/index.mjs +0 -2
- package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
- package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
- package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
- package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
- package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
- package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
- package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
- package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
- package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
- package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
- package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
- package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
- package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
- package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
- package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
- package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
- package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
- package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
- package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
- package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
- package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
- package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
- package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
- package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
- package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
- package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
- package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
- package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
- package/node_modules/typebox/build/type/script/parser.mjs +27 -23
- package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
- package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
- package/node_modules/typebox/package.json +27 -27
- package/node_modules/typebox/readme.md +79 -82
- package/openclaw.plugin.json +0 -6
- package/package.json +5 -5
- package/dist/catchup-Dh-Rv4Ur.js +0 -294
- package/dist/monitor-reply-cache-BdeUQaHO.js +0 -353
- package/dist/persisted-echo-cache-DGxy-J4t.js +0 -143
- package/node_modules/typebox/build/format/_idna.d.mts +0 -2
- package/node_modules/typebox/build/format/_idna.mjs +0 -246
- package/node_modules/typebox/build/format/_puny.d.mts +0 -1
- package/node_modules/typebox/build/format/_puny.mjs +0 -77
- package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
- package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
- package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
- package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
- package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
- package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { a as resolveDefaultIMessageAccountId, i as listIMessageAccountIds, o as resolveIMessageAccount } from "./accounts-CCirBEIm.js";
|
|
2
|
+
import { a as IMESSAGE_REPLY_CACHE_NAMESPACE, c as IMESSAGE_SENT_ECHOES_TTL_MS, f as resolveIMessageReplyCacheEntryKey, i as IMESSAGE_REPLY_CACHE_MAX_ENTRIES, l as capFailureRetriesMap, n as IMESSAGE_REPLY_CACHE_COUNTER_KEY, o as IMESSAGE_REPLY_CACHE_TTL_MS, p as resolveIMessageSentEchoEntryKey, r as IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE, s as IMESSAGE_SENT_ECHOES_NAMESPACE, t as IMESSAGE_CATCHUP_CURSOR_NAMESPACE, u as resolveIMessageCatchupCursorKey } from "./state-contract-4TK_cDDb.js";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { asFiniteNumber, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import { fileExists } from "openclaw/plugin-sdk/security-runtime";
|
|
8
|
+
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
9
|
+
//#region extensions/imessage/src/state-migrations.ts
|
|
10
|
+
function resolveMigrationStateDir(params) {
|
|
11
|
+
return params.stateDir ?? resolveStateDir(params.env);
|
|
12
|
+
}
|
|
13
|
+
function remainingTtlMs(timestamp, ttlMs) {
|
|
14
|
+
const remaining = ttlMs - Math.max(0, Date.now() - timestamp);
|
|
15
|
+
return remaining > 0 ? remaining : void 0;
|
|
16
|
+
}
|
|
17
|
+
function readJsonl(pathValue) {
|
|
18
|
+
try {
|
|
19
|
+
return fs.readFileSync(pathValue, "utf8").split(/\n+/).flatMap((line) => {
|
|
20
|
+
if (!line) return [];
|
|
21
|
+
try {
|
|
22
|
+
return [JSON.parse(line)];
|
|
23
|
+
} catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
} catch (err) {
|
|
28
|
+
throw new Error(`Failed reading ${pathValue}: ${String(err)}`, { cause: err });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function parseReplyCacheEntry(raw) {
|
|
32
|
+
if (!raw || typeof raw !== "object") return null;
|
|
33
|
+
const parsed = raw;
|
|
34
|
+
if (typeof parsed.accountId !== "string" || typeof parsed.messageId !== "string" || typeof parsed.shortId !== "string" || typeof parsed.timestamp !== "number") return null;
|
|
35
|
+
return {
|
|
36
|
+
accountId: parsed.accountId,
|
|
37
|
+
messageId: parsed.messageId,
|
|
38
|
+
shortId: parsed.shortId,
|
|
39
|
+
timestamp: parsed.timestamp,
|
|
40
|
+
...typeof parsed.chatGuid === "string" ? { chatGuid: parsed.chatGuid } : {},
|
|
41
|
+
...typeof parsed.chatIdentifier === "string" ? { chatIdentifier: parsed.chatIdentifier } : {},
|
|
42
|
+
...typeof parsed.chatId === "number" ? { chatId: parsed.chatId } : {},
|
|
43
|
+
...typeof parsed.isFromMe === "boolean" ? { isFromMe: parsed.isFromMe } : {}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function readReplyCacheMaxShortId(sourcePath) {
|
|
47
|
+
let max = 0;
|
|
48
|
+
for (const raw of readJsonl(sourcePath)) {
|
|
49
|
+
if (!raw || typeof raw !== "object") continue;
|
|
50
|
+
const shortId = raw.shortId;
|
|
51
|
+
if (typeof shortId !== "string") continue;
|
|
52
|
+
const numeric = Number.parseInt(shortId, 10);
|
|
53
|
+
if (Number.isFinite(numeric) && numeric > max) max = numeric;
|
|
54
|
+
}
|
|
55
|
+
return max;
|
|
56
|
+
}
|
|
57
|
+
function readReplyCounterValue(value) {
|
|
58
|
+
if (!value || typeof value !== "object") return null;
|
|
59
|
+
const counter = value.counter;
|
|
60
|
+
return asFiniteNumber(counter) ?? null;
|
|
61
|
+
}
|
|
62
|
+
function shouldReplaceReplyCounter(existingValue, incomingValue) {
|
|
63
|
+
const incomingCounter = readReplyCounterValue(incomingValue);
|
|
64
|
+
if (incomingCounter === null) return false;
|
|
65
|
+
const existingCounter = readReplyCounterValue(existingValue);
|
|
66
|
+
return existingCounter === null || incomingCounter > existingCounter;
|
|
67
|
+
}
|
|
68
|
+
function parseSentEchoEntry(raw) {
|
|
69
|
+
if (!raw || typeof raw !== "object") return null;
|
|
70
|
+
const parsed = raw;
|
|
71
|
+
if (typeof parsed.scope !== "string" || typeof parsed.timestamp !== "number") return null;
|
|
72
|
+
return {
|
|
73
|
+
scope: parsed.scope,
|
|
74
|
+
timestamp: parsed.timestamp,
|
|
75
|
+
...typeof parsed.text === "string" ? { text: parsed.text } : {},
|
|
76
|
+
...typeof parsed.messageId === "string" ? { messageId: parsed.messageId } : {}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function listReplyCacheEntries(sourcePath) {
|
|
80
|
+
const entriesByKey = /* @__PURE__ */ new Map();
|
|
81
|
+
for (const entry of readJsonl(sourcePath).map(parseReplyCacheEntry)) {
|
|
82
|
+
if (!entry) continue;
|
|
83
|
+
const ttlMs = remainingTtlMs(entry.timestamp, IMESSAGE_REPLY_CACHE_TTL_MS);
|
|
84
|
+
if (!ttlMs) continue;
|
|
85
|
+
const key = resolveIMessageReplyCacheEntryKey(entry.messageId);
|
|
86
|
+
entriesByKey.delete(key);
|
|
87
|
+
entriesByKey.set(key, {
|
|
88
|
+
value: entry,
|
|
89
|
+
ttlMs
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return [...entriesByKey.entries()].slice(-IMESSAGE_REPLY_CACHE_MAX_ENTRIES).map(([key, entry]) => ({
|
|
93
|
+
key,
|
|
94
|
+
value: entry.value,
|
|
95
|
+
ttlMs: entry.ttlMs
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
function listSentEchoEntries(sourcePath) {
|
|
99
|
+
return readJsonl(sourcePath).map(parseSentEchoEntry).filter((entry) => Boolean(entry)).slice(-256).flatMap((entry) => {
|
|
100
|
+
const ttlMs = remainingTtlMs(entry.timestamp, IMESSAGE_SENT_ECHOES_TTL_MS);
|
|
101
|
+
if (!ttlMs) return [];
|
|
102
|
+
return [{
|
|
103
|
+
key: resolveIMessageSentEchoEntryKey(entry),
|
|
104
|
+
value: entry,
|
|
105
|
+
ttlMs
|
|
106
|
+
}];
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function resolveLegacyCatchupCursorPath(stateDir, accountId) {
|
|
110
|
+
const safePrefix = accountId.replace(/[^a-zA-Z0-9_-]/g, "_") || "account";
|
|
111
|
+
const hash = createHash("sha256").update(accountId, "utf8").digest("hex").slice(0, 12);
|
|
112
|
+
return path.join(stateDir, "imessage", "catchup", `${safePrefix}__${hash}.json`);
|
|
113
|
+
}
|
|
114
|
+
function listLegacyCatchupCursorPaths(stateDir) {
|
|
115
|
+
const dir = path.join(stateDir, "imessage", "catchup");
|
|
116
|
+
try {
|
|
117
|
+
return fs.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => path.join(dir, entry.name));
|
|
118
|
+
} catch {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function normalizeCatchupCursor(raw) {
|
|
123
|
+
if (!raw || typeof raw !== "object") return null;
|
|
124
|
+
const value = raw;
|
|
125
|
+
if (typeof value.lastSeenMs !== "number" || !Number.isFinite(value.lastSeenMs) || typeof value.lastSeenRowid !== "number" || !Number.isFinite(value.lastSeenRowid)) return null;
|
|
126
|
+
const failureRetries = sanitizeCatchupFailureRetries(value.failureRetries);
|
|
127
|
+
const hasRetries = Object.keys(failureRetries).length > 0;
|
|
128
|
+
return {
|
|
129
|
+
lastSeenMs: value.lastSeenMs,
|
|
130
|
+
lastSeenRowid: value.lastSeenRowid,
|
|
131
|
+
updatedAt: typeof value.updatedAt === "number" ? value.updatedAt : 0,
|
|
132
|
+
...hasRetries ? { failureRetries } : {}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function readCatchupCursor(sourcePath) {
|
|
136
|
+
let parsed;
|
|
137
|
+
try {
|
|
138
|
+
parsed = JSON.parse(fs.readFileSync(sourcePath, "utf8"));
|
|
139
|
+
} catch (err) {
|
|
140
|
+
throw new Error(`Failed reading ${sourcePath}: ${String(err)}`, { cause: err });
|
|
141
|
+
}
|
|
142
|
+
const cursor = normalizeCatchupCursor(parsed);
|
|
143
|
+
if (!cursor) throw new Error(`Invalid iMessage catchup cursor: ${sourcePath}`);
|
|
144
|
+
return cursor;
|
|
145
|
+
}
|
|
146
|
+
function sanitizeCatchupFailureRetries(raw) {
|
|
147
|
+
if (!raw || typeof raw !== "object") return {};
|
|
148
|
+
const out = {};
|
|
149
|
+
for (const [guid, count] of Object.entries(raw)) if (typeof count === "number" && Number.isFinite(count) && count > 0) out[guid] = Math.floor(count);
|
|
150
|
+
return capFailureRetriesMap(out);
|
|
151
|
+
}
|
|
152
|
+
function shouldReplaceCatchupCursor(existingValue, incomingValue) {
|
|
153
|
+
const incoming = normalizeCatchupCursor(incomingValue);
|
|
154
|
+
if (!incoming) return false;
|
|
155
|
+
const existing = normalizeCatchupCursor(existingValue);
|
|
156
|
+
return !existing || incoming.lastSeenRowid > existing.lastSeenRowid || incoming.lastSeenRowid === existing.lastSeenRowid && incoming.lastSeenMs > existing.lastSeenMs;
|
|
157
|
+
}
|
|
158
|
+
function detectReplyCacheMigration(params) {
|
|
159
|
+
const stateDir = resolveMigrationStateDir(params);
|
|
160
|
+
const sourcePath = path.join(stateDir, "imessage", "reply-cache.jsonl");
|
|
161
|
+
if (!fileExists(sourcePath)) return [];
|
|
162
|
+
const plans = [];
|
|
163
|
+
plans.push({
|
|
164
|
+
kind: "plugin-state-import",
|
|
165
|
+
label: "iMessage reply short-id counter",
|
|
166
|
+
sourcePath,
|
|
167
|
+
targetPath: `plugin state:${IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE}`,
|
|
168
|
+
pluginId: "imessage",
|
|
169
|
+
namespace: IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE,
|
|
170
|
+
maxEntries: 1,
|
|
171
|
+
scopeKey: "",
|
|
172
|
+
stateDir,
|
|
173
|
+
preview: `- iMessage reply short-id counter: ${sourcePath} → plugin state (${IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE})`,
|
|
174
|
+
readEntries: () => {
|
|
175
|
+
const maxShortId = readReplyCacheMaxShortId(sourcePath);
|
|
176
|
+
return maxShortId > 0 ? [{
|
|
177
|
+
key: IMESSAGE_REPLY_CACHE_COUNTER_KEY,
|
|
178
|
+
value: { counter: maxShortId }
|
|
179
|
+
}] : [];
|
|
180
|
+
},
|
|
181
|
+
shouldReplaceExistingEntry: ({ existingValue, incomingValue }) => shouldReplaceReplyCounter(existingValue, incomingValue)
|
|
182
|
+
});
|
|
183
|
+
plans.push({
|
|
184
|
+
kind: "plugin-state-import",
|
|
185
|
+
label: "iMessage reply short-id cache",
|
|
186
|
+
sourcePath,
|
|
187
|
+
targetPath: `plugin state:${IMESSAGE_REPLY_CACHE_NAMESPACE}`,
|
|
188
|
+
pluginId: "imessage",
|
|
189
|
+
namespace: IMESSAGE_REPLY_CACHE_NAMESPACE,
|
|
190
|
+
maxEntries: IMESSAGE_REPLY_CACHE_MAX_ENTRIES,
|
|
191
|
+
scopeKey: "",
|
|
192
|
+
stateDir,
|
|
193
|
+
cleanupSource: "rename",
|
|
194
|
+
cleanupWhenEmpty: true,
|
|
195
|
+
preview: `- iMessage reply short-id cache: ${sourcePath} → plugin state (${IMESSAGE_REPLY_CACHE_NAMESPACE})`,
|
|
196
|
+
readEntries: () => listReplyCacheEntries(sourcePath)
|
|
197
|
+
});
|
|
198
|
+
return plans;
|
|
199
|
+
}
|
|
200
|
+
function detectSentEchoMigration(params) {
|
|
201
|
+
const stateDir = resolveMigrationStateDir(params);
|
|
202
|
+
const sourcePath = path.join(stateDir, "imessage", "sent-echoes.jsonl");
|
|
203
|
+
if (!fileExists(sourcePath)) return [];
|
|
204
|
+
return [{
|
|
205
|
+
kind: "plugin-state-import",
|
|
206
|
+
label: "iMessage sent-echo dedupe cache",
|
|
207
|
+
sourcePath,
|
|
208
|
+
targetPath: `plugin state:${IMESSAGE_SENT_ECHOES_NAMESPACE}`,
|
|
209
|
+
pluginId: "imessage",
|
|
210
|
+
namespace: IMESSAGE_SENT_ECHOES_NAMESPACE,
|
|
211
|
+
maxEntries: 256,
|
|
212
|
+
scopeKey: "",
|
|
213
|
+
stateDir,
|
|
214
|
+
cleanupSource: "rename",
|
|
215
|
+
cleanupWhenEmpty: true,
|
|
216
|
+
preview: `- iMessage sent-echo dedupe cache: ${sourcePath} → plugin state (${IMESSAGE_SENT_ECHOES_NAMESPACE})`,
|
|
217
|
+
readEntries: () => listSentEchoEntries(sourcePath)
|
|
218
|
+
}];
|
|
219
|
+
}
|
|
220
|
+
function detectCatchupCursorMigrations(params) {
|
|
221
|
+
const stateDir = resolveMigrationStateDir(params);
|
|
222
|
+
const accountIds = uniqueStrings([resolveDefaultIMessageAccountId(params.cfg), ...listIMessageAccountIds(params.cfg)].map((accountId) => resolveIMessageAccount({
|
|
223
|
+
cfg: params.cfg,
|
|
224
|
+
accountId
|
|
225
|
+
}).accountId));
|
|
226
|
+
const configuredPaths = new Set(accountIds.map((accountId) => resolveLegacyCatchupCursorPath(stateDir, accountId)));
|
|
227
|
+
const configuredPlans = accountIds.flatMap((accountId) => {
|
|
228
|
+
const sourcePath = resolveLegacyCatchupCursorPath(stateDir, accountId);
|
|
229
|
+
if (!fileExists(sourcePath)) return [];
|
|
230
|
+
return {
|
|
231
|
+
kind: "plugin-state-import",
|
|
232
|
+
label: "iMessage catchup cursor",
|
|
233
|
+
sourcePath,
|
|
234
|
+
targetPath: `plugin state:${IMESSAGE_CATCHUP_CURSOR_NAMESPACE}`,
|
|
235
|
+
pluginId: "imessage",
|
|
236
|
+
namespace: IMESSAGE_CATCHUP_CURSOR_NAMESPACE,
|
|
237
|
+
maxEntries: 256,
|
|
238
|
+
scopeKey: "",
|
|
239
|
+
stateDir,
|
|
240
|
+
cleanupSource: "rename",
|
|
241
|
+
preview: `- iMessage catchup cursor: ${sourcePath} → plugin state (${IMESSAGE_CATCHUP_CURSOR_NAMESPACE})`,
|
|
242
|
+
readEntries: () => {
|
|
243
|
+
const cursor = readCatchupCursor(sourcePath);
|
|
244
|
+
return [{
|
|
245
|
+
key: resolveIMessageCatchupCursorKey(accountId),
|
|
246
|
+
value: cursor
|
|
247
|
+
}];
|
|
248
|
+
},
|
|
249
|
+
shouldReplaceExistingEntry: (replaceParams) => shouldReplaceCatchupCursor(replaceParams.existingValue, replaceParams.incomingValue)
|
|
250
|
+
};
|
|
251
|
+
});
|
|
252
|
+
const orphanPlans = listLegacyCatchupCursorPaths(stateDir).filter((sourcePath) => !configuredPaths.has(sourcePath)).map((sourcePath) => ({
|
|
253
|
+
kind: "plugin-state-import",
|
|
254
|
+
label: "iMessage orphan catchup cursor",
|
|
255
|
+
sourcePath,
|
|
256
|
+
targetPath: `plugin state:${IMESSAGE_CATCHUP_CURSOR_NAMESPACE}`,
|
|
257
|
+
pluginId: "imessage",
|
|
258
|
+
namespace: IMESSAGE_CATCHUP_CURSOR_NAMESPACE,
|
|
259
|
+
maxEntries: 256,
|
|
260
|
+
scopeKey: "",
|
|
261
|
+
stateDir,
|
|
262
|
+
cleanupSource: "rename",
|
|
263
|
+
cleanupWhenEmpty: true,
|
|
264
|
+
preview: `- iMessage orphan catchup cursor: ${sourcePath} → archived legacy state`,
|
|
265
|
+
readEntries: () => []
|
|
266
|
+
}));
|
|
267
|
+
return [...configuredPlans, ...orphanPlans];
|
|
268
|
+
}
|
|
269
|
+
async function detectIMessageLegacyStateMigrations(params) {
|
|
270
|
+
return [
|
|
271
|
+
...detectCatchupCursorMigrations(params),
|
|
272
|
+
...detectReplyCacheMigration(params),
|
|
273
|
+
...detectSentEchoMigration(params)
|
|
274
|
+
];
|
|
275
|
+
}
|
|
276
|
+
//#endregion
|
|
277
|
+
export { detectIMessageLegacyStateMigrations };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns true if the value is a ISO8601 DateTime string
|
|
3
|
-
* @
|
|
3
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc3339
|
|
4
4
|
* @example `2020-12-12T20:20:40+00:00`
|
|
5
5
|
*/
|
|
6
|
-
export declare function IsDateTime(value: string
|
|
6
|
+
export declare function IsDateTime(value: string): boolean;
|
|
@@ -2,10 +2,10 @@ import { IsDate } from './date.mjs';
|
|
|
2
2
|
import { IsTime } from './time.mjs';
|
|
3
3
|
/**
|
|
4
4
|
* Returns true if the value is a ISO8601 DateTime string
|
|
5
|
-
* @
|
|
5
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc3339
|
|
6
6
|
* @example `2020-12-12T20:20:40+00:00`
|
|
7
7
|
*/
|
|
8
|
-
export function IsDateTime(value
|
|
8
|
+
export function IsDateTime(value) {
|
|
9
9
|
const dateTime = value.split(/T/i);
|
|
10
|
-
return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1]
|
|
10
|
+
return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1]);
|
|
11
11
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const Email = /^(
|
|
1
|
+
const Email = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]|\\.)*")@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*|\[(?:IPv6:[a-f0-9:]+|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})\])$/i;
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is an Email
|
|
4
|
-
* @specification
|
|
4
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc5322
|
|
5
5
|
*/
|
|
6
6
|
export function IsEmail(value) {
|
|
7
7
|
return Email.test(value);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as Idna from './
|
|
1
|
+
import * as Idna from './idna/index.mjs';
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is a valid hostname.
|
|
4
4
|
* @specification https://tools.ietf.org/html/rfc1123
|
|
@@ -6,13 +6,5 @@ import * as Idna from './_idna.mjs';
|
|
|
6
6
|
* @specification https://tools.ietf.org/html/rfc5892
|
|
7
7
|
*/
|
|
8
8
|
export function IsHostname(value) {
|
|
9
|
-
|
|
10
|
-
return false;
|
|
11
|
-
if (value.charCodeAt(value.length - 1) === 46)
|
|
12
|
-
return false;
|
|
13
|
-
for (const label of value.split('.')) {
|
|
14
|
-
if (!Idna.IsLabel(label))
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
return true;
|
|
9
|
+
return Idna.IsHostname(value);
|
|
18
10
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const IdnEmail = /^(
|
|
1
|
+
const IdnEmail = /^(?:[A-Za-z0-9!#$%&'*+\/=?^_`{|}~\u{0080}-\u{10FFFF}-]+(?:\.[A-Za-z0-9!#$%&'*+\/=?^_`{|}~\u{0080}-\u{10FFFF}-]+)*|"(?:[^"\\]|\\.)*")@[\p{L}\p{N}](?:[\p{L}\p{N}-]{0,62})(?<!-)(?:\.[\p{L}\p{N}](?:[\p{L}\p{N}-]{0,62})(?<!-))*$/iu;
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is an IdnEmail
|
|
4
|
-
* @specification
|
|
4
|
+
* @specification https://datatracker.ietf.org/doc/html/rfc4952
|
|
5
5
|
*/
|
|
6
6
|
export function IsIdnEmail(value) {
|
|
7
|
-
return IdnEmail.test(value);
|
|
7
|
+
return IdnEmail.test(value.normalize('NFC'));
|
|
8
8
|
}
|
|
@@ -3,5 +3,6 @@
|
|
|
3
3
|
* @specification https://tools.ietf.org/html/rfc3490
|
|
4
4
|
* @specification https://tools.ietf.org/html/rfc5891
|
|
5
5
|
* @specification https://tools.ietf.org/html/rfc5892
|
|
6
|
+
* @specification https://tools.ietf.org/html/rfc5893
|
|
6
7
|
*/
|
|
7
8
|
export declare function IsIdnHostname(value: string): boolean;
|
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Idna from './idna/index.mjs';
|
|
2
2
|
/**
|
|
3
3
|
* Returns true if the value is a valid internationalized (IDN) hostname.
|
|
4
4
|
* @specification https://tools.ietf.org/html/rfc3490
|
|
5
5
|
* @specification https://tools.ietf.org/html/rfc5891
|
|
6
6
|
* @specification https://tools.ietf.org/html/rfc5892
|
|
7
|
+
* @specification https://tools.ietf.org/html/rfc5893
|
|
7
8
|
*/
|
|
8
9
|
export function IsIdnHostname(value) {
|
|
9
|
-
|
|
10
|
-
return false;
|
|
11
|
-
const canonical = value.normalize('NFC').replace(/[\u002E\u3002\uFF0E\uFF61]/g, '.');
|
|
12
|
-
if (canonical.length > 253)
|
|
13
|
-
return false;
|
|
14
|
-
for (const label of canonical.split('.')) {
|
|
15
|
-
if (!IsIdnLabel(label))
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
return true;
|
|
10
|
+
return Idna.IsIdnHostname(value);
|
|
19
11
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// deno-fmt-ignore-file
|
|
2
|
+
import * as FormatPuny from './puny.mjs';
|
|
3
|
+
import * as Pattern from '../pattern/pattern.mjs';
|
|
4
|
+
// ------------------------------------------------------------------
|
|
5
|
+
// LOCAL REGEX: Allowed Bidi classes per direction (RFC 5893)
|
|
6
|
+
// ------------------------------------------------------------------
|
|
7
|
+
const RE_RTL_ALLOWED = /^(?:R|AL|AN|EN|ES|CS|ET|ON|BN|NSM)$/;
|
|
8
|
+
const RE_LTR_ALLOWED = /^(?:L|EN|ES|CS|ET|ON|BN|NSM)$/;
|
|
9
|
+
const RE_RTL_CLASSES = /^(?:R|AL|AN)$/;
|
|
10
|
+
// ------------------------------------------------------------------
|
|
11
|
+
// HasBidiChars
|
|
12
|
+
//
|
|
13
|
+
// Decodes a punycode label to its Unicode form (if it is one) and
|
|
14
|
+
// checks whether the result is an RTL label per RFC 5893 §1.4.
|
|
15
|
+
// ------------------------------------------------------------------
|
|
16
|
+
export function HasBidiChars(value) {
|
|
17
|
+
if (FormatPuny.IsAcePrefixed(value)) {
|
|
18
|
+
try {
|
|
19
|
+
return HasRightToLeftCharacters(FormatPuny.Decode(value.slice(4).toLowerCase()));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return HasRightToLeftCharacters(value);
|
|
26
|
+
}
|
|
27
|
+
// ------------------------------------------------------------------
|
|
28
|
+
// GetBidiClass
|
|
29
|
+
// ------------------------------------------------------------------
|
|
30
|
+
function GetBidiClass(codePoint) {
|
|
31
|
+
const char = String.fromCodePoint(codePoint);
|
|
32
|
+
return (Pattern.RE_EUROPEAN_NUMBER.test(char) ? 'EN' :
|
|
33
|
+
Pattern.RE_ARABIC_INDIC_DIGIT.test(char) ? 'AN' :
|
|
34
|
+
// Pattern.RE_EUROPEAN_SEPARATOR.test(char) ? 'ES' : // (no-spec-coverage)
|
|
35
|
+
// Pattern.RE_COMMON_SEPARATOR.test(char) ? 'CS' : // (no-spec-coverage)
|
|
36
|
+
Pattern.RE_MARK_NONSPACING.test(char) ? 'NSM' :
|
|
37
|
+
Pattern.RE_SCRIPT_HEBREW.test(char) ? 'R' :
|
|
38
|
+
Pattern.RE_SCRIPT_ARABIC_LETTER.test(char) ? 'AL' :
|
|
39
|
+
Pattern.RE_LETTER.test(char) ? 'L' :
|
|
40
|
+
'ON');
|
|
41
|
+
}
|
|
42
|
+
// ------------------------------------------------------------------
|
|
43
|
+
// HasRightToLeftCharacters
|
|
44
|
+
//
|
|
45
|
+
// Per RFC 5893 §1.4: "An RTL label is a label that contains at least
|
|
46
|
+
// one character of type R, AL, or AN." Used to determine whether a
|
|
47
|
+
// domain name is a "Bidi domain name" (RFC 5893 §1.4), in which case
|
|
48
|
+
// the Bidi Rule applies to every label in the domain.
|
|
49
|
+
// ------------------------------------------------------------------
|
|
50
|
+
export function HasRightToLeftCharacters(value) {
|
|
51
|
+
for (const ch of value)
|
|
52
|
+
if (RE_RTL_CLASSES.test(GetBidiClass(ch.codePointAt(0))))
|
|
53
|
+
return true;
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
// ------------------------------------------------------------------
|
|
57
|
+
// SatisfiesBidiRule
|
|
58
|
+
// ------------------------------------------------------------------
|
|
59
|
+
export function SatisfiesBidiRule(value) {
|
|
60
|
+
let isRtl = false;
|
|
61
|
+
let allowed = RE_LTR_ALLOWED;
|
|
62
|
+
let sawEN = false;
|
|
63
|
+
let sawAN = false;
|
|
64
|
+
let isFirst = true;
|
|
65
|
+
// let lastClass: string | null = null // (no-spec-coverage)
|
|
66
|
+
for (const ch of value) {
|
|
67
|
+
const bidiClass = GetBidiClass(ch.codePointAt(0));
|
|
68
|
+
if (isFirst) {
|
|
69
|
+
// 1. The first character must be a character with Bidi property L, R, or AL.
|
|
70
|
+
if (bidiClass !== 'L' && bidiClass !== 'R' && bidiClass !== 'AL')
|
|
71
|
+
return false;
|
|
72
|
+
isRtl = bidiClass === 'R' || bidiClass === 'AL';
|
|
73
|
+
allowed = isRtl ? RE_RTL_ALLOWED : RE_LTR_ALLOWED;
|
|
74
|
+
isFirst = false;
|
|
75
|
+
}
|
|
76
|
+
// 2 / 5. Only characters in the allowed set for this direction may appear.
|
|
77
|
+
if (!allowed.test(bidiClass))
|
|
78
|
+
return false;
|
|
79
|
+
if (bidiClass === 'EN')
|
|
80
|
+
sawEN = true;
|
|
81
|
+
else if (bidiClass === 'AN')
|
|
82
|
+
sawAN = true;
|
|
83
|
+
// if (bidiClass !== 'NSM') lastClass = bidiClass // (no-spec-coverage)
|
|
84
|
+
}
|
|
85
|
+
if (isRtl && sawEN && sawAN)
|
|
86
|
+
return false; // spec coverage path
|
|
87
|
+
// (no-spec-coverage)
|
|
88
|
+
// if (isRtl) {
|
|
89
|
+
// // 3. The end of the label must be R, AL, EN, or AN (ignoring trailing NSM).
|
|
90
|
+
// // if (lastClass !== 'R' && lastClass !== 'AL' && lastClass !== 'EN' && lastClass !== 'AN') return false
|
|
91
|
+
// // 4. If an EN is present, no AN may be present, and vice versa.
|
|
92
|
+
// if (sawEN && sawAN) return false
|
|
93
|
+
// } else {
|
|
94
|
+
// // 6. The end of the label must be L or EN (ignoring trailing NSM).
|
|
95
|
+
// // if (lastClass !== 'L' && lastClass !== 'EN') return false
|
|
96
|
+
// }
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import * as Pattern from '../pattern/pattern.mjs';
|
|
2
|
+
// ------------------------------------------------------------------
|
|
3
|
+
// PunyCode (RFC 3492)
|
|
4
|
+
// ------------------------------------------------------------------
|
|
5
|
+
const PUNYCODE_BASE = 36;
|
|
6
|
+
const PUNYCODE_TMIN = 1;
|
|
7
|
+
const PUNYCODE_TMAX = 26;
|
|
8
|
+
const PUNYCODE_SKEW = 38;
|
|
9
|
+
const PUNYCODE_DAMP = 700;
|
|
10
|
+
const PUNYCODE_INITIAL_BIAS = 72;
|
|
11
|
+
const PUNYCODE_INITIAL_N = 128;
|
|
12
|
+
// ------------------------------------------------------------------
|
|
13
|
+
// ThrowDontCare
|
|
14
|
+
// ------------------------------------------------------------------
|
|
15
|
+
function ThrowDontCare() {
|
|
16
|
+
throw null;
|
|
17
|
+
}
|
|
18
|
+
// ------------------------------------------------------------------
|
|
19
|
+
// IsAcePrefixed
|
|
20
|
+
// ------------------------------------------------------------------
|
|
21
|
+
export function IsAcePrefixed(value) {
|
|
22
|
+
return value.toLowerCase().startsWith('xn--');
|
|
23
|
+
}
|
|
24
|
+
// ------------------------------------------------------------------
|
|
25
|
+
// Adapt
|
|
26
|
+
// ------------------------------------------------------------------
|
|
27
|
+
function Adapt(delta, numPoints, firstTime) {
|
|
28
|
+
delta = firstTime ? Math.floor(delta / PUNYCODE_DAMP) : delta >> 1;
|
|
29
|
+
delta += Math.floor(delta / numPoints);
|
|
30
|
+
let k = 0;
|
|
31
|
+
while (delta > (((PUNYCODE_BASE - PUNYCODE_TMIN) * PUNYCODE_TMAX) >> 1)) {
|
|
32
|
+
delta = Math.floor(delta / (PUNYCODE_BASE - PUNYCODE_TMIN));
|
|
33
|
+
k += PUNYCODE_BASE;
|
|
34
|
+
}
|
|
35
|
+
return k + Math.floor(((PUNYCODE_BASE - PUNYCODE_TMIN + 1) * delta) / (delta + PUNYCODE_SKEW));
|
|
36
|
+
}
|
|
37
|
+
// ------------------------------------------------------------------
|
|
38
|
+
// Decode
|
|
39
|
+
// ------------------------------------------------------------------
|
|
40
|
+
export function Decode(value) {
|
|
41
|
+
const output = [];
|
|
42
|
+
let n = PUNYCODE_INITIAL_N;
|
|
43
|
+
let i = 0;
|
|
44
|
+
let bias = PUNYCODE_INITIAL_BIAS;
|
|
45
|
+
const delimIdx = value.lastIndexOf('-');
|
|
46
|
+
if (delimIdx > 0) {
|
|
47
|
+
for (let j = 0; j < delimIdx; j++) {
|
|
48
|
+
const cp = value.charCodeAt(j);
|
|
49
|
+
if (cp >= 128)
|
|
50
|
+
ThrowDontCare(); // throw new Error('Invalid punycode: non-basic before delimiter')
|
|
51
|
+
output.push(cp);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
let inIdx = delimIdx < 0 ? 0 : delimIdx + 1;
|
|
55
|
+
while (inIdx < value.length) {
|
|
56
|
+
const oldi = i;
|
|
57
|
+
let w = 1;
|
|
58
|
+
let k = PUNYCODE_BASE;
|
|
59
|
+
while (true) {
|
|
60
|
+
if (inIdx >= value.length)
|
|
61
|
+
ThrowDontCare(); // throw new Error('Invalid punycode: unexpected end of input')
|
|
62
|
+
const ch = value.charCodeAt(inIdx++);
|
|
63
|
+
let digit;
|
|
64
|
+
if (ch >= 0x61 && ch <= 0x7a)
|
|
65
|
+
digit = ch - 0x61; // a-z => 0-25
|
|
66
|
+
else if (ch >= 0x30 && ch <= 0x39)
|
|
67
|
+
digit = ch - 0x30 + 26; // 0-9 => 26-35
|
|
68
|
+
// else if (ch >= 0x41 && ch <= 0x5a) digit = ch - 0x41 // A-Z => 0-25 // (no-spec-coverage)
|
|
69
|
+
else
|
|
70
|
+
ThrowDontCare(); // throw new Error('Invalid punycode: bad digit character')
|
|
71
|
+
i += digit * w;
|
|
72
|
+
const t = k <= bias ? PUNYCODE_TMIN : k >= bias + PUNYCODE_TMAX ? PUNYCODE_TMAX : k - bias;
|
|
73
|
+
if (digit < t)
|
|
74
|
+
break;
|
|
75
|
+
w *= PUNYCODE_BASE - t;
|
|
76
|
+
k += PUNYCODE_BASE;
|
|
77
|
+
}
|
|
78
|
+
const outLen = output.length + 1;
|
|
79
|
+
bias = Adapt(i - oldi, outLen, oldi === 0);
|
|
80
|
+
n += Math.floor(i / outLen);
|
|
81
|
+
i %= outLen;
|
|
82
|
+
output.splice(i, 0, n);
|
|
83
|
+
i++;
|
|
84
|
+
}
|
|
85
|
+
return String.fromCodePoint(...output);
|
|
86
|
+
}
|
|
87
|
+
// ------------------------------------------------------------------
|
|
88
|
+
// DigitToChar
|
|
89
|
+
// ------------------------------------------------------------------
|
|
90
|
+
function DigitToChar(digit) {
|
|
91
|
+
return digit < 26 ? String.fromCharCode(digit + 0x61) : String.fromCharCode(digit - 26 + 0x30);
|
|
92
|
+
}
|
|
93
|
+
// ------------------------------------------------------------------
|
|
94
|
+
// Encode
|
|
95
|
+
//
|
|
96
|
+
// Encodes a Unicode string into its Punycode payload (the part that
|
|
97
|
+
// follows the 'xn--' ACE prefix). Used to compute the true A-label
|
|
98
|
+
// length of a U-label, since the 63-octet label length limit (RFC
|
|
99
|
+
// 5891 §4.2.4) applies to the encoded form, not the code point count.
|
|
100
|
+
// ------------------------------------------------------------------
|
|
101
|
+
const RE_REPLACE_NON_ASCII = new RegExp(Pattern.RE_NON_ASCII.source, 'gu');
|
|
102
|
+
export function Encode(input) {
|
|
103
|
+
// Extract basic ASCII characters and set initial output array state
|
|
104
|
+
const basic = input.replace(RE_REPLACE_NON_ASCII, '');
|
|
105
|
+
const basicLength = basic.length;
|
|
106
|
+
const result = basicLength > 0 ? [basic, '-'] : [];
|
|
107
|
+
// Extract unicode codepoints
|
|
108
|
+
const codePoints = Array.from(input, (char) => char.codePointAt(0));
|
|
109
|
+
let n = PUNYCODE_INITIAL_N;
|
|
110
|
+
let delta = 0;
|
|
111
|
+
let bias = PUNYCODE_INITIAL_BIAS;
|
|
112
|
+
let handledCPCount = basicLength;
|
|
113
|
+
while (handledCPCount < codePoints.length) {
|
|
114
|
+
let m = Infinity;
|
|
115
|
+
for (const cp of codePoints) {
|
|
116
|
+
if (cp >= n && cp < m)
|
|
117
|
+
m = cp;
|
|
118
|
+
}
|
|
119
|
+
delta += (m - n) * (handledCPCount + 1);
|
|
120
|
+
n = m;
|
|
121
|
+
for (const cp of codePoints) {
|
|
122
|
+
if (cp < n)
|
|
123
|
+
delta++;
|
|
124
|
+
if (cp === n) {
|
|
125
|
+
let q = delta;
|
|
126
|
+
for (let k = PUNYCODE_BASE;; k += PUNYCODE_BASE) {
|
|
127
|
+
const t = k <= bias ? PUNYCODE_TMIN : k >= bias + PUNYCODE_TMAX ? PUNYCODE_TMAX : k - bias;
|
|
128
|
+
if (q < t)
|
|
129
|
+
break;
|
|
130
|
+
const digit = t + ((q - t) % (PUNYCODE_BASE - t));
|
|
131
|
+
result.push(DigitToChar(digit));
|
|
132
|
+
q = Math.floor((q - t) / (PUNYCODE_BASE - t));
|
|
133
|
+
}
|
|
134
|
+
result.push(DigitToChar(q));
|
|
135
|
+
bias = Adapt(delta, handledCPCount + 1, handledCPCount === basicLength);
|
|
136
|
+
delta = 0;
|
|
137
|
+
handledCPCount++;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
delta++;
|
|
141
|
+
n++;
|
|
142
|
+
}
|
|
143
|
+
return result.join('');
|
|
144
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function IsHostname(value: string): boolean;
|