effortless-aws 0.33.0 → 0.34.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.
- package/dist/{chunk-HGSMOO4A.js → chunk-6HFS224S.js} +173 -34
- package/dist/index.d.ts +230 -48
- package/dist/index.js +38 -2
- package/dist/index.js.map +1 -1
- package/dist/runtime/wrap-api.js +9 -2
- package/dist/runtime/wrap-bucket.js +14 -3
- package/dist/runtime/wrap-cron.js +1 -1
- package/dist/runtime/wrap-fifo-queue.js +1 -1
- package/dist/runtime/wrap-mcp.js +235 -0
- package/dist/runtime/wrap-table-stream.js +1 -1
- package/dist/runtime/wrap-worker.js +1 -1
- package/package.json +3 -2
|
@@ -233,9 +233,6 @@ var createTableClient = (tableName, options) => {
|
|
|
233
233
|
};
|
|
234
234
|
};
|
|
235
235
|
|
|
236
|
-
// src/handlers/auth.ts
|
|
237
|
-
import * as crypto from "crypto";
|
|
238
|
-
|
|
239
236
|
// src/handlers/handler-options.ts
|
|
240
237
|
var toSeconds = (d) => {
|
|
241
238
|
if (typeof d === "number") return d;
|
|
@@ -250,10 +247,35 @@ var toSeconds = (d) => {
|
|
|
250
247
|
};
|
|
251
248
|
|
|
252
249
|
// src/handlers/auth.ts
|
|
250
|
+
import * as crypto from "crypto";
|
|
251
|
+
var cfBase64Encode = (buffer) => buffer.toString("base64").replace(/\+/g, "-").replace(/=/g, "_").replace(/\//g, "~");
|
|
252
|
+
var signCfCookies = (policy, config) => {
|
|
253
|
+
const ttlSeconds = toSeconds(policy.ttl);
|
|
254
|
+
const expireTime = Math.floor(Date.now() / 1e3) + ttlSeconds;
|
|
255
|
+
const resource = config.domain === "*" ? `https://*${policy.path}` : `https://${config.domain}${policy.path}`;
|
|
256
|
+
const policyJson = JSON.stringify({
|
|
257
|
+
Statement: [{
|
|
258
|
+
Resource: resource,
|
|
259
|
+
Condition: {
|
|
260
|
+
DateLessThan: { "AWS:EpochTime": expireTime }
|
|
261
|
+
}
|
|
262
|
+
}]
|
|
263
|
+
});
|
|
264
|
+
const policyBase64 = cfBase64Encode(Buffer.from(policyJson, "utf-8"));
|
|
265
|
+
const signature = cfBase64Encode(
|
|
266
|
+
crypto.sign("sha1", Buffer.from(policyJson, "utf-8"), config.privateKey)
|
|
267
|
+
);
|
|
268
|
+
const cookieAttrs = `; Secure; SameSite=Lax; Path=/; Max-Age=${ttlSeconds}`;
|
|
269
|
+
return [
|
|
270
|
+
`CloudFront-Policy=${policyBase64}${cookieAttrs}`,
|
|
271
|
+
`CloudFront-Signature=${signature}${cookieAttrs}`,
|
|
272
|
+
`CloudFront-Key-Pair-Id=${config.keyPairId}${cookieAttrs}`
|
|
273
|
+
];
|
|
274
|
+
};
|
|
253
275
|
var AUTH_COOKIE_NAME = "__eff_session";
|
|
254
|
-
var createAuthRuntime = (secret, defaultExpiresIn, apiTokenVerify, apiTokenHeader, apiTokenCacheTtlSeconds) => {
|
|
276
|
+
var createAuthRuntime = (secret, defaultExpiresIn, apiTokenVerify, apiTokenHeader, apiTokenCacheTtlSeconds, cfSigningConfig) => {
|
|
255
277
|
const tokenCache = apiTokenCacheTtlSeconds ? /* @__PURE__ */ new Map() : void 0;
|
|
256
|
-
const
|
|
278
|
+
const sign2 = (payload) => crypto.createHmac("sha256", secret).update(payload).digest("base64url");
|
|
257
279
|
const cookieBase = `${AUTH_COOKIE_NAME}=`;
|
|
258
280
|
const cookieAttrs = "; HttpOnly; Secure; SameSite=Lax; Path=/";
|
|
259
281
|
const decodeSession = (cookieValue) => {
|
|
@@ -262,7 +284,7 @@ var createAuthRuntime = (secret, defaultExpiresIn, apiTokenVerify, apiTokenHeade
|
|
|
262
284
|
if (dot === -1) return void 0;
|
|
263
285
|
const payload = cookieValue.slice(0, dot);
|
|
264
286
|
const sig = cookieValue.slice(dot + 1);
|
|
265
|
-
if (
|
|
287
|
+
if (sign2(payload) !== sig) return void 0;
|
|
266
288
|
try {
|
|
267
289
|
const parsed = JSON.parse(Buffer.from(payload, "base64url").toString("utf-8"));
|
|
268
290
|
if (parsed.exp <= Math.floor(Date.now() / 1e3)) return void 0;
|
|
@@ -284,13 +306,16 @@ var createAuthRuntime = (secret, defaultExpiresIn, apiTokenVerify, apiTokenHeade
|
|
|
284
306
|
const seconds = options?.expiresIn ? toSeconds(options.expiresIn) : defaultExpiresIn;
|
|
285
307
|
const exp = Math.floor(Date.now() / 1e3) + seconds;
|
|
286
308
|
const payload = Buffer.from(JSON.stringify({ exp, ...data }), "utf-8").toString("base64url");
|
|
287
|
-
const sig =
|
|
309
|
+
const sig = sign2(payload);
|
|
310
|
+
const sessionCookie = `${cookieBase}${payload}.${sig}${cookieAttrs}; Max-Age=${seconds}`;
|
|
311
|
+
const cfCookies = options?.cdnPolicy && cfSigningConfig ? signCfCookies(options.cdnPolicy, cfSigningConfig) : void 0;
|
|
288
312
|
return {
|
|
289
313
|
status: 200,
|
|
290
314
|
body: { ok: true },
|
|
291
315
|
headers: {
|
|
292
|
-
"set-cookie":
|
|
293
|
-
}
|
|
316
|
+
"set-cookie": sessionCookie
|
|
317
|
+
},
|
|
318
|
+
...cfCookies ? { cookies: [sessionCookie, ...cfCookies] } : {}
|
|
294
319
|
};
|
|
295
320
|
},
|
|
296
321
|
clearSession() {
|
|
@@ -327,34 +352,91 @@ var createAuthRuntime = (secret, defaultExpiresIn, apiTokenVerify, apiTokenHeade
|
|
|
327
352
|
|
|
328
353
|
// src/runtime/bucket-client.ts
|
|
329
354
|
import { S3 } from "@aws-sdk/client-s3";
|
|
330
|
-
var
|
|
331
|
-
|
|
332
|
-
|
|
355
|
+
var createBucketMethods = (getClient2, bucketName) => ({
|
|
356
|
+
bucketName,
|
|
357
|
+
async put(key, body, options) {
|
|
358
|
+
await getClient2().putObject({
|
|
359
|
+
Bucket: bucketName,
|
|
360
|
+
Key: key,
|
|
361
|
+
Body: typeof body === "string" ? Buffer.from(body) : body,
|
|
362
|
+
...options?.contentType ? { ContentType: options.contentType } : {}
|
|
363
|
+
});
|
|
364
|
+
},
|
|
365
|
+
async get(key) {
|
|
366
|
+
try {
|
|
367
|
+
const result = await getClient2().getObject({
|
|
368
|
+
Bucket: bucketName,
|
|
369
|
+
Key: key
|
|
370
|
+
});
|
|
371
|
+
const chunks = [];
|
|
372
|
+
const stream = result.Body;
|
|
373
|
+
for await (const chunk of stream) {
|
|
374
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
body: Buffer.concat(chunks),
|
|
378
|
+
contentType: result.ContentType
|
|
379
|
+
};
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (error instanceof Error && (error.name === "NoSuchKey" || error.$metadata?.httpStatusCode === 404)) {
|
|
382
|
+
return void 0;
|
|
383
|
+
}
|
|
384
|
+
throw error;
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
async delete(key) {
|
|
388
|
+
await getClient2().deleteObject({
|
|
389
|
+
Bucket: bucketName,
|
|
390
|
+
Key: key
|
|
391
|
+
});
|
|
392
|
+
},
|
|
393
|
+
async list(prefix) {
|
|
394
|
+
const items = [];
|
|
395
|
+
let continuationToken;
|
|
396
|
+
do {
|
|
397
|
+
const result = await getClient2().listObjectsV2({
|
|
398
|
+
Bucket: bucketName,
|
|
399
|
+
...prefix ? { Prefix: prefix } : {},
|
|
400
|
+
...continuationToken ? { ContinuationToken: continuationToken } : {}
|
|
401
|
+
});
|
|
402
|
+
for (const obj of result.Contents ?? []) {
|
|
403
|
+
if (obj.Key) {
|
|
404
|
+
items.push({
|
|
405
|
+
key: obj.Key,
|
|
406
|
+
size: obj.Size ?? 0,
|
|
407
|
+
lastModified: obj.LastModified
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
continuationToken = result.IsTruncated ? result.NextContinuationToken : void 0;
|
|
412
|
+
} while (continuationToken);
|
|
413
|
+
return items;
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
var createEntityClient = (getClient2, bucketName, entityName, cacheSeconds) => {
|
|
417
|
+
const entityKey = (id) => `${entityName}/${id}.json`;
|
|
333
418
|
return {
|
|
334
|
-
|
|
335
|
-
async put(key, body, options) {
|
|
419
|
+
async put(id, data) {
|
|
336
420
|
await getClient2().putObject({
|
|
337
421
|
Bucket: bucketName,
|
|
338
|
-
Key:
|
|
339
|
-
Body:
|
|
340
|
-
|
|
422
|
+
Key: entityKey(id),
|
|
423
|
+
Body: JSON.stringify(data),
|
|
424
|
+
ContentType: "application/json",
|
|
425
|
+
...cacheSeconds != null ? { CacheControl: `public, max-age=${cacheSeconds}` } : {}
|
|
341
426
|
});
|
|
342
427
|
},
|
|
343
|
-
async get(
|
|
428
|
+
async get(id) {
|
|
344
429
|
try {
|
|
345
430
|
const result = await getClient2().getObject({
|
|
346
431
|
Bucket: bucketName,
|
|
347
|
-
Key:
|
|
432
|
+
Key: entityKey(id)
|
|
348
433
|
});
|
|
349
434
|
const chunks = [];
|
|
350
435
|
const stream = result.Body;
|
|
351
436
|
for await (const chunk of stream) {
|
|
352
437
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
353
438
|
}
|
|
354
|
-
return
|
|
355
|
-
body: Buffer.concat(chunks),
|
|
356
|
-
contentType: result.ContentType
|
|
357
|
-
};
|
|
439
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf-8"));
|
|
358
440
|
} catch (error) {
|
|
359
441
|
if (error instanceof Error && (error.name === "NoSuchKey" || error.$metadata?.httpStatusCode === 404)) {
|
|
360
442
|
return void 0;
|
|
@@ -362,28 +444,37 @@ var createBucketClient = (bucketName) => {
|
|
|
362
444
|
throw error;
|
|
363
445
|
}
|
|
364
446
|
},
|
|
365
|
-
async delete(
|
|
447
|
+
async delete(id) {
|
|
366
448
|
await getClient2().deleteObject({
|
|
367
449
|
Bucket: bucketName,
|
|
368
|
-
Key:
|
|
450
|
+
Key: entityKey(id)
|
|
369
451
|
});
|
|
370
452
|
},
|
|
371
|
-
async list(
|
|
453
|
+
async list() {
|
|
372
454
|
const items = [];
|
|
373
455
|
let continuationToken;
|
|
456
|
+
const prefix = `${entityName}/`;
|
|
374
457
|
do {
|
|
375
458
|
const result = await getClient2().listObjectsV2({
|
|
376
459
|
Bucket: bucketName,
|
|
377
|
-
|
|
460
|
+
Prefix: prefix,
|
|
378
461
|
...continuationToken ? { ContinuationToken: continuationToken } : {}
|
|
379
462
|
});
|
|
380
463
|
for (const obj of result.Contents ?? []) {
|
|
381
|
-
if (obj.Key)
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
464
|
+
if (!obj.Key || !obj.Key.endsWith(".json")) continue;
|
|
465
|
+
const id = obj.Key.slice(prefix.length, -".json".length);
|
|
466
|
+
try {
|
|
467
|
+
const getResult = await getClient2().getObject({
|
|
468
|
+
Bucket: bucketName,
|
|
469
|
+
Key: obj.Key
|
|
386
470
|
});
|
|
471
|
+
const chunks = [];
|
|
472
|
+
const stream = getResult.Body;
|
|
473
|
+
for await (const chunk of stream) {
|
|
474
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
475
|
+
}
|
|
476
|
+
items.push({ id, data: JSON.parse(Buffer.concat(chunks).toString("utf-8")) });
|
|
477
|
+
} catch {
|
|
387
478
|
}
|
|
388
479
|
}
|
|
389
480
|
continuationToken = result.IsTruncated ? result.NextContinuationToken : void 0;
|
|
@@ -392,6 +483,21 @@ var createBucketClient = (bucketName) => {
|
|
|
392
483
|
}
|
|
393
484
|
};
|
|
394
485
|
};
|
|
486
|
+
var createBucketClient = (bucketName) => {
|
|
487
|
+
let client2 = null;
|
|
488
|
+
const getClient2 = () => client2 ??= new S3({});
|
|
489
|
+
return createBucketMethods(getClient2, bucketName);
|
|
490
|
+
};
|
|
491
|
+
var createBucketClientWithEntities = (bucketName, entitiesConfig) => {
|
|
492
|
+
let client2 = null;
|
|
493
|
+
const getClient2 = () => client2 ??= new S3({});
|
|
494
|
+
const base = createBucketMethods(getClient2, bucketName);
|
|
495
|
+
const result = { ...base };
|
|
496
|
+
for (const [entityName, config] of Object.entries(entitiesConfig)) {
|
|
497
|
+
result[entityName] = createEntityClient(getClient2, bucketName, entityName, config.cacheSeconds);
|
|
498
|
+
}
|
|
499
|
+
return result;
|
|
500
|
+
};
|
|
395
501
|
|
|
396
502
|
// src/runtime/handler-utils.ts
|
|
397
503
|
import { readFileSync } from "fs";
|
|
@@ -566,7 +672,17 @@ var DEP_FACTORIES = {
|
|
|
566
672
|
const tagField = depHandler?.__spec?.tagField;
|
|
567
673
|
return createTableClient(name, tagField ? { tagField } : void 0);
|
|
568
674
|
},
|
|
569
|
-
bucket: (name) =>
|
|
675
|
+
bucket: (name, depHandler) => {
|
|
676
|
+
const entities = depHandler?.__spec?.entities;
|
|
677
|
+
if (entities && Object.keys(entities).length > 0) {
|
|
678
|
+
const config = {};
|
|
679
|
+
for (const [entityName, entityOpts] of Object.entries(entities)) {
|
|
680
|
+
config[entityName] = entityOpts.cache ? { cacheSeconds: toSeconds(entityOpts.cache) } : {};
|
|
681
|
+
}
|
|
682
|
+
return createBucketClientWithEntities(name, config);
|
|
683
|
+
}
|
|
684
|
+
return createBucketClient(name);
|
|
685
|
+
},
|
|
570
686
|
mailer: () => createEmailClient(),
|
|
571
687
|
queue: (name) => createQueueClient(name),
|
|
572
688
|
worker: (name) => createWorkerClient(name)
|
|
@@ -630,6 +746,25 @@ var createHandlerRuntime = (handler, handlerType, logLevel = "info", extraSetupA
|
|
|
630
746
|
resolvedParams = await buildParams(handler.config);
|
|
631
747
|
return resolvedParams;
|
|
632
748
|
};
|
|
749
|
+
let resolvedCfSigningConfig = null;
|
|
750
|
+
const getCfSigningConfig = async () => {
|
|
751
|
+
if (resolvedCfSigningConfig !== null) return resolvedCfSigningConfig;
|
|
752
|
+
const cfSigningKeySsmPath = process.env.EFF_CF_SIGNING_KEY;
|
|
753
|
+
const cfKeyPairId = process.env.EFF_CF_KEY_PAIR_ID;
|
|
754
|
+
const cfDomain = process.env.EFF_CF_DOMAIN;
|
|
755
|
+
if (!cfSigningKeySsmPath || !cfKeyPairId || !cfDomain) {
|
|
756
|
+
resolvedCfSigningConfig = void 0;
|
|
757
|
+
return void 0;
|
|
758
|
+
}
|
|
759
|
+
const values = await getParameters([cfSigningKeySsmPath]);
|
|
760
|
+
const privateKey = values.get(cfSigningKeySsmPath);
|
|
761
|
+
if (!privateKey) {
|
|
762
|
+
resolvedCfSigningConfig = void 0;
|
|
763
|
+
return void 0;
|
|
764
|
+
}
|
|
765
|
+
resolvedCfSigningConfig = { privateKey, keyPairId: cfKeyPairId, domain: cfDomain };
|
|
766
|
+
return resolvedCfSigningConfig;
|
|
767
|
+
};
|
|
633
768
|
const getAuthRuntime = async (setupCtx) => {
|
|
634
769
|
if (resolvedAuthRuntime !== null) return resolvedAuthRuntime;
|
|
635
770
|
const authOpts = setupCtx && typeof setupCtx === "object" && "auth" in setupCtx ? setupCtx.auth : void 0;
|
|
@@ -644,12 +779,14 @@ var createHandlerRuntime = (handler, handlerType, logLevel = "info", extraSetupA
|
|
|
644
779
|
const cacheTtlSeconds = apiToken?.cacheTtl ? toSeconds(apiToken.cacheTtl) : void 0;
|
|
645
780
|
const rawVerify = apiToken?.verify;
|
|
646
781
|
const wrappedVerify = rawVerify ? (args) => rawVerify(args.value) : void 0;
|
|
782
|
+
const cfSigningConfig = await getCfSigningConfig();
|
|
647
783
|
resolvedAuthRuntime = createAuthRuntime(
|
|
648
784
|
secret,
|
|
649
785
|
defaultExpires,
|
|
650
786
|
wrappedVerify,
|
|
651
787
|
apiToken?.header,
|
|
652
|
-
cacheTtlSeconds
|
|
788
|
+
cacheTtlSeconds,
|
|
789
|
+
cfSigningConfig
|
|
653
790
|
);
|
|
654
791
|
return resolvedAuthRuntime;
|
|
655
792
|
};
|
|
@@ -733,8 +870,10 @@ var createHandlerRuntime = (handler, handlerType, logLevel = "info", extraSetupA
|
|
|
733
870
|
|
|
734
871
|
export {
|
|
735
872
|
createTableClient,
|
|
873
|
+
toSeconds,
|
|
736
874
|
AUTH_COOKIE_NAME,
|
|
737
875
|
createBucketClient,
|
|
876
|
+
createBucketClientWithEntities,
|
|
738
877
|
buildDeps,
|
|
739
878
|
buildParams,
|
|
740
879
|
createHandlerRuntime
|