@apifuse/provider-sdk 2.2.0-beta.60 → 2.2.0-beta.61
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/AUTHORING.md +17 -16
- package/CHANGELOG.md +4 -0
- package/bin/apifuse-dev.ts +5 -7
- package/bin/apifuse-pack-types.ts +22 -0
- package/bin/apifuse-perf.ts +4 -6
- package/bin/apifuse-record.ts +4 -7
- package/bin/apifuse-submit-check.ts +2 -2
- package/dist/define.d.ts +3 -2
- package/dist/define.js +1 -1
- package/dist/dev.js +3 -1
- package/dist/engine.d.ts +7 -6
- package/dist/engine.js +3 -4
- package/dist/error-resolution.js +21 -4
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +9 -0
- package/dist/handle-meta.d.ts +39 -0
- package/dist/handle-meta.js +29 -0
- package/dist/handle.d.ts +187 -0
- package/dist/handle.js +254 -0
- package/dist/i18n/catalog.js +21 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/lint.d.ts +3 -0
- package/dist/lint.js +352 -47
- package/dist/provider.d.ts +3 -3
- package/dist/provider.js +2 -2
- package/dist/runtime/handle-wordlist.d.ts +43 -0
- package/dist/runtime/{choice-wordlist.js → handle-wordlist.js} +127 -9
- package/dist/runtime/handle.d.ts +71 -0
- package/dist/runtime/handle.js +895 -0
- package/dist/runtime/native-network.d.ts +10 -1
- package/dist/runtime/native-network.js +230 -29
- package/dist/runtime/native-telemetry.d.ts +142 -0
- package/dist/runtime/native-telemetry.js +231 -0
- package/dist/runtime/state.d.ts +4 -1
- package/dist/runtime/state.js +32 -17
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/sensitive-values.d.ts +1 -1
- package/dist/server/sensitive-values.js +0 -3
- package/dist/server/serve-implementation.d.ts +4 -11
- package/dist/server/serve-implementation.js +86 -18
- package/dist/testing/run.js +10 -7
- package/dist/types.d.ts +4 -107
- package/package.json +1 -1
- package/src/define.ts +4 -3
- package/src/dev.ts +3 -1
- package/src/engine.ts +5 -6
- package/src/error-resolution.ts +21 -4
- package/src/errors.ts +13 -0
- package/src/handle-meta.ts +58 -0
- package/src/handle.ts +529 -0
- package/src/i18n/catalog.ts +23 -1
- package/src/index.ts +26 -13
- package/src/lint.ts +414 -55
- package/src/provider.ts +9 -20
- package/src/runtime/{choice-wordlist.ts → handle-wordlist.ts} +130 -9
- package/src/runtime/handle.ts +1162 -0
- package/src/runtime/native-network.ts +281 -33
- package/src/runtime/native-telemetry.ts +366 -0
- package/src/runtime/state.ts +44 -16
- package/src/server/index.ts +17 -0
- package/src/server/sensitive-values.ts +0 -3
- package/src/server/serve-implementation.ts +87 -32
- package/src/testing/run.ts +11 -7
- package/src/types.ts +4 -137
- package/dist/choice-token.d.ts +0 -24
- package/dist/choice-token.js +0 -74
- package/dist/runtime/choice-wordlist.d.ts +0 -9
- package/dist/runtime/choice.d.ts +0 -27
- package/dist/runtime/choice.js +0 -801
- package/src/choice-token.ts +0 -165
- package/src/runtime/choice.ts +0 -1197
|
@@ -119,12 +119,22 @@ yacht yahoo yard yearbook yesterday yiddish yield yo-yo yodel yogurt yuppie zeal
|
|
|
119
119
|
zestfully zigzagged zillion zipping zirconium zodiac zombie zookeeper zucchini
|
|
120
120
|
`;
|
|
121
121
|
|
|
122
|
-
export const
|
|
123
|
-
export const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
122
|
+
export const HANDLE_WORDLIST_SIZE = 1_296;
|
|
123
|
+
export const HANDLE_WORD_ENTROPY_BITS = Math.log2(HANDLE_WORDLIST_SIZE);
|
|
124
|
+
/**
|
|
125
|
+
* Bound handles live under `state.forConnection(connectionId)`: another
|
|
126
|
+
* connection cannot address them at all, so no guessing analysis applies. The
|
|
127
|
+
* only requirement is uniqueness among live handles of one connection
|
|
128
|
+
* (1,296^2 = 1.68M keys, CAS-if-absent with retries), which two words satisfy.
|
|
129
|
+
*/
|
|
130
|
+
export const BOUND_HANDLE_WORD_COUNT = 2;
|
|
131
|
+
/** Public handles are addressable by anyone; ADR 0006 §2 guessing numbers apply. */
|
|
132
|
+
export const PUBLIC_HANDLE_WORD_COUNT = 4;
|
|
133
|
+
/** Used when a public kind declares `strength: "high"` or its ttl exceeds one hour. */
|
|
134
|
+
export const HIGH_PUBLIC_HANDLE_WORD_COUNT = 5;
|
|
135
|
+
export const PUBLIC_HANDLE_ENTROPY_BITS = PUBLIC_HANDLE_WORD_COUNT * HANDLE_WORD_ENTROPY_BITS;
|
|
136
|
+
export const HIGH_PUBLIC_HANDLE_ENTROPY_BITS =
|
|
137
|
+
HIGH_PUBLIC_HANDLE_WORD_COUNT * HANDLE_WORD_ENTROPY_BITS;
|
|
128
138
|
|
|
129
139
|
export const EFF_SHORT_WORDLIST_2: readonly string[] = Object.freeze(
|
|
130
140
|
EFF_SHORT_WORDLIST_2_TEXT.trim().split(/\s+/),
|
|
@@ -132,14 +142,125 @@ export const EFF_SHORT_WORDLIST_2: readonly string[] = Object.freeze(
|
|
|
132
142
|
|
|
133
143
|
const EFF_SHORT_WORD_SET: ReadonlySet<string> = new Set(EFF_SHORT_WORDLIST_2);
|
|
134
144
|
|
|
135
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Words the runtime may issue. The handle grammar joins words with `-`, so the
|
|
147
|
+
* one hyphenated official entry (`yo-yo`) would fuse with the separator and is
|
|
148
|
+
* never issued. The official list itself stays embedded unchanged.
|
|
149
|
+
*/
|
|
150
|
+
export const ISSUABLE_HANDLE_WORDS: readonly string[] = Object.freeze(
|
|
151
|
+
EFF_SHORT_WORDLIST_2.filter((word) => !word.includes("-")),
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
export function handleWordAt(index: number): string {
|
|
136
155
|
const word = EFF_SHORT_WORDLIST_2[index];
|
|
137
156
|
if (word === undefined) {
|
|
138
|
-
throw new RangeError(`
|
|
157
|
+
throw new RangeError(`Handle word index is out of range: ${index}`);
|
|
139
158
|
}
|
|
140
159
|
return word;
|
|
141
160
|
}
|
|
142
161
|
|
|
143
|
-
export function
|
|
162
|
+
export function isHandleWord(value: string): boolean {
|
|
144
163
|
return EFF_SHORT_WORD_SET.has(value);
|
|
145
164
|
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Symmetric-deletion index (SymSpell, max distance 1). Two strings at
|
|
168
|
+
* Levenshtein distance <= 1 always share a form reachable by deleting at most
|
|
169
|
+
* one character from each side, so a lookup of the query and its one-deletion
|
|
170
|
+
* variants yields every candidate; a real distance check then removes the false
|
|
171
|
+
* positives (e.g. transpositions). ~10k entries, O(len) per lookup.
|
|
172
|
+
*/
|
|
173
|
+
/** Shortest and longest words in the list (3 and 10 for the official list). */
|
|
174
|
+
export const MIN_HANDLE_WORD_LENGTH: number = Math.min(
|
|
175
|
+
...EFF_SHORT_WORDLIST_2.map((word) => word.length),
|
|
176
|
+
);
|
|
177
|
+
export const MAX_HANDLE_WORD_LENGTH: number = Math.max(
|
|
178
|
+
...EFF_SHORT_WORDLIST_2.map((word) => word.length),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const DELETION_INDEX: ReadonlyMap<string, readonly string[]> = (() => {
|
|
182
|
+
const index = new Map<string, string[]>();
|
|
183
|
+
const add = (form: string, word: string): void => {
|
|
184
|
+
const bucket = index.get(form);
|
|
185
|
+
if (bucket === undefined) index.set(form, [word]);
|
|
186
|
+
else if (!bucket.includes(word)) bucket.push(word);
|
|
187
|
+
};
|
|
188
|
+
for (const word of EFF_SHORT_WORDLIST_2) {
|
|
189
|
+
add(word, word);
|
|
190
|
+
for (const variant of oneDeletionVariants(word)) add(variant, word);
|
|
191
|
+
}
|
|
192
|
+
return index;
|
|
193
|
+
})();
|
|
194
|
+
|
|
195
|
+
function oneDeletionVariants(value: string): string[] {
|
|
196
|
+
const variants: string[] = [];
|
|
197
|
+
for (let position = 0; position < value.length; position += 1) {
|
|
198
|
+
variants.push(value.slice(0, position) + value.slice(position + 1));
|
|
199
|
+
}
|
|
200
|
+
return variants;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** True when Levenshtein distance between `left` and `right` is at most 1. */
|
|
204
|
+
function withinEditDistanceOne(left: string, right: string): boolean {
|
|
205
|
+
if (left === right) return true;
|
|
206
|
+
const lengthDelta = left.length - right.length;
|
|
207
|
+
if (lengthDelta > 1 || lengthDelta < -1) return false;
|
|
208
|
+
if (lengthDelta === 0) {
|
|
209
|
+
let mismatches = 0;
|
|
210
|
+
for (let position = 0; position < left.length; position += 1) {
|
|
211
|
+
if (left[position] !== right[position]) {
|
|
212
|
+
mismatches += 1;
|
|
213
|
+
if (mismatches > 1) return false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
const longer = lengthDelta > 0 ? left : right;
|
|
219
|
+
const shorter = lengthDelta > 0 ? right : left;
|
|
220
|
+
let longIndex = 0;
|
|
221
|
+
let shortIndex = 0;
|
|
222
|
+
let skipped = false;
|
|
223
|
+
while (shortIndex < shorter.length) {
|
|
224
|
+
if (longer[longIndex] === shorter[shortIndex]) {
|
|
225
|
+
longIndex += 1;
|
|
226
|
+
shortIndex += 1;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (skipped) return false;
|
|
230
|
+
skipped = true;
|
|
231
|
+
longIndex += 1;
|
|
232
|
+
}
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Resolves one handle segment to a wordlist entry: the exact word, or the
|
|
238
|
+
* unique word at edit distance <= `maxDistance`. Returns null when no word or
|
|
239
|
+
* more than one word qualifies. The official list has pairwise edit distance
|
|
240
|
+
* >= 3, so at distance 1 every non-null answer is unique; the uniqueness check
|
|
241
|
+
* is kept as a guard so a wordlist change cannot silently make normalization
|
|
242
|
+
* map one input onto two different canonical handles.
|
|
243
|
+
*/
|
|
244
|
+
export function findHandleWordWithinDistance(segment: string, maxDistance: 1): string | null {
|
|
245
|
+
if (segment.length === 0) return null;
|
|
246
|
+
if (EFF_SHORT_WORD_SET.has(segment)) return segment;
|
|
247
|
+
if (maxDistance !== 1) return null;
|
|
248
|
+
// A segment that is more than one character shorter or longer than every
|
|
249
|
+
// word cannot be within distance 1 of any word. Rejecting it here also bounds
|
|
250
|
+
// the variant generation below (which is O(len²) in memory) against
|
|
251
|
+
// client-supplied garbage of arbitrary length.
|
|
252
|
+
if (
|
|
253
|
+
segment.length < MIN_HANDLE_WORD_LENGTH - maxDistance ||
|
|
254
|
+
segment.length > MAX_HANDLE_WORD_LENGTH + maxDistance
|
|
255
|
+
) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
const matches = new Set<string>();
|
|
259
|
+
for (const form of [segment, ...oneDeletionVariants(segment)]) {
|
|
260
|
+
for (const candidate of DELETION_INDEX.get(form) ?? []) {
|
|
261
|
+
if (withinEditDistanceOne(segment, candidate)) matches.add(candidate);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (matches.size !== 1) return null;
|
|
265
|
+
return matches.values().next().value ?? null;
|
|
266
|
+
}
|