@jini-ai/platform 0.1.0

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 (66) hide show
  1. package/LICENSE +201 -0
  2. package/dist/asset-cache.d.ts +80 -0
  3. package/dist/asset-cache.d.ts.map +1 -0
  4. package/dist/asset-cache.js +423 -0
  5. package/dist/asset-cache.js.map +1 -0
  6. package/dist/aws-sigv4.d.ts +51 -0
  7. package/dist/aws-sigv4.d.ts.map +1 -0
  8. package/dist/aws-sigv4.js +104 -0
  9. package/dist/aws-sigv4.js.map +1 -0
  10. package/dist/blob-storage.d.ts +89 -0
  11. package/dist/blob-storage.d.ts.map +1 -0
  12. package/dist/blob-storage.js +315 -0
  13. package/dist/blob-storage.js.map +1 -0
  14. package/dist/command.d.ts +32 -0
  15. package/dist/command.d.ts.map +1 -0
  16. package/dist/command.js +101 -0
  17. package/dist/command.js.map +1 -0
  18. package/dist/download.d.ts +92 -0
  19. package/dist/download.d.ts.map +1 -0
  20. package/dist/download.js +856 -0
  21. package/dist/download.js.map +1 -0
  22. package/dist/fs.d.ts +55 -0
  23. package/dist/fs.d.ts.map +1 -0
  24. package/dist/fs.js +124 -0
  25. package/dist/fs.js.map +1 -0
  26. package/dist/home-expansion.d.ts +18 -0
  27. package/dist/home-expansion.d.ts.map +1 -0
  28. package/dist/home-expansion.js +58 -0
  29. package/dist/home-expansion.js.map +1 -0
  30. package/dist/http.d.ts +14 -0
  31. package/dist/http.d.ts.map +1 -0
  32. package/dist/http.js +39 -0
  33. package/dist/http.js.map +1 -0
  34. package/dist/index.d.ts +59 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +45 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/process.d.ts +159 -0
  39. package/dist/process.d.ts.map +1 -0
  40. package/dist/process.js +362 -0
  41. package/dist/process.js.map +1 -0
  42. package/dist/proxy-env.d.ts +50 -0
  43. package/dist/proxy-env.d.ts.map +1 -0
  44. package/dist/proxy-env.js +373 -0
  45. package/dist/proxy-env.js.map +1 -0
  46. package/dist/resource-paths.d.ts +68 -0
  47. package/dist/resource-paths.d.ts.map +1 -0
  48. package/dist/resource-paths.js +137 -0
  49. package/dist/resource-paths.js.map +1 -0
  50. package/dist/sandbox-env.d.ts +86 -0
  51. package/dist/sandbox-env.d.ts.map +1 -0
  52. package/dist/sandbox-env.js +215 -0
  53. package/dist/sandbox-env.js.map +1 -0
  54. package/dist/shell.d.ts +46 -0
  55. package/dist/shell.d.ts.map +1 -0
  56. package/dist/shell.js +78 -0
  57. package/dist/shell.js.map +1 -0
  58. package/dist/terminal.d.ts +137 -0
  59. package/dist/terminal.d.ts.map +1 -0
  60. package/dist/terminal.js +398 -0
  61. package/dist/terminal.js.map +1 -0
  62. package/dist/toolchain.d.ts +17 -0
  63. package/dist/toolchain.d.ts.map +1 -0
  64. package/dist/toolchain.js +251 -0
  65. package/dist/toolchain.js.map +1 -0
  66. package/package.json +47 -0
@@ -0,0 +1,423 @@
1
+ // Local disk cache + same-origin proxy for external images/videos referenced
2
+ // by sandboxed HTML (an iframe preview, an embedded document, etc.) that a
3
+ // host application renders on cross-origin CDNs.
4
+ //
5
+ // Two problems this solves for that sandboxed content:
6
+ // 1. A CSP like `img-src 'self' data: blob:; media-src 'self' ...` blocks
7
+ // absolute third-party URLs outright inside the sandbox.
8
+ // 2. Even when reachable, those hosts have no nearby edge for many users,
9
+ // so each render re-pays multi-second cross-border latency per asset.
10
+ //
11
+ // A caller rewrites matching URLs (via `assetCacheRewriteUrl`) to a
12
+ // same-origin route it serves from this cache: fetched once, validated,
13
+ // stored content-addressably on disk, and replayed instantly afterwards.
14
+ //
15
+ // SSRF is the load-bearing risk here: the route fetches a caller-supplied URL.
16
+ // `assertSafePublicUrl` rejects non-http(s) schemes, embedded credentials,
17
+ // localhost, and literal private IPs up-front. The authoritative guard against
18
+ // DNS rebinding / TOCTOU is `createValidatingLookup`: it is installed as the
19
+ // undici Agent's connection-time `lookup`, so the address that is *validated*
20
+ // is the exact address the socket *connects to* — there is no separate
21
+ // validation lookup a rebinding resolver could diverge from.
22
+ import { createHash } from 'node:crypto';
23
+ import { lookup as dnsLookupCb } from 'node:dns';
24
+ import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
25
+ import { isIP } from 'node:net';
26
+ import path from 'node:path';
27
+ import { Agent } from 'undici';
28
+ /** Media extensions we are willing to cache + proxy. Anything else (HTML,
29
+ * CSS, JS, fonts) is intentionally left alone so this surface never becomes a
30
+ * general-purpose open proxy. */
31
+ export const CACHEABLE_MEDIA_EXT = /\.(?:png|jpe?g|webp|gif|avif|svg|mp4|webm|mov|m4v|ogg|mp3|wav)(?:$|[?#])/i;
32
+ const EXT_TO_MIME = {
33
+ '.png': 'image/png',
34
+ '.jpg': 'image/jpeg',
35
+ '.jpeg': 'image/jpeg',
36
+ '.webp': 'image/webp',
37
+ '.gif': 'image/gif',
38
+ '.avif': 'image/avif',
39
+ '.svg': 'image/svg+xml',
40
+ '.mp4': 'video/mp4',
41
+ '.webm': 'video/webm',
42
+ '.mov': 'video/quicktime',
43
+ '.m4v': 'video/x-m4v',
44
+ '.ogg': 'audio/ogg',
45
+ '.mp3': 'audio/mpeg',
46
+ '.wav': 'audio/wav',
47
+ };
48
+ /** True when `raw` is an absolute http(s) URL pointing at a media file we are
49
+ * willing to route through the cache. Pure + side-effect free so the URL
50
+ * rewriter and tests can share the exact predicate. */
51
+ export function isCacheableExternalUrl(raw) {
52
+ if (typeof raw !== 'string')
53
+ return false;
54
+ const value = raw.trim();
55
+ if (!/^https?:\/\//i.test(value))
56
+ return false;
57
+ // `String#split` always returns a non-empty array (even `''.split(x)` is
58
+ // `['']`), so index 0 is never `undefined` — the `!` narrows a
59
+ // `noUncheckedIndexedAccess` false positive, not a real fallback path.
60
+ const pathOnly = value.split(/[?#]/)[0];
61
+ return CACHEABLE_MEDIA_EXT.test(pathOnly) || CACHEABLE_MEDIA_EXT.test(value);
62
+ }
63
+ /** Map a cacheable external URL to its same-origin proxy URL. `routePath`
64
+ * defaults to `/api/asset-cache` but is caller-configurable since the route
65
+ * is mounted by the host application, not this package. */
66
+ export function assetCacheRewriteUrl(raw, routePath = '/api/asset-cache') {
67
+ return `${routePath}?url=${encodeURIComponent(raw.trim())}`;
68
+ }
69
+ /** Error carrying the HTTP status the route should surface. */
70
+ export class AssetCacheError extends Error {
71
+ status;
72
+ constructor(status, message) {
73
+ super(message);
74
+ this.name = 'AssetCacheError';
75
+ this.status = status;
76
+ }
77
+ }
78
+ /** Expand an IPv6 literal (with `::` compression and/or an embedded dotted
79
+ * IPv4 tail) into its eight 16-bit groups, or null if it does not parse.
80
+ * Node's URL parser normalizes mapped literals to hex (`::ffff:127.0.0.1` →
81
+ * `::ffff:7f00:1`), so a string regex can't reliably spot the mapped range —
82
+ * we have to canonicalize.
83
+ *
84
+ * Exported (previously module-private) so its parse-failure guards are
85
+ * directly unit-testable: `isPrivateAddress`'s only real call site gates
86
+ * every call behind `net.isIP(addr) === 6`, and Node's `isIP` already fully
87
+ * validates IPv6 syntax (including embedded-IPv4-tail octet ranges) before
88
+ * returning 6 — empirically re-verified this session by fuzzing ~10k
89
+ * `isIP`-accepted v6 literals through this function's guards with zero
90
+ * hits — so none of these guards are reachable through that path. They stay
91
+ * as real defense-in-depth (fail closed with `null` rather than trust a
92
+ * platform primitive never changing behavior), matching this file's
93
+ * "exported for direct unit testing" precedent (see `readBodyCapped`'s
94
+ * abort parameter). */
95
+ export function expandIpv6(addr) {
96
+ // `String#split` always returns a non-empty array, so index 0 is never
97
+ // `undefined` — the `!` narrows a `noUncheckedIndexedAccess` false
98
+ // positive, not a real fallback path.
99
+ let s = addr.toLowerCase().split('%')[0];
100
+ if (!s.includes(':'))
101
+ return null;
102
+ // Fold a trailing dotted IPv4 (`…:1.2.3.4`) into two hex groups.
103
+ const v4 = /^(.*:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(s);
104
+ if (v4) {
105
+ // `v4[2]` is the regex's second (mandatory, non-optional) capture group
106
+ // of a successful match — always defined.
107
+ const o = v4[2].split('.').map((n) => Number(n));
108
+ if (o.length !== 4 || o.some((n) => !Number.isInteger(n) || n < 0 || n > 255))
109
+ return null;
110
+ // `o` is now a validated 4-element array — indices 0-3 always defined.
111
+ const hi = (((o[0] << 8) | o[1])).toString(16);
112
+ const lo = (((o[2] << 8) | o[3])).toString(16);
113
+ s = `${v4[1]}${hi}:${lo}`;
114
+ }
115
+ const halves = s.split('::');
116
+ if (halves.length > 2)
117
+ return null;
118
+ const parse = (p) => p === '' ? [] : p.split(':').map((g) => (/^[0-9a-f]{1,4}$/.test(g) ? parseInt(g, 16) : Number.NaN));
119
+ // `halves` always has at least one element (split guarantee).
120
+ const left = parse(halves[0]);
121
+ let groups;
122
+ if (halves.length === 1) {
123
+ groups = left;
124
+ }
125
+ else {
126
+ // `halves.length === 2` here (the `> 2` case already returned above).
127
+ const right = parse(halves[1]);
128
+ const fill = 8 - left.length - right.length;
129
+ if (fill < 1)
130
+ return null;
131
+ groups = [...left, ...new Array(fill).fill(0), ...right];
132
+ }
133
+ if (groups.length !== 8 || groups.some((g) => Number.isNaN(g) || g < 0 || g > 0xffff))
134
+ return null;
135
+ return groups;
136
+ }
137
+ /** True for any address an external CDN must never resolve to: loopback,
138
+ * link-local, private, CGNAT, ULA, multicast, or unspecified. An unparseable
139
+ * input is treated as unsafe. */
140
+ export function isPrivateAddress(addr) {
141
+ const fam = isIP(addr);
142
+ if (fam === 4) {
143
+ // `net.isIP(addr) === 4` already guarantees `addr` is a syntactically
144
+ // valid dotted-quad with four 0-255 decimal octets (no leading-zero
145
+ // octal ambiguity, no out-of-range component) — empirically re-verified
146
+ // this session by fuzzing 500k+ candidate strings through `isIP` and
147
+ // this same re-derivation with zero divergences. `o` is therefore always
148
+ // a validated 4-element array.
149
+ const o = addr.split('.').map((n) => Number(n));
150
+ const a = o[0];
151
+ const b = o[1];
152
+ if (a === 0)
153
+ return true; // "this" network / unspecified
154
+ if (a === 10)
155
+ return true; // private
156
+ if (a === 127)
157
+ return true; // loopback
158
+ if (a === 169 && b === 254)
159
+ return true; // link-local
160
+ if (a === 172 && b >= 16 && b <= 31)
161
+ return true; // private
162
+ if (a === 192 && b === 168)
163
+ return true; // private
164
+ if (a === 100 && b >= 64 && b <= 127)
165
+ return true; // CGNAT
166
+ if (a >= 224)
167
+ return true; // multicast + reserved
168
+ return false;
169
+ }
170
+ if (fam === 6) {
171
+ const groups = expandIpv6(addr);
172
+ // `net.isIP(addr) === 6` already guarantees `addr` is syntactically valid
173
+ // IPv6 (Node validates this before classifying the family), and
174
+ // `expandIpv6` implements the same canonicalization Node's own parser
175
+ // does, so it never returns `null` for an `isIP`-accepted literal —
176
+ // empirically re-verified this session by fuzzing ~10k `isIP`-accepted
177
+ // v6 literals through `expandIpv6` with zero `null` results (see
178
+ // `expandIpv6`'s doc comment and `source-map.md`'s 2026-07-22 entry).
179
+ // Kept as a real fail-closed guard (defense-in-depth against a future
180
+ // change to either parser) rather than asserted away, so it stays
181
+ // provably-unreachable-but-intentional instead of a forced test.
182
+ if (!groups)
183
+ return true; // can't canonicalize a "valid" v6 → refuse
184
+ // IPv4-mapped (`::ffff:0:0/96`) and the deprecated IPv4-compatible
185
+ // (`::0:0/96`, excluding :: / ::1) carry an embedded IPv4 — classify it by
186
+ // its real v4 range so `::ffff:7f00:1` (== 127.0.0.1) is caught.
187
+ const topZero = groups.slice(0, 5).every((g) => g === 0);
188
+ if (topZero && (groups[5] === 0xffff || groups[5] === 0)) {
189
+ // `groups` is a validated 8-element array once non-null — indices 6-7
190
+ // always defined.
191
+ const a = groups[6] >> 8;
192
+ const b = groups[6] & 0xff;
193
+ const c = groups[7] >> 8;
194
+ const d = groups[7] & 0xff;
195
+ if (groups[5] === 0xffff || a !== 0 || b !== 0) {
196
+ return isPrivateAddress(`${a}.${b}.${c}.${d}`);
197
+ }
198
+ }
199
+ if (groups.every((g) => g === 0))
200
+ return true; // :: (unspecified)
201
+ if (groups.slice(0, 7).every((g) => g === 0) && groups[7] === 1)
202
+ return true; // ::1 loopback
203
+ const first = groups[0];
204
+ if ((first & 0xffc0) === 0xfe80)
205
+ return true; // link-local fe80::/10 (fe80–febf)
206
+ if ((first & 0xfe00) === 0xfc00)
207
+ return true; // unique-local fc00::/7
208
+ if ((first & 0xff00) === 0xff00)
209
+ return true; // multicast ff00::/8
210
+ return false;
211
+ }
212
+ return true; // not a literal IP — caller must resolve before trusting it
213
+ }
214
+ /** Cheap up-front rejection: enforce http(s), no embedded credentials, no
215
+ * localhost, and no *literal* private IP. This is NOT the DNS-rebinding guard
216
+ * (that is `createValidatingLookup`, applied at connection time) — it only
217
+ * fast-fails the obvious cases before any socket is opened. */
218
+ export function assertSafePublicUrl(raw) {
219
+ let url;
220
+ try {
221
+ url = new URL(raw);
222
+ }
223
+ catch {
224
+ throw new AssetCacheError(400, 'invalid url');
225
+ }
226
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
227
+ throw new AssetCacheError(400, 'unsupported url scheme');
228
+ }
229
+ if (url.username || url.password) {
230
+ throw new AssetCacheError(400, 'url credentials are not allowed');
231
+ }
232
+ const host = url.hostname.replace(/^\[|\]$/g, '');
233
+ const lowerHost = host.toLowerCase();
234
+ if (lowerHost === 'localhost' || lowerHost.endsWith('.localhost')) {
235
+ throw new AssetCacheError(400, 'localhost is not allowed');
236
+ }
237
+ if (isIP(host) && isPrivateAddress(host)) {
238
+ throw new AssetCacheError(400, 'url points at a private address');
239
+ }
240
+ return url;
241
+ }
242
+ /** Wrap a `dns.lookup`-shaped resolver so the resolved address is rejected when
243
+ * it is private. Installed as the undici Agent's connection-time `lookup`, so
244
+ * the validated address IS the one the socket connects to — closing the
245
+ * DNS-rebinding / TOCTOU gap a separate pre-validation lookup would leave open.
246
+ * Exported so the guard can be unit-tested without standing up a server. */
247
+ export function createValidatingLookup(lookupImpl = dnsLookupCb) {
248
+ return function validatingLookup(hostname, options, callback) {
249
+ const cb = (typeof options === 'function' ? options : callback);
250
+ const opts = (typeof options === 'function' ? {} : (options ?? {}));
251
+ lookupImpl(hostname, opts, (err, address, family) => {
252
+ if (err)
253
+ return cb(err);
254
+ const list = Array.isArray(address) ? address : [{ address, family }];
255
+ for (const entry of list) {
256
+ const addr = typeof entry === 'string' ? entry : entry.address;
257
+ if (isPrivateAddress(addr)) {
258
+ return cb(new AssetCacheError(400, 'host resolves to a private address'));
259
+ }
260
+ }
261
+ return cb(null, address, family);
262
+ });
263
+ };
264
+ }
265
+ /** Build a disk-backed cache for external sandboxed-content media. Concurrent
266
+ * requests for the same URL share one in-flight fetch; completed fetches
267
+ * persist to `<cacheDir>/<sha256>` (+ `.json` sidecar) and are replayed from
268
+ * disk. */
269
+ export function createAssetCache(opts) {
270
+ const cacheDir = opts.cacheDir;
271
+ const maxBytes = opts.maxBytes ?? 64 * 1024 * 1024;
272
+ const timeoutMs = opts.fetchTimeoutMs ?? 15_000;
273
+ const fetchImpl = opts.fetchImpl ?? fetch;
274
+ // Pin SSRF validation to the actual outbound connection: the Agent resolves
275
+ // the host once, and `validatingLookup` rejects the connection if that exact
276
+ // address is private. No separate pre-validation lookup to rebind around.
277
+ const dispatcher = new Agent({
278
+ connect: { lookup: createValidatingLookup(opts.lookupImpl) },
279
+ });
280
+ const inflight = new Map();
281
+ function keyFor(rawUrl) {
282
+ return createHash('sha256').update(rawUrl).digest('hex');
283
+ }
284
+ async function readFromDisk(key) {
285
+ const blobPath = path.join(cacheDir, key);
286
+ const metaPath = `${blobPath}.json`;
287
+ try {
288
+ const [buf, metaRaw] = await Promise.all([readFile(blobPath), readFile(metaPath, 'utf8')]);
289
+ const meta = JSON.parse(metaRaw);
290
+ const contentType = typeof meta.contentType === 'string' && meta.contentType ? meta.contentType : 'application/octet-stream';
291
+ return { buf, contentType };
292
+ }
293
+ catch {
294
+ return null;
295
+ }
296
+ }
297
+ async function writeToDisk(key, result) {
298
+ await mkdir(cacheDir, { recursive: true });
299
+ const blobPath = path.join(cacheDir, key);
300
+ const tmpPath = `${blobPath}.${process.pid}.tmp`;
301
+ await writeFile(tmpPath, result.buf);
302
+ await rename(tmpPath, blobPath);
303
+ await writeFile(`${blobPath}.json`, JSON.stringify({ contentType: result.contentType }));
304
+ }
305
+ function resolveContentType(headerValue, url) {
306
+ // `(headerValue ?? '')` is reachable (no content-type header at all). The
307
+ // trailing `?? ''` after the optional-chained `.split(';')[0]?.trim()` is
308
+ // not: `String#split` always returns a non-empty array, so `[0]` is never
309
+ // `undefined` and `?.` never short-circuits — kept for
310
+ // `noUncheckedIndexedAccess`, not a real fallback path.
311
+ const header = (headerValue ?? '').split(';')[0].trim().toLowerCase();
312
+ if (/^(?:image|video|audio)\//.test(header))
313
+ return header;
314
+ const ext = path.posix.extname(url.pathname).toLowerCase();
315
+ const guessed = EXT_TO_MIME[ext];
316
+ if (guessed)
317
+ return guessed;
318
+ throw new AssetCacheError(415, `unsupported content-type: ${header || 'unknown'}`);
319
+ }
320
+ /** Drain the body, aborting the moment the accumulated size exceeds
321
+ * `maxBytes`. A response without a trustworthy Content-Length must never be
322
+ * fully buffered first — that would turn this caller-supplied proxy into a
323
+ * memory-exhaustion path. */
324
+ async function readBodyCapped(response, controller) {
325
+ const body = response.body;
326
+ if (!body) {
327
+ const ab = await response.arrayBuffer();
328
+ if (ab.byteLength > maxBytes)
329
+ throw new AssetCacheError(413, 'asset exceeds size limit');
330
+ return Buffer.from(ab);
331
+ }
332
+ const reader = body.getReader();
333
+ const chunks = [];
334
+ let total = 0;
335
+ try {
336
+ for (;;) {
337
+ const { done, value } = await reader.read();
338
+ if (done)
339
+ break;
340
+ if (!value)
341
+ continue;
342
+ total += value.byteLength;
343
+ if (total > maxBytes) {
344
+ controller.abort(); // stop pulling more bytes from upstream
345
+ throw new AssetCacheError(413, 'asset exceeds size limit');
346
+ }
347
+ chunks.push(value);
348
+ }
349
+ }
350
+ finally {
351
+ try {
352
+ await reader.cancel();
353
+ }
354
+ catch {
355
+ // reader already closed / errored — nothing to release
356
+ }
357
+ }
358
+ return Buffer.concat(chunks);
359
+ }
360
+ async function fetchAndStore(rawUrl, key) {
361
+ const url = assertSafePublicUrl(rawUrl);
362
+ const controller = new AbortController();
363
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
364
+ try {
365
+ let response;
366
+ try {
367
+ // `dispatcher` carries the connection-time SSRF guard. It is an undici
368
+ // extension of RequestInit; attach it at runtime to avoid the
369
+ // undici-types (bundled with @types/node) vs undici@7 Dispatcher version
370
+ // skew that a typed field would trip over.
371
+ const init = { redirect: 'error', signal: controller.signal };
372
+ init.dispatcher = dispatcher;
373
+ response = await fetchImpl(url, init);
374
+ }
375
+ catch (err) {
376
+ throw new AssetCacheError(502, `fetch failed: ${err instanceof Error ? err.message : String(err)}`);
377
+ }
378
+ if (!response.ok) {
379
+ throw new AssetCacheError(502, `upstream responded ${response.status}`);
380
+ }
381
+ const declaredLength = Number(response.headers.get('content-length') ?? '');
382
+ if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
383
+ throw new AssetCacheError(413, 'asset exceeds size limit');
384
+ }
385
+ const contentType = resolveContentType(response.headers.get('content-type'), url);
386
+ const buf = await readBodyCapped(response, controller);
387
+ const result = { buf, contentType };
388
+ try {
389
+ await writeToDisk(key, result);
390
+ }
391
+ catch {
392
+ // A cache-write failure must not fail the request; serve from memory.
393
+ }
394
+ return result;
395
+ }
396
+ finally {
397
+ clearTimeout(timer);
398
+ }
399
+ }
400
+ async function get(rawUrl) {
401
+ if (!isCacheableExternalUrl(rawUrl)) {
402
+ throw new AssetCacheError(400, 'url is not a cacheable external media url');
403
+ }
404
+ const key = keyFor(rawUrl.trim());
405
+ const cached = await readFromDisk(key);
406
+ if (cached)
407
+ return cached;
408
+ const existing = inflight.get(key);
409
+ if (existing)
410
+ return existing;
411
+ const pending = fetchAndStore(rawUrl.trim(), key).finally(() => {
412
+ inflight.delete(key);
413
+ });
414
+ inflight.set(key, pending);
415
+ return pending;
416
+ }
417
+ return { get };
418
+ }
419
+ /** Exposed for tests that need to assert the on-disk key layout. */
420
+ export function assetCacheKey(rawUrl) {
421
+ return createHash('sha256').update(rawUrl.trim()).digest('hex');
422
+ }
423
+ //# sourceMappingURL=asset-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-cache.js","sourceRoot":"","sources":["../src/asset-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /** AWS credentials used to derive a SigV4 signature. */
2
+ export interface SigV4Credentials {
3
+ accessKeyId: string;
4
+ secretAccessKey: string;
5
+ sessionToken?: string;
6
+ }
7
+ /** Input to {@link signSigV4}. */
8
+ export interface SignSigV4Input {
9
+ method: string;
10
+ /**
11
+ * Canonical path (URI-encoded but with `/` preserved between segments).
12
+ * For S3 object keys, the caller must double-encode RFC-3986-reserved
13
+ * chars beyond the canonical pass.
14
+ */
15
+ path: string;
16
+ /** Already-stringified canonical query (sorted `key=value` join). Pass `''` when the request has no query. */
17
+ query: string;
18
+ /** Mutable lower-case header map. The signer adds the four amz headers and the Authorization header. */
19
+ headers: Record<string, string>;
20
+ /** Raw body bytes; pass an empty Buffer for no-body requests. */
21
+ body: Buffer;
22
+ region: string;
23
+ service: string;
24
+ credentials: SigV4Credentials;
25
+ /** ISO-8601 UTC datetime used for the signature. Defaults to `new Date()`. */
26
+ now?: Date;
27
+ }
28
+ /** Result of {@link signSigV4} — the same values written into `input.headers`, surfaced directly for convenience. */
29
+ export interface SignSigV4Result {
30
+ authorization: string;
31
+ amzDate: string;
32
+ contentSha256: string;
33
+ }
34
+ /**
35
+ * Signs an HTTP request per the AWS Signature Version 4 recipe, mutating
36
+ * `input.headers` in place and returning the same signature fields.
37
+ *
38
+ * @param input - Method/path/query/headers/body plus region, service, and credentials.
39
+ * @returns The `authorization` header value, the `x-amz-date` used, and the body's SHA-256 hex digest.
40
+ */
41
+ export declare function signSigV4(input: SignSigV4Input): SignSigV4Result;
42
+ /**
43
+ * URI-encodes a path segment per RFC 3986, with the AWS twist that reserved
44
+ * chars outside the unreserved set (`!'()*`) get `%HH`-encoded even though
45
+ * `encodeURIComponent` leaves them alone.
46
+ *
47
+ * @param seg - A single path segment (no `/`).
48
+ * @returns The RFC-3986/AWS-encoded segment.
49
+ */
50
+ export declare function encodeS3PathSegment(seg: string): string;
51
+ //# sourceMappingURL=aws-sigv4.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws-sigv4.d.ts","sourceRoot":"","sources":["../src/aws-sigv4.ts"],"names":[],"mappings":"AA2BA,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,kCAAkC;AAClC,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,8GAA8G;IAC9G,KAAK,EAAE,MAAM,CAAC;IACd,wGAAwG;IACxG,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,8EAA8E;IAC9E,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,qHAAqH;AACrH,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAmDhE;AAoBD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * @module aws-sigv4
3
+ *
4
+ * Minimal AWS Signature V4 signer, implementing the spec'd canonical-request /
5
+ * string-to-sign / signing-key / authorization recipe with only `node:crypto`.
6
+ * No `@aws-sdk/*` dependency is pulled in — a host daemon that only needs to
7
+ * sign a handful of S3-compatible requests stays free of an extra 60+ MB of
8
+ * SDK code on disk.
9
+ *
10
+ * References:
11
+ * https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
12
+ * https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
13
+ *
14
+ * Caller passes the HTTP method + path + query + headers + body, the region +
15
+ * service (e.g. `'s3'`) + AWS access key + secret key, an optional
16
+ * `sessionToken` (for STS-vended creds), and an optional `now` override so
17
+ * tests can pin deterministic signatures.
18
+ *
19
+ * The signer mutates the supplied headers map by adding `authorization`,
20
+ * `x-amz-date`, `x-amz-content-sha256`, and (when supplied)
21
+ * `x-amz-security-token`. The caller is responsible for setting `host`.
22
+ *
23
+ * Path-style vs virtual-host-style: this signer does not care; the caller
24
+ * chooses by setting `host` + path appropriately.
25
+ */
26
+ import { createHash, createHmac } from 'node:crypto';
27
+ /**
28
+ * Signs an HTTP request per the AWS Signature Version 4 recipe, mutating
29
+ * `input.headers` in place and returning the same signature fields.
30
+ *
31
+ * @param input - Method/path/query/headers/body plus region, service, and credentials.
32
+ * @returns The `authorization` header value, the `x-amz-date` used, and the body's SHA-256 hex digest.
33
+ */
34
+ export function signSigV4(input) {
35
+ const now = input.now ?? new Date();
36
+ const amzDate = formatAmzDate(now);
37
+ const dateStamp = amzDate.slice(0, 8);
38
+ const contentSha256 = sha256Hex(input.body);
39
+ // Add the required amz headers BEFORE composing the canonical request, since they're part of
40
+ // the signed-headers list.
41
+ input.headers['x-amz-date'] = amzDate;
42
+ input.headers['x-amz-content-sha256'] = contentSha256;
43
+ if (input.credentials.sessionToken) {
44
+ input.headers['x-amz-security-token'] = input.credentials.sessionToken;
45
+ }
46
+ // Canonical headers: lower-case keys + trim values; sort by key.
47
+ const headerKeys = Object.keys(input.headers)
48
+ .map((k) => k.toLowerCase())
49
+ .sort();
50
+ const canonicalHeaders = headerKeys
51
+ .map((k) => `${k}:${trimValue(input.headers[k] ?? input.headers[k.toLowerCase()] ?? '')}\n`)
52
+ .join('');
53
+ const signedHeaders = headerKeys.join(';');
54
+ const canonicalRequest = [
55
+ input.method.toUpperCase(),
56
+ input.path,
57
+ input.query,
58
+ canonicalHeaders,
59
+ signedHeaders,
60
+ contentSha256,
61
+ ].join('\n');
62
+ const credentialScope = `${dateStamp}/${input.region}/${input.service}/aws4_request`;
63
+ const stringToSign = [
64
+ 'AWS4-HMAC-SHA256',
65
+ amzDate,
66
+ credentialScope,
67
+ sha256Hex(Buffer.from(canonicalRequest, 'utf8')),
68
+ ].join('\n');
69
+ // Derive the signing key.
70
+ const kDate = hmac(`AWS4${input.credentials.secretAccessKey}`, dateStamp);
71
+ const kRegion = hmac(kDate, input.region);
72
+ const kService = hmac(kRegion, input.service);
73
+ const kSigning = hmac(kService, 'aws4_request');
74
+ const signature = createHmac('sha256', kSigning).update(stringToSign).digest('hex');
75
+ const authorization = `AWS4-HMAC-SHA256 Credential=${input.credentials.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
76
+ input.headers['authorization'] = authorization;
77
+ return { authorization, amzDate, contentSha256 };
78
+ }
79
+ function sha256Hex(body) {
80
+ return createHash('sha256').update(body).digest('hex');
81
+ }
82
+ function hmac(key, value) {
83
+ return createHmac('sha256', key).update(value).digest();
84
+ }
85
+ function trimValue(v) {
86
+ return v.trim().replace(/\s+/g, ' ');
87
+ }
88
+ function formatAmzDate(d) {
89
+ const iso = d.toISOString().replace(/[-:]/g, '');
90
+ // ISO is YYYYMMDDTHHmmss.sssZ; AWS expects YYYYMMDDTHHMMSSZ.
91
+ return `${iso.slice(0, 15)}Z`;
92
+ }
93
+ /**
94
+ * URI-encodes a path segment per RFC 3986, with the AWS twist that reserved
95
+ * chars outside the unreserved set (`!'()*`) get `%HH`-encoded even though
96
+ * `encodeURIComponent` leaves them alone.
97
+ *
98
+ * @param seg - A single path segment (no `/`).
99
+ * @returns The RFC-3986/AWS-encoded segment.
100
+ */
101
+ export function encodeS3PathSegment(seg) {
102
+ return encodeURIComponent(seg).replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());
103
+ }
104
+ //# sourceMappingURL=aws-sigv4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws-sigv4.js","sourceRoot":"","sources":["../src/aws-sigv4.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAsCrD;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACtC,MAAM,aAAa,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE5C,6FAA6F;IAC7F,2BAA2B;IAC3B,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC;IACtC,KAAK,CAAC,OAAO,CAAC,sBAAsB,CAAC,GAAG,aAAa,CAAC;IACtD,IAAI,KAAK,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC;QACnC,KAAK,CAAC,OAAO,CAAC,sBAAsB,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC;IACzE,CAAC;IAED,iEAAiE;IACjE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;SAC3B,IAAI,EAAE,CAAC;IACV,MAAM,gBAAgB,GAAG,UAAU;SAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC;SAC3F,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE3C,MAAM,gBAAgB,GAAG;QACvB,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE;QAC1B,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,KAAK;QACX,gBAAgB;QAChB,aAAa;QACb,aAAa;KACd,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,MAAM,eAAe,GAAG,GAAG,SAAS,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,eAAe,CAAC;IACrF,MAAM,YAAY,GAAG;QACnB,kBAAkB;QAClB,OAAO;QACP,eAAe;QACf,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;KACjD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,0BAA0B;IAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,KAAK,CAAC,WAAW,CAAC,eAAe,EAAE,EAAE,SAAS,CAAC,CAAC;IAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IAEhD,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpF,MAAM,aAAa,GAAG,+BAA+B,KAAK,CAAC,WAAW,CAAC,WAAW,IAAI,eAAe,mBAAmB,aAAa,eAAe,SAAS,EAAE,CAAC;IAChK,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,aAAa,CAAC;IAE/C,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;AACnD,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,IAAI,CAAC,GAAoB,EAAE,KAAa;IAC/C,OAAO,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;AAC1D,CAAC;AAED,SAAS,SAAS,CAAC,CAAS;IAC1B,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,aAAa,CAAC,CAAO;IAC5B,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACjD,6DAA6D;IAC7D,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC;AAChC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,OAAO,kBAAkB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC9G,CAAC"}