@ericminassian/platform 0.0.0-stage → 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 (78) hide show
  1. package/dist/app.d.ts +10 -0
  2. package/dist/app.js +23 -0
  3. package/dist/app.js.map +1 -0
  4. package/dist/auth/cookies.d.ts +10 -0
  5. package/dist/auth/cookies.js +37 -0
  6. package/dist/auth/cookies.js.map +1 -0
  7. package/dist/auth/index.d.ts +22 -0
  8. package/dist/auth/index.js +217 -0
  9. package/dist/auth/index.js.map +1 -0
  10. package/dist/auth/pkce.d.ts +16 -0
  11. package/dist/auth/pkce.js +32 -0
  12. package/dist/auth/pkce.js.map +1 -0
  13. package/dist/auth/tokens.d.ts +17 -0
  14. package/dist/auth/tokens.js +142 -0
  15. package/dist/auth/tokens.js.map +1 -0
  16. package/dist/bindings.d.ts +13 -0
  17. package/dist/bindings.js +32 -0
  18. package/dist/bindings.js.map +1 -0
  19. package/dist/clients.d.ts +38 -0
  20. package/dist/clients.js +65 -0
  21. package/dist/clients.js.map +1 -0
  22. package/dist/context.d.ts +31 -0
  23. package/dist/context.js +33 -0
  24. package/dist/context.js.map +1 -0
  25. package/dist/db.d.ts +97 -0
  26. package/dist/db.js +297 -0
  27. package/dist/db.js.map +1 -0
  28. package/dist/events.d.ts +9 -0
  29. package/dist/events.js +28 -0
  30. package/dist/events.js.map +1 -0
  31. package/dist/http.d.ts +14 -0
  32. package/dist/http.js +82 -0
  33. package/dist/http.js.map +1 -0
  34. package/dist/index.d.ts +30 -0
  35. package/dist/index.js +11 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/queue.d.ts +7 -0
  38. package/dist/queue.js +50 -0
  39. package/dist/queue.js.map +1 -0
  40. package/dist/secrets.d.ts +8 -0
  41. package/dist/secrets.js +42 -0
  42. package/dist/secrets.js.map +1 -0
  43. package/dist/server.d.ts +12 -0
  44. package/dist/server.js +232 -0
  45. package/dist/server.js.map +1 -0
  46. package/dist/storage.d.ts +30 -0
  47. package/dist/storage.js +108 -0
  48. package/dist/storage.js.map +1 -0
  49. package/dist/telemetry.d.ts +42 -0
  50. package/dist/telemetry.js +218 -0
  51. package/dist/telemetry.js.map +1 -0
  52. package/dist/testing.d.ts +31 -0
  53. package/dist/testing.js +54 -0
  54. package/dist/testing.js.map +1 -0
  55. package/dist/worker.d.ts +73 -0
  56. package/dist/worker.js +179 -0
  57. package/dist/worker.js.map +1 -0
  58. package/package.json +58 -3
  59. package/src/app.ts +32 -0
  60. package/src/auth/cookies.ts +39 -0
  61. package/src/auth/index.ts +246 -0
  62. package/src/auth/pkce.ts +43 -0
  63. package/src/auth/tokens.ts +174 -0
  64. package/src/bindings.ts +35 -0
  65. package/src/clients.ts +98 -0
  66. package/src/context.ts +56 -0
  67. package/src/db.ts +380 -0
  68. package/src/events.ts +42 -0
  69. package/src/http.ts +82 -0
  70. package/src/index.ts +40 -0
  71. package/src/queue.ts +67 -0
  72. package/src/secrets.ts +57 -0
  73. package/src/server.ts +274 -0
  74. package/src/storage.ts +143 -0
  75. package/src/telemetry.ts +260 -0
  76. package/src/testing.ts +73 -0
  77. package/src/worker.ts +291 -0
  78. package/README.md +0 -3
@@ -0,0 +1,38 @@
1
+ import type { CognitoIdentityProviderClient } from '@aws-sdk/client-cognito-identity-provider';
2
+ import type { EventBridgeClient } from '@aws-sdk/client-eventbridge';
3
+ import type { LambdaClient } from '@aws-sdk/client-lambda';
4
+ import type { S3Client } from '@aws-sdk/client-s3';
5
+ import type { SQSClient } from '@aws-sdk/client-sqs';
6
+ import type { SSMClient } from '@aws-sdk/client-ssm';
7
+ import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
8
+ /**
9
+ * AWS clients, created on first use. Under `plat dev` / `plat test` the
10
+ * standard `AWS_ENDPOINT_URL` points every client at the AWS Emulator and
11
+ * `NODE_EXTRA_CA_CERTS` trusts its certificate; nothing else differs.
12
+ *
13
+ * Only DynamoDB is imported eagerly (most apps use it, and `db.raw()` is
14
+ * synchronous). Everything else is a dynamic import, which esbuild keeps in
15
+ * the same bundle but evaluates lazily — so cold starts only pay for the
16
+ * services a request actually touches.
17
+ */
18
+ export interface Clients {
19
+ ddb: DynamoDBDocumentClient;
20
+ sqs: SQSClient;
21
+ events: EventBridgeClient;
22
+ s3: S3Client;
23
+ ssm: SSMClient;
24
+ lambda: LambdaClient;
25
+ cognito: CognitoIdentityProviderClient;
26
+ }
27
+ export type LazyClientName = Exclude<keyof Clients, 'ddb'>;
28
+ /** Minimal client shape (tests may substitute `{ send }` fakes). */
29
+ export interface SendClient {
30
+ send(command: unknown): Promise<unknown>;
31
+ }
32
+ /** The DynamoDB document client (synchronous). */
33
+ export declare function ddbClient(): DynamoDBDocumentClient;
34
+ export declare function client<K extends LazyClientName>(name: K): Promise<Clients[K]>;
35
+ /** Substitute a client (tests). `undefined` removes the override. */
36
+ export declare function setClient(name: keyof Clients, fake: SendClient | undefined): void;
37
+ /** Drop cached clients (tests that change region/endpoint between runs). */
38
+ export declare function resetClients(): void;
@@ -0,0 +1,65 @@
1
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
2
+ import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
3
+ import { isEmulator, region } from './bindings.js';
4
+ const instances = {};
5
+ const pending = {};
6
+ const overrides = {};
7
+ function config() {
8
+ const endpoint = process.env.AWS_ENDPOINT_URL;
9
+ return { region: region(), ...(endpoint ? { endpoint } : {}) };
10
+ }
11
+ const lazyFactories = {
12
+ sqs: async () => new (await import('@aws-sdk/client-sqs')).SQSClient(config()),
13
+ events: async () => new (await import('@aws-sdk/client-eventbridge')).EventBridgeClient(config()),
14
+ s3: async () => new (await import('@aws-sdk/client-s3')).S3Client({
15
+ ...config(),
16
+ ...(isEmulator() ? { forcePathStyle: true } : {}),
17
+ }),
18
+ ssm: async () => new (await import('@aws-sdk/client-ssm')).SSMClient(config()),
19
+ lambda: async () => new (await import('@aws-sdk/client-lambda')).LambdaClient(config()),
20
+ cognito: async () => new (await import('@aws-sdk/client-cognito-identity-provider')).CognitoIdentityProviderClient(config()),
21
+ };
22
+ /** The DynamoDB document client (synchronous). */
23
+ export function ddbClient() {
24
+ const o = overrides.ddb;
25
+ if (o)
26
+ return o;
27
+ let c = instances.ddb;
28
+ if (!c) {
29
+ c = DynamoDBDocumentClient.from(new DynamoDBClient(config()), {
30
+ marshallOptions: { removeUndefinedValues: true, convertClassInstanceToMap: true },
31
+ });
32
+ instances.ddb = c;
33
+ }
34
+ return c;
35
+ }
36
+ export async function client(name) {
37
+ const o = overrides[name];
38
+ if (o)
39
+ return o;
40
+ const existing = instances[name];
41
+ if (existing)
42
+ return existing;
43
+ pending[name] ??= lazyFactories[name]().then((c) => {
44
+ instances[name] = c;
45
+ return c;
46
+ });
47
+ return pending[name];
48
+ }
49
+ /** Substitute a client (tests). `undefined` removes the override. */
50
+ export function setClient(name, fake) {
51
+ if (fake)
52
+ overrides[name] = fake;
53
+ else
54
+ delete overrides[name];
55
+ }
56
+ /** Drop cached clients (tests that change region/endpoint between runs). */
57
+ export function resetClients() {
58
+ for (const k of Object.keys(instances))
59
+ delete instances[k];
60
+ for (const k of Object.keys(pending))
61
+ delete pending[k];
62
+ for (const k of Object.keys(overrides))
63
+ delete overrides[k];
64
+ }
65
+ //# sourceMappingURL=clients.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clients.js","sourceRoot":"","sources":["../src/clients.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAM1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AA6BnD,MAAM,SAAS,GAA4C,EAAE,CAAC;AAC9D,MAAM,OAAO,GAAqD,EAAE,CAAC;AACrE,MAAM,SAAS,GAA+C,EAAE,CAAC;AAEjE,SAAS,MAAM;IACb,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,aAAa,GAAyD;IAC1E,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;IAC9E,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC;IACjG,EAAE,EAAE,KAAK,IAAI,EAAE,CACb,IAAI,CAAC,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,QAAQ,CAAC;QAChD,GAAG,MAAM,EAAE;QACX,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAC;IACJ,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;IAC9E,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;IACvF,OAAO,EAAE,KAAK,IAAI,EAAE,CAClB,IAAI,CAAC,MAAM,MAAM,CAAC,2CAA2C,CAAC,CAAC,CAAC,6BAA6B,CAAC,MAAM,EAAE,CAAC;CAC1G,CAAC;AAEF,kDAAkD;AAClD,MAAM,UAAU,SAAS;IACvB,MAAM,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC;IACxB,IAAI,CAAC;QAAE,OAAO,CAAsC,CAAC;IACrD,IAAI,CAAC,GAAG,SAAS,CAAC,GAAyC,CAAC;IAC5D,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,CAAC,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE;YAC5D,eAAe,EAAE,EAAE,qBAAqB,EAAE,IAAI,EAAE,yBAAyB,EAAE,IAAI,EAAE;SAClF,CAAC,CAAC;QACH,SAAS,CAAC,GAAG,GAAG,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAA2B,IAAO;IAC5D,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC;QAAE,OAAO,CAA0B,CAAC;IACzC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,QAAQ;QAAE,OAAO,QAAsB,CAAC;IAC5C,OAAO,CAAC,IAAI,CAAC,KAAK,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QACjD,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,OAAO,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,IAAI,CAAwB,CAAC;AAC9C,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,SAAS,CAAC,IAAmB,EAAE,IAA4B;IACzE,IAAI,IAAI;QAAE,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;;QAC5B,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,YAAY;IAC1B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAsB;QAAE,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;IACjF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAsB;QAAE,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7E,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAsB;QAAE,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;AACnF,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { type PlatformUser } from '@ericminassian/platform-core';
2
+ /** Per-request / per-job context carried through async calls. */
3
+ export interface RequestContext {
4
+ trace: string;
5
+ /** Span id of the root (request or job). */
6
+ span: string;
7
+ /** performance.now() when the root started, for span offsets. */
8
+ startedAt: number;
9
+ /** Set-Cookie values produced while handling the request (e.g. token refresh). */
10
+ cookies: string[];
11
+ /** Matched route pattern, set by the router integration. */
12
+ route?: string;
13
+ /** Resolved user (undefined = not looked up yet, null = signed out). */
14
+ user?: PlatformUser | null;
15
+ /** Job name for error attribution in workers. */
16
+ job?: string;
17
+ /** Error a framework handled itself (e.g. a Hono app's onError), for telemetry. */
18
+ error?: unknown;
19
+ }
20
+ export declare function currentContext(): RequestContext | undefined;
21
+ export declare function runWithContext<T>(ctx: RequestContext, fn: () => T): T;
22
+ export declare function newContext(trace?: string | null): RequestContext;
23
+ /** Trace id of the current request/job, or a fresh one outside any context. */
24
+ export declare function currentTrace(): string;
25
+ /** Parse a W3C `traceparent` header (`00-<32 hex trace>-<16 hex span>-<flags>`). */
26
+ export declare function parseTraceparent(header: string | null | undefined): string | undefined;
27
+ /**
28
+ * Record the route pattern (e.g. `/api/todos/:id`) for the current request's
29
+ * telemetry. Hono apps get this automatically; other frameworks can call it.
30
+ */
31
+ export declare function setRoute(pattern: string): void;
@@ -0,0 +1,33 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { newSpanId, newTraceId } from '@ericminassian/platform-core';
3
+ const storage = new AsyncLocalStorage();
4
+ export function currentContext() {
5
+ return storage.getStore();
6
+ }
7
+ export function runWithContext(ctx, fn) {
8
+ return storage.run(ctx, fn);
9
+ }
10
+ export function newContext(trace) {
11
+ return { trace: trace || newTraceId(), span: newSpanId(), startedAt: performance.now(), cookies: [] };
12
+ }
13
+ /** Trace id of the current request/job, or a fresh one outside any context. */
14
+ export function currentTrace() {
15
+ return storage.getStore()?.trace ?? newTraceId();
16
+ }
17
+ /** Parse a W3C `traceparent` header (`00-<32 hex trace>-<16 hex span>-<flags>`). */
18
+ export function parseTraceparent(header) {
19
+ if (!header)
20
+ return undefined;
21
+ const m = /^[\da-f]{2}-([\da-f]{32})-[\da-f]{16}-[\da-f]{2}$/i.exec(header.trim());
22
+ return m?.[1]?.toLowerCase();
23
+ }
24
+ /**
25
+ * Record the route pattern (e.g. `/api/todos/:id`) for the current request's
26
+ * telemetry. Hono apps get this automatically; other frameworks can call it.
27
+ */
28
+ export function setRoute(pattern) {
29
+ const ctx = storage.getStore();
30
+ if (ctx)
31
+ ctx.route = pattern;
32
+ }
33
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAqB,MAAM,8BAA8B,CAAC;AAqBxF,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAkB,CAAC;AAExD,MAAM,UAAU,cAAc;IAC5B,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,cAAc,CAAI,GAAmB,EAAE,EAAW;IAChE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAqB;IAC9C,OAAO,EAAE,KAAK,EAAE,KAAK,IAAI,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,WAAW,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AACxG,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,YAAY;IAC1B,OAAO,OAAO,CAAC,QAAQ,EAAE,EAAE,KAAK,IAAI,UAAU,EAAE,CAAC;AACnD,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAAC,MAAiC;IAChE,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,MAAM,CAAC,GAAG,oDAAoD,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACnF,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC/B,IAAI,GAAG;QAAE,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC;AAC/B,CAAC"}
package/dist/db.d.ts ADDED
@@ -0,0 +1,97 @@
1
+ import { type QueryCommandInput } from '@aws-sdk/lib-dynamodb';
2
+ /**
3
+ * Document collections on the stage's single DynamoDB table (single-table
4
+ * design). Adding a collection needs no provisioning.
5
+ *
6
+ * Item layout:
7
+ * pk = "<collection>#<partition values joined by #>"
8
+ * sk = "<sort values joined by #>" (or "#" without a sort key)
9
+ * gsi{n}pk = "<collection>#<index>#<values>", gsi{n}sk = sort values or "#"
10
+ * _c = collection, _ttl = epoch seconds (DynamoDB TTL)
11
+ * Internal attributes are stripped from everything returned.
12
+ */
13
+ export type KeyValue = string | number;
14
+ type Fields<T> = (keyof T & string) | readonly (keyof T & string)[];
15
+ export interface CollectionOptions<T> {
16
+ /** Attribute(s) forming the partition key. */
17
+ partition: Fields<T>;
18
+ /** Attribute(s) forming the sort key (optional). */
19
+ sort?: Fields<T>;
20
+ /** Up to three secondary indexes, each pinned to a fixed slot so reordering is safe. */
21
+ indexes?: Record<string, {
22
+ slot: 1 | 2 | 3;
23
+ partition: Fields<T>;
24
+ sort?: Fields<T>;
25
+ }>;
26
+ /** Attribute holding an epoch-seconds expiry (DynamoDB TTL). */
27
+ ttl?: keyof T & string;
28
+ }
29
+ export interface QueryOptions {
30
+ index?: string;
31
+ beginsWith?: string;
32
+ between?: [KeyValue, KeyValue];
33
+ gt?: KeyValue;
34
+ gte?: KeyValue;
35
+ lt?: KeyValue;
36
+ lte?: KeyValue;
37
+ limit?: number;
38
+ cursor?: string;
39
+ reverse?: boolean;
40
+ }
41
+ export interface Page<T> {
42
+ items: T[];
43
+ cursor?: string;
44
+ }
45
+ export interface Collection<T extends Record<string, unknown>> {
46
+ get(key: Partial<T>): Promise<T | undefined>;
47
+ put(item: T, opts?: {
48
+ ifNotExists?: boolean;
49
+ }): Promise<T>;
50
+ update(key: Partial<T>, patch: Partial<T>): Promise<T>;
51
+ delete(key: Partial<T>): Promise<void>;
52
+ /** All items sharing the given partition-key values. */
53
+ query(partition: Partial<T>, opts?: QueryOptions): Promise<Page<T>>;
54
+ }
55
+ export interface Db {
56
+ collection<T extends Record<string, unknown>>(name: string, opts: CollectionOptions<T>): Collection<T>;
57
+ /** Escape hatch (AWS-specific): the DynamoDB document client and table name. */
58
+ raw(): {
59
+ client: unknown;
60
+ table: string;
61
+ };
62
+ }
63
+ export declare const INTERNAL_ATTRIBUTES: string[];
64
+ /** Values for `fields` from `item`, joined by `#`. Throws when any is missing. */
65
+ export declare function keyPart(item: Record<string, unknown>, fields: string[], what: string): string;
66
+ export declare function stripInternal<T>(item: Record<string, unknown> | undefined): T | undefined;
67
+ export declare function encodeCursor(key: Record<string, unknown> | undefined): string | undefined;
68
+ export declare function decodeCursor(cursor: string | undefined): Record<string, unknown> | undefined;
69
+ /** Pure key/attribute mapping, exported for tests and the console's data browser. */
70
+ export declare function createKeyMapper<T extends Record<string, unknown>>(name: string, opts: CollectionOptions<T>): {
71
+ name: string;
72
+ partition: string[];
73
+ sort: string[];
74
+ indexes: {
75
+ name: string;
76
+ slot: 1 | 2 | 3;
77
+ partition: string[];
78
+ sort: string[];
79
+ }[];
80
+ primaryKey: (item: Record<string, unknown>) => {
81
+ pk: string;
82
+ sk: string;
83
+ };
84
+ derived: (item: Record<string, unknown>) => Record<string, string | number | null>;
85
+ toItem: (item: T) => Record<string, unknown>;
86
+ queryInput: (values: Partial<T>, q: QueryOptions, tableName: string) => QueryCommandInput;
87
+ keyFields: Set<string>;
88
+ derivedSources: Set<string>;
89
+ };
90
+ /** `SET #a = :a, ... REMOVE #b` from a patch; `undefined`/`null` values are removed. */
91
+ export declare function setExpression(patch: Record<string, unknown>, skip: Set<string>): {
92
+ expr: string;
93
+ names: Record<string, string>;
94
+ values: Record<string, unknown>;
95
+ };
96
+ export declare const db: Db;
97
+ export {};
package/dist/db.js ADDED
@@ -0,0 +1,297 @@
1
+ import { DeleteCommand, GetCommand, PutCommand, QueryCommand, UpdateCommand, } from '@aws-sdk/lib-dynamodb';
2
+ import { getBindings } from './bindings.js';
3
+ import { ddbClient } from './clients.js';
4
+ import { span } from './telemetry.js';
5
+ export const INTERNAL_ATTRIBUTES = [
6
+ 'pk',
7
+ 'sk',
8
+ 'gsi1pk',
9
+ 'gsi1sk',
10
+ 'gsi2pk',
11
+ 'gsi2sk',
12
+ 'gsi3pk',
13
+ 'gsi3sk',
14
+ '_c',
15
+ '_ttl',
16
+ ];
17
+ const COLLECTION_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
18
+ function list(f) {
19
+ if (!f)
20
+ return [];
21
+ return typeof f === 'string' ? [f] : [...f];
22
+ }
23
+ function table() {
24
+ const t = getBindings().table;
25
+ if (!t)
26
+ throw new Error('This app has no database. Add `"db": true` to platform.json.');
27
+ return t;
28
+ }
29
+ function ddb() {
30
+ return ddbClient();
31
+ }
32
+ /** Values for `fields` from `item`, joined by `#`. Throws when any is missing. */
33
+ export function keyPart(item, fields, what) {
34
+ return fields
35
+ .map((f) => {
36
+ const v = item[f];
37
+ if (v === undefined || v === null || v === '')
38
+ throw new Error(`${what}: missing key attribute "${f}"`);
39
+ if (typeof v !== 'string' && typeof v !== 'number')
40
+ throw new Error(`${what}: key attribute "${f}" must be a string or number`);
41
+ return String(v);
42
+ })
43
+ .join('#');
44
+ }
45
+ function hasAll(item, fields) {
46
+ return fields.every((f) => item[f] !== undefined && item[f] !== null && item[f] !== '');
47
+ }
48
+ export function stripInternal(item) {
49
+ if (!item)
50
+ return undefined;
51
+ const out = {};
52
+ for (const [k, v] of Object.entries(item))
53
+ if (!INTERNAL_ATTRIBUTES.includes(k))
54
+ out[k] = v;
55
+ return out;
56
+ }
57
+ export function encodeCursor(key) {
58
+ return key ? Buffer.from(JSON.stringify(key)).toString('base64url') : undefined;
59
+ }
60
+ export function decodeCursor(cursor) {
61
+ if (!cursor)
62
+ return undefined;
63
+ try {
64
+ return JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8'));
65
+ }
66
+ catch {
67
+ throw new Error('Invalid cursor');
68
+ }
69
+ }
70
+ /** Pure key/attribute mapping, exported for tests and the console's data browser. */
71
+ export function createKeyMapper(name, opts) {
72
+ if (!COLLECTION_NAME.test(name))
73
+ throw new Error(`Invalid collection name "${name}"`);
74
+ const partition = list(opts.partition);
75
+ const sort = list(opts.sort);
76
+ if (partition.length === 0)
77
+ throw new Error(`Collection "${name}" needs a partition key`);
78
+ const indexes = Object.entries(opts.indexes ?? {}).map(([indexName, ix]) => ({
79
+ name: indexName,
80
+ slot: ix.slot,
81
+ partition: list(ix.partition),
82
+ sort: list(ix.sort),
83
+ }));
84
+ const slots = new Set();
85
+ for (const ix of indexes) {
86
+ if (slots.has(ix.slot))
87
+ throw new Error(`Collection "${name}": two indexes use slot ${ix.slot}`);
88
+ slots.add(ix.slot);
89
+ }
90
+ const what = `collection "${name}"`;
91
+ const primaryKey = (item) => ({
92
+ pk: `${name}#${keyPart(item, partition, what)}`,
93
+ sk: sort.length ? keyPart(item, sort, what) : '#',
94
+ });
95
+ /** gsi/ttl attributes for `item`; `null` values mean "remove". */
96
+ const derived = (item) => {
97
+ const out = {};
98
+ for (const ix of indexes) {
99
+ if (hasAll(item, ix.partition) && (ix.sort.length === 0 || hasAll(item, ix.sort))) {
100
+ out[`gsi${ix.slot}pk`] = `${name}#${ix.name}#${keyPart(item, ix.partition, what)}`;
101
+ out[`gsi${ix.slot}sk`] = ix.sort.length ? keyPart(item, ix.sort, what) : '#';
102
+ }
103
+ else {
104
+ out[`gsi${ix.slot}pk`] = null;
105
+ out[`gsi${ix.slot}sk`] = null;
106
+ }
107
+ }
108
+ if (opts.ttl) {
109
+ const v = item[opts.ttl];
110
+ out._ttl = typeof v === 'number' ? Math.floor(v) : null;
111
+ }
112
+ return out;
113
+ };
114
+ const toItem = (item) => {
115
+ const extra = {};
116
+ for (const [k, v] of Object.entries(derived(item)))
117
+ if (v !== null)
118
+ extra[k] = v;
119
+ return { ...item, ...primaryKey(item), _c: name, ...extra };
120
+ };
121
+ const queryInput = (values, q, tableName) => {
122
+ let pkAttr = 'pk';
123
+ let skAttr = 'sk';
124
+ let pkValue;
125
+ let indexName;
126
+ if (q.index) {
127
+ const ix = indexes.find((i) => i.name === q.index);
128
+ if (!ix)
129
+ throw new Error(`${what} has no index "${q.index}"`);
130
+ pkAttr = `gsi${ix.slot}pk`;
131
+ skAttr = `gsi${ix.slot}sk`;
132
+ indexName = `gsi${ix.slot}`;
133
+ pkValue = `${name}#${ix.name}#${keyPart(values, ix.partition, what)}`;
134
+ }
135
+ else {
136
+ pkValue = `${name}#${keyPart(values, partition, what)}`;
137
+ }
138
+ const names = { '#pk': pkAttr };
139
+ const vals = { ':pk': pkValue };
140
+ let cond = '#pk = :pk';
141
+ const sortCond = (expr) => {
142
+ names['#sk'] = skAttr;
143
+ cond += ` AND ${expr}`;
144
+ };
145
+ if (q.beginsWith !== undefined) {
146
+ vals[':b'] = String(q.beginsWith);
147
+ sortCond('begins_with(#sk, :b)');
148
+ }
149
+ else if (q.between) {
150
+ vals[':lo'] = String(q.between[0]);
151
+ vals[':hi'] = String(q.between[1]);
152
+ sortCond('#sk BETWEEN :lo AND :hi');
153
+ }
154
+ else {
155
+ const ops = [
156
+ ['gt', '>'],
157
+ ['gte', '>='],
158
+ ['lt', '<'],
159
+ ['lte', '<='],
160
+ ];
161
+ const set = ops.filter(([k]) => q[k] !== undefined);
162
+ if (set.length > 1)
163
+ throw new Error('Use `between` for a range; only one of gt/gte/lt/lte is allowed');
164
+ const op = set[0];
165
+ if (op) {
166
+ vals[':v'] = String(q[op[0]]);
167
+ sortCond(`#sk ${op[1]} :v`);
168
+ }
169
+ }
170
+ const start = decodeCursor(q.cursor);
171
+ return {
172
+ TableName: tableName,
173
+ ...(indexName ? { IndexName: indexName } : {}),
174
+ KeyConditionExpression: cond,
175
+ ExpressionAttributeNames: names,
176
+ ExpressionAttributeValues: vals,
177
+ ScanIndexForward: !q.reverse,
178
+ ...(q.limit ? { Limit: q.limit } : {}),
179
+ ...(start ? { ExclusiveStartKey: start } : {}),
180
+ };
181
+ };
182
+ const keyFields = new Set([...partition, ...sort]);
183
+ const derivedSources = new Set([
184
+ ...indexes.flatMap((i) => [...i.partition, ...i.sort]),
185
+ ...(opts.ttl ? [opts.ttl] : []),
186
+ ]);
187
+ return {
188
+ name,
189
+ partition,
190
+ sort,
191
+ indexes,
192
+ primaryKey,
193
+ derived,
194
+ toItem,
195
+ queryInput,
196
+ keyFields,
197
+ derivedSources,
198
+ };
199
+ }
200
+ function collection(name, opts) {
201
+ const m = createKeyMapper(name, opts);
202
+ return {
203
+ get: (key) => span('db.get', name, async () => {
204
+ const out = await ddb().send(new GetCommand({ TableName: table(), Key: m.primaryKey(key) }));
205
+ return stripInternal(out.Item);
206
+ }),
207
+ put: (item, putOpts) => span('db.put', name, async () => {
208
+ await ddb().send(new PutCommand({
209
+ TableName: table(),
210
+ Item: m.toItem(item),
211
+ ...(putOpts?.ifNotExists ? { ConditionExpression: 'attribute_not_exists(pk)' } : {}),
212
+ }));
213
+ return item;
214
+ }),
215
+ update: (key, patch) => span('db.update', name, async () => {
216
+ for (const k of Object.keys(patch)) {
217
+ if (m.keyFields.has(k) && patch[k] !== key[k])
218
+ throw new Error(`collection "${name}": cannot change key attribute "${k}"`);
219
+ }
220
+ const tableName = table();
221
+ const Key = m.primaryKey(key);
222
+ const { expr, names, values } = setExpression(patch, m.keyFields);
223
+ const out = await ddb().send(new UpdateCommand({
224
+ TableName: tableName,
225
+ Key,
226
+ UpdateExpression: expr,
227
+ ExpressionAttributeNames: { ...names, '#pk': 'pk' },
228
+ ...(Object.keys(values).length ? { ExpressionAttributeValues: values } : {}),
229
+ ConditionExpression: 'attribute_exists(#pk)',
230
+ ReturnValues: 'ALL_NEW',
231
+ }));
232
+ let item = out.Attributes ?? {};
233
+ if (Object.keys(patch).some((k) => m.derivedSources.has(k))) {
234
+ const d = setExpression(m.derived(item), new Set());
235
+ const again = await ddb().send(new UpdateCommand({
236
+ TableName: tableName,
237
+ Key,
238
+ UpdateExpression: d.expr,
239
+ ExpressionAttributeNames: { ...d.names, '#pk': 'pk' },
240
+ ...(Object.keys(d.values).length ? { ExpressionAttributeValues: d.values } : {}),
241
+ ConditionExpression: 'attribute_exists(#pk)',
242
+ ReturnValues: 'ALL_NEW',
243
+ }));
244
+ item = again.Attributes ?? item;
245
+ }
246
+ return stripInternal(item);
247
+ }),
248
+ delete: (key) => span('db.delete', name, async () => {
249
+ await ddb().send(new DeleteCommand({ TableName: table(), Key: m.primaryKey(key) }));
250
+ }),
251
+ query: (values, q = {}) => span('db.query', name, async () => {
252
+ const out = await ddb().send(new QueryCommand(m.queryInput(values, q, table())));
253
+ const page = { items: (out.Items ?? []).map((i) => stripInternal(i)) };
254
+ const cursor = encodeCursor(out.LastEvaluatedKey);
255
+ if (cursor)
256
+ page.cursor = cursor;
257
+ return page;
258
+ }),
259
+ };
260
+ }
261
+ /** `SET #a = :a, ... REMOVE #b` from a patch; `undefined`/`null` values are removed. */
262
+ export function setExpression(patch, skip) {
263
+ const sets = [];
264
+ const removes = [];
265
+ const names = {};
266
+ const values = {};
267
+ let i = 0;
268
+ for (const [k, v] of Object.entries(patch)) {
269
+ if (skip.has(k))
270
+ continue;
271
+ const n = `#f${i}`;
272
+ names[n] = k;
273
+ if (v === undefined || v === null)
274
+ removes.push(n);
275
+ else {
276
+ values[`:v${i}`] = v;
277
+ sets.push(`${n} = :v${i}`);
278
+ }
279
+ i++;
280
+ }
281
+ const parts = [];
282
+ if (sets.length)
283
+ parts.push(`SET ${sets.join(', ')}`);
284
+ if (removes.length)
285
+ parts.push(`REMOVE ${removes.join(', ')}`);
286
+ if (!parts.length) {
287
+ // No-op update still verifies existence.
288
+ names['#c'] = '_c';
289
+ parts.push('SET #c = #c');
290
+ }
291
+ return { expr: parts.join(' '), names, values };
292
+ }
293
+ export const db = {
294
+ collection,
295
+ raw: () => ({ client: ddb(), table: table() }),
296
+ };
297
+ //# sourceMappingURL=db.js.map
package/dist/db.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface Events {
2
+ publish<T = unknown>(type: string, detail: T): Promise<void>;
3
+ }
4
+ /** Envelope inside EventBridge `detail`, so the trace survives the hop. */
5
+ export interface EventEnvelope<T = unknown> {
6
+ data: T;
7
+ trace: string;
8
+ }
9
+ export declare const events: Events;
package/dist/events.js ADDED
@@ -0,0 +1,28 @@
1
+ import { getBindings } from './bindings.js';
2
+ import { client } from './clients.js';
3
+ import { currentTrace } from './context.js';
4
+ import { span } from './telemetry.js';
5
+ export const events = {
6
+ publish: (type, detail) => span('events.publish', type, async () => {
7
+ const b = getBindings();
8
+ if (!b.bus)
9
+ throw new Error('This app has no event bus. Add an "events" entry to platform.json.');
10
+ const envelope = { data: detail, trace: currentTrace() };
11
+ const { PutEventsCommand } = await import('@aws-sdk/client-eventbridge');
12
+ const out = await (await client('events')).send(new PutEventsCommand({
13
+ Entries: [
14
+ {
15
+ EventBusName: b.bus.name,
16
+ Source: `platform.${b.app}`,
17
+ DetailType: type,
18
+ Detail: JSON.stringify(envelope),
19
+ },
20
+ ],
21
+ }));
22
+ if (out.FailedEntryCount) {
23
+ const e = out.Entries?.find((x) => x.ErrorCode);
24
+ throw new Error(`events.publish("${type}") failed: ${e?.ErrorCode ?? 'unknown'} ${e?.ErrorMessage ?? ''}`.trim());
25
+ }
26
+ }),
27
+ };
28
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAYtC,MAAM,CAAC,MAAM,MAAM,GAAW;IAC5B,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CACxB,IAAI,CAAC,gBAAgB,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,CAAC,GAAG,WAAW,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;QAClG,MAAM,QAAQ,GAAkB,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,CAAC;QACxE,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;QACzE,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAC7C,IAAI,gBAAgB,CAAC;YACnB,OAAO,EAAE;gBACP;oBACE,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI;oBACxB,MAAM,EAAE,YAAY,CAAC,CAAC,GAAG,EAAE;oBAC3B,UAAU,EAAE,IAAI;oBAChB,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;iBACjC;aACF;SACF,CAAC,CACH,CAAC;QACF,IAAI,GAAG,CAAC,gBAAgB,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAChD,MAAM,IAAI,KAAK,CACb,mBAAmB,IAAI,cAAc,CAAC,EAAE,SAAS,IAAI,SAAS,IAAI,CAAC,EAAE,YAAY,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CACjG,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;CACL,CAAC"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { type IncomingMessage, type Server, type ServerResponse } from 'node:http';
2
+ /**
3
+ * Minimal Node `http` ⇄ web `fetch` adapter (no framework dependency).
4
+ * Streams request and response bodies, preserves multiple Set-Cookie headers.
5
+ */
6
+ export type FetchFn = (request: Request) => Promise<Response>;
7
+ export declare function toRequest(req: IncomingMessage, signal?: AbortSignal): Request;
8
+ export declare function writeResponse(res: ServerResponse, response: Response): Promise<void>;
9
+ export declare function createNodeServer(fetchFn: FetchFn): Server;
10
+ /** Parse `host:port` (IPv6 in brackets allowed). */
11
+ export declare function parseEndpoint(endpoint: string): {
12
+ host: string;
13
+ port: number;
14
+ };