@apifuse/provider-sdk 2.2.0-beta.60 → 2.2.0-beta.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/AUTHORING.md +17 -16
  2. package/CHANGELOG.md +8 -0
  3. package/bin/apifuse-dev.ts +5 -7
  4. package/bin/apifuse-pack-types.ts +26 -0
  5. package/bin/apifuse-perf.ts +4 -6
  6. package/bin/apifuse-record.ts +4 -7
  7. package/bin/apifuse-submit-check.ts +2 -2
  8. package/dist/define.d.ts +3 -2
  9. package/dist/define.js +1 -1
  10. package/dist/dev.js +3 -1
  11. package/dist/engine.d.ts +7 -6
  12. package/dist/engine.js +3 -4
  13. package/dist/error-resolution.js +21 -4
  14. package/dist/errors.d.ts +3 -0
  15. package/dist/errors.js +9 -0
  16. package/dist/handle-meta.d.ts +55 -0
  17. package/dist/handle-meta.js +57 -0
  18. package/dist/handle.d.ts +208 -0
  19. package/dist/handle.js +299 -0
  20. package/dist/i18n/catalog.js +21 -1
  21. package/dist/index.d.ts +7 -4
  22. package/dist/index.js +6 -3
  23. package/dist/lint.d.ts +3 -0
  24. package/dist/lint.js +372 -47
  25. package/dist/provider.d.ts +5 -3
  26. package/dist/provider.js +4 -2
  27. package/dist/runtime/handle-wordlist.d.ts +43 -0
  28. package/dist/runtime/{choice-wordlist.js → handle-wordlist.js} +127 -9
  29. package/dist/runtime/handle.d.ts +71 -0
  30. package/dist/runtime/handle.js +900 -0
  31. package/dist/runtime/http-telemetry-binding.d.ts +10 -0
  32. package/dist/runtime/http-telemetry-binding.js +152 -0
  33. package/dist/runtime/http-telemetry-guard.d.ts +6 -0
  34. package/dist/runtime/http-telemetry-guard.js +150 -0
  35. package/dist/runtime/http-telemetry.d.ts +104 -0
  36. package/dist/runtime/http-telemetry.js +190 -0
  37. package/dist/runtime/http.d.ts +31 -0
  38. package/dist/runtime/http.js +222 -146
  39. package/dist/runtime/native-network.d.ts +10 -1
  40. package/dist/runtime/native-network.js +230 -29
  41. package/dist/runtime/native-telemetry.d.ts +142 -0
  42. package/dist/runtime/native-telemetry.js +231 -0
  43. package/dist/runtime/state.d.ts +4 -1
  44. package/dist/runtime/state.js +32 -17
  45. package/dist/server/index.d.ts +3 -0
  46. package/dist/server/index.js +2 -0
  47. package/dist/server/sensitive-values.d.ts +1 -1
  48. package/dist/server/sensitive-values.js +0 -3
  49. package/dist/server/serve-implementation.d.ts +6 -11
  50. package/dist/server/serve-implementation.js +119 -52
  51. package/dist/testing/run.js +10 -7
  52. package/dist/types.d.ts +4 -107
  53. package/package.json +1 -1
  54. package/src/define.ts +4 -3
  55. package/src/dev.ts +3 -1
  56. package/src/engine.ts +5 -6
  57. package/src/error-resolution.ts +21 -4
  58. package/src/errors.ts +13 -0
  59. package/src/handle-meta.ts +93 -0
  60. package/src/handle.ts +612 -0
  61. package/src/i18n/catalog.ts +23 -1
  62. package/src/index.ts +45 -13
  63. package/src/lint.ts +437 -55
  64. package/src/provider.ts +19 -20
  65. package/src/runtime/{choice-wordlist.ts → handle-wordlist.ts} +130 -9
  66. package/src/runtime/handle.ts +1172 -0
  67. package/src/runtime/http-telemetry-binding.ts +162 -0
  68. package/src/runtime/http-telemetry-guard.ts +155 -0
  69. package/src/runtime/http-telemetry.ts +292 -0
  70. package/src/runtime/http.ts +295 -173
  71. package/src/runtime/native-network.ts +281 -33
  72. package/src/runtime/native-telemetry.ts +366 -0
  73. package/src/runtime/state.ts +44 -16
  74. package/src/server/index.ts +30 -0
  75. package/src/server/sensitive-values.ts +0 -3
  76. package/src/server/serve-implementation.ts +127 -89
  77. package/src/testing/run.ts +11 -7
  78. package/src/types.ts +4 -137
  79. package/dist/choice-token.d.ts +0 -24
  80. package/dist/choice-token.js +0 -74
  81. package/dist/runtime/choice-wordlist.d.ts +0 -9
  82. package/dist/runtime/choice.d.ts +0 -27
  83. package/dist/runtime/choice.js +0 -801
  84. package/src/choice-token.ts +0 -165
  85. 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 CHOICE_WORDLIST_SIZE = 1_296;
123
- export const CHOICE_WORD_ENTROPY_BITS = Math.log2(CHOICE_WORDLIST_SIZE);
124
- export const STANDARD_CHOICE_WORD_COUNT = 4;
125
- export const HIGH_CHOICE_WORD_COUNT = 5;
126
- export const STANDARD_CHOICE_ENTROPY_BITS = STANDARD_CHOICE_WORD_COUNT * CHOICE_WORD_ENTROPY_BITS;
127
- export const HIGH_CHOICE_ENTROPY_BITS = HIGH_CHOICE_WORD_COUNT * CHOICE_WORD_ENTROPY_BITS;
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
- export function choiceWordAt(index: number): string {
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(`Choice word index is out of range: ${index}`);
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 isChoiceWord(value: string): boolean {
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
+ }