@ericminassian/plat 0.0.0-stage → 0.2.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 (160) hide show
  1. package/dist/api-types.d.ts +7 -0
  2. package/dist/api-types.js +3 -0
  3. package/dist/api-types.js.map +1 -0
  4. package/dist/api.d.ts +123 -0
  5. package/dist/api.js +271 -0
  6. package/dist/api.js.map +1 -0
  7. package/dist/bin.d.ts +2 -0
  8. package/dist/bin.js +4 -0
  9. package/dist/bin.js.map +1 -0
  10. package/dist/build.d.ts +48 -0
  11. package/dist/build.js +185 -0
  12. package/dist/build.js.map +1 -0
  13. package/dist/commands/account.d.ts +2 -0
  14. package/dist/commands/account.js +45 -0
  15. package/dist/commands/account.js.map +1 -0
  16. package/dist/commands/apps.d.ts +2 -0
  17. package/dist/commands/apps.js +75 -0
  18. package/dist/commands/apps.js.map +1 -0
  19. package/dist/commands/common.d.ts +19 -0
  20. package/dist/commands/common.js +43 -0
  21. package/dist/commands/common.js.map +1 -0
  22. package/dist/commands/lifecycle.d.ts +3 -0
  23. package/dist/commands/lifecycle.js +106 -0
  24. package/dist/commands/lifecycle.js.map +1 -0
  25. package/dist/commands/manage.d.ts +2 -0
  26. package/dist/commands/manage.js +103 -0
  27. package/dist/commands/manage.js.map +1 -0
  28. package/dist/commands/observe.d.ts +8 -0
  29. package/dist/commands/observe.js +195 -0
  30. package/dist/commands/observe.js.map +1 -0
  31. package/dist/credentials.d.ts +13 -0
  32. package/dist/credentials.js +42 -0
  33. package/dist/credentials.js.map +1 -0
  34. package/dist/deploy.d.ts +23 -0
  35. package/dist/deploy.js +100 -0
  36. package/dist/deploy.js.map +1 -0
  37. package/dist/dev/crons.d.ts +16 -0
  38. package/dist/dev/crons.js +47 -0
  39. package/dist/dev/crons.js.map +1 -0
  40. package/dist/dev/entries.d.ts +6 -0
  41. package/dist/dev/entries.js +21 -0
  42. package/dist/dev/entries.js.map +1 -0
  43. package/dist/dev/index.d.ts +33 -0
  44. package/dist/dev/index.js +262 -0
  45. package/dist/dev/index.js.map +1 -0
  46. package/dist/dev/local-stage.d.ts +46 -0
  47. package/dist/dev/local-stage.js +116 -0
  48. package/dist/dev/local-stage.js.map +1 -0
  49. package/dist/dev/pretty.d.ts +16 -0
  50. package/dist/dev/pretty.js +91 -0
  51. package/dist/dev/pretty.js.map +1 -0
  52. package/dist/dev/proc.d.ts +7 -0
  53. package/dist/dev/proc.js +16 -0
  54. package/dist/dev/proc.js.map +1 -0
  55. package/dist/dev/router.d.ts +67 -0
  56. package/dist/dev/router.js +184 -0
  57. package/dist/dev/router.js.map +1 -0
  58. package/dist/dev/server-entry.d.ts +1 -0
  59. package/dist/dev/server-entry.js +28 -0
  60. package/dist/dev/server-entry.js.map +1 -0
  61. package/dist/dev/test.d.ts +10 -0
  62. package/dist/dev/test.js +62 -0
  63. package/dist/dev/test.js.map +1 -0
  64. package/dist/dev/worker-host.d.ts +1 -0
  65. package/dist/dev/worker-host.js +67 -0
  66. package/dist/dev/worker-host.js.map +1 -0
  67. package/dist/dev/worker.d.ts +57 -0
  68. package/dist/dev/worker.js +216 -0
  69. package/dist/dev/worker.js.map +1 -0
  70. package/dist/github.d.ts +39 -0
  71. package/dist/github.js +108 -0
  72. package/dist/github.js.map +1 -0
  73. package/dist/index.d.ts +14 -0
  74. package/dist/index.js +15 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/init.d.ts +30 -0
  77. package/dist/init.js +114 -0
  78. package/dist/init.js.map +1 -0
  79. package/dist/main.d.ts +3 -0
  80. package/dist/main.js +24 -0
  81. package/dist/main.js.map +1 -0
  82. package/dist/project.d.ts +21 -0
  83. package/dist/project.js +138 -0
  84. package/dist/project.js.map +1 -0
  85. package/dist/util/esbuild.d.ts +2 -0
  86. package/dist/util/esbuild.js +9 -0
  87. package/dist/util/esbuild.js.map +1 -0
  88. package/dist/util/net.d.ts +4 -0
  89. package/dist/util/net.js +33 -0
  90. package/dist/util/net.js.map +1 -0
  91. package/dist/util/output.d.ts +42 -0
  92. package/dist/util/output.js +105 -0
  93. package/dist/util/output.js.map +1 -0
  94. package/dist/util/prompt.d.ts +3 -0
  95. package/dist/util/prompt.js +22 -0
  96. package/dist/util/prompt.js.map +1 -0
  97. package/dist/util/time.d.ts +11 -0
  98. package/dist/util/time.js +29 -0
  99. package/dist/util/time.js.map +1 -0
  100. package/dist/util/zip.d.ts +10 -0
  101. package/dist/util/zip.js +45 -0
  102. package/dist/util/zip.js.map +1 -0
  103. package/package.json +50 -3
  104. package/src/api-types.ts +8 -0
  105. package/src/api.ts +341 -0
  106. package/src/bin.ts +4 -0
  107. package/src/build.ts +252 -0
  108. package/src/commands/account.ts +62 -0
  109. package/src/commands/apps.ts +102 -0
  110. package/src/commands/common.ts +51 -0
  111. package/src/commands/lifecycle.ts +140 -0
  112. package/src/commands/manage.ts +169 -0
  113. package/src/commands/observe.ts +276 -0
  114. package/src/credentials.ts +50 -0
  115. package/src/deploy.ts +138 -0
  116. package/src/dev/crons.ts +70 -0
  117. package/src/dev/entries.ts +23 -0
  118. package/src/dev/index.ts +281 -0
  119. package/src/dev/local-stage.ts +176 -0
  120. package/src/dev/pretty.ts +90 -0
  121. package/src/dev/proc.ts +15 -0
  122. package/src/dev/router.ts +246 -0
  123. package/src/dev/server-entry.ts +21 -0
  124. package/src/dev/test.ts +68 -0
  125. package/src/dev/worker-host.ts +62 -0
  126. package/src/dev/worker.ts +332 -0
  127. package/src/github.ts +121 -0
  128. package/src/index.ts +20 -0
  129. package/src/init.ts +136 -0
  130. package/src/main.ts +28 -0
  131. package/src/project.ts +143 -0
  132. package/src/util/esbuild.ts +10 -0
  133. package/src/util/net.ts +33 -0
  134. package/src/util/output.ts +117 -0
  135. package/src/util/prompt.ts +20 -0
  136. package/src/util/time.ts +32 -0
  137. package/src/util/zip.ts +46 -0
  138. package/template/.github/workflows/platform.yml +24 -0
  139. package/template/AGENTS.md +139 -0
  140. package/template/CLAUDE.md +1 -0
  141. package/template/README.md +13 -0
  142. package/template/_gitignore +8 -0
  143. package/template/package.json +31 -0
  144. package/template/plat.config.ts +31 -0
  145. package/template/src/server.ts +100 -0
  146. package/template/src/types.ts +21 -0
  147. package/template/src/worker.ts +32 -0
  148. package/template/test/server.test.ts +120 -0
  149. package/template/test/todos.integration.test.ts +30 -0
  150. package/template/tsconfig.json +18 -0
  151. package/template/vitest.config.ts +9 -0
  152. package/template/vitest.integration.config.ts +10 -0
  153. package/template/web/index.html +12 -0
  154. package/template/web/src/App.tsx +102 -0
  155. package/template/web/src/api.ts +11 -0
  156. package/template/web/src/main.tsx +10 -0
  157. package/template/web/src/styles.css +102 -0
  158. package/template/web/tsconfig.json +18 -0
  159. package/template/web/vite.config.ts +19 -0
  160. package/README.md +0 -3
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Dev children are spawned `detached` so each leads its own process group:
3
+ * `shell: true` commands (`pnpm --dir web dev` → pnpm → vite) and `tsx watch`
4
+ * fork grandchildren that a plain `child.kill()` leaves running.
5
+ */
6
+ export function killTree(child, signal = 'SIGTERM') {
7
+ if (child.exitCode !== null || child.signalCode !== null || !child.pid)
8
+ return;
9
+ try {
10
+ process.kill(-child.pid, signal);
11
+ }
12
+ catch {
13
+ child.kill(signal);
14
+ }
15
+ }
16
+ //# sourceMappingURL=proc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proc.js","sourceRoot":"","sources":["../../src/dev/proc.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAmB,EAAE,MAAM,GAAmB,SAAS;IAC9E,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG;QAAE,OAAO;IAC/E,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrB,CAAC;AACH,CAAC"}
@@ -0,0 +1,67 @@
1
+ import { type IncomingMessage, type Server, type ServerResponse } from 'node:http';
2
+ import { type RouteRecord } from '@ericminassian/platform-core';
3
+ /**
4
+ * `plat dev`'s stand-in for the CloudFront router: the same `decide()` the
5
+ * edge function runs, against a local route record. Server routes go to the
6
+ * local web server; static routes go to the frontend dev server (original URL,
7
+ * so it can do HMR and its own SPA fallback) or to files in `web.static`.
8
+ */
9
+ export interface Target {
10
+ host: string;
11
+ port: number;
12
+ }
13
+ export interface LocalRouterState {
14
+ record: RouteRecord;
15
+ server?: Target;
16
+ webDev?: Target;
17
+ /** A frontend dev server is configured but not accepting connections yet. */
18
+ webDevStarting?: boolean;
19
+ staticDir?: string;
20
+ }
21
+ export type LocalDecision = {
22
+ kind: 'server';
23
+ target: Target;
24
+ } | {
25
+ kind: 'webdev';
26
+ target: Target;
27
+ } | {
28
+ kind: 'file';
29
+ path: string;
30
+ } | {
31
+ kind: 'unavailable';
32
+ reason: string;
33
+ } | {
34
+ kind: 'notfound';
35
+ };
36
+ /** Pure routing decision, shared by the router and tests. */
37
+ export declare function localDecision(state: LocalRouterState, uri: string): LocalDecision;
38
+ /** Local route record for an app (mirrors what the engine writes to the KVS). */
39
+ export declare function localRouteRecord(opts: {
40
+ app: string;
41
+ stage: string;
42
+ deploymentId: string;
43
+ hasServer: boolean;
44
+ hasStatic: boolean;
45
+ serverRoutes: string[];
46
+ spa: boolean;
47
+ }): RouteRecord;
48
+ export type Interceptor = (req: IncomingMessage, res: ServerResponse, url: URL) => Promise<boolean> | boolean;
49
+ export interface LocalRouterOptions {
50
+ port: number;
51
+ host?: string;
52
+ state: () => LocalRouterState;
53
+ /** Handled before routing (local sign-in, dev endpoints). Return true when handled. */
54
+ intercept?: Interceptor[];
55
+ onRequest?: (line: {
56
+ method: string;
57
+ url: string;
58
+ status: number;
59
+ ms: number;
60
+ kind: string;
61
+ }) => void;
62
+ }
63
+ export declare function startLocalRouter(opts: LocalRouterOptions): Promise<Server>;
64
+ /** Headers the edge would add, so apps see their public host. */
65
+ export declare function forwardedHeaders(req: IncomingMessage, toServer: boolean): Record<string, string | string[] | undefined>;
66
+ /** Join without escaping `root` (path traversal). */
67
+ export declare function safeJoin(root: string, rel: string): string | null;
@@ -0,0 +1,184 @@
1
+ import { createReadStream, existsSync, statSync } from 'node:fs';
2
+ import { createServer, request as httpRequest, } from 'node:http';
3
+ import { connect } from 'node:net';
4
+ import { extname, join, normalize, sep } from 'node:path';
5
+ import { decide, HOST_HEADER } from '@ericminassian/platform-core';
6
+ /** Pure routing decision, shared by the router and tests. */
7
+ export function localDecision(state, uri) {
8
+ const pathname = uri.split('?')[0] ?? '/';
9
+ const d = decide(state.record, pathname);
10
+ if (d.t === 'notfound')
11
+ return { kind: 'notfound' };
12
+ if (d.t === 'server') {
13
+ return state.server
14
+ ? { kind: 'server', target: state.server }
15
+ : { kind: 'unavailable', reason: 'the web server is starting' };
16
+ }
17
+ if (state.webDev)
18
+ return { kind: 'webdev', target: state.webDev };
19
+ if (state.webDevStarting)
20
+ return { kind: 'unavailable', reason: 'the frontend dev server is starting' };
21
+ if (!state.staticDir)
22
+ return { kind: 'unavailable', reason: 'no frontend: set web.static or web.dev in plat.config.ts' };
23
+ const prefix = `/${state.record.s ?? ''}`;
24
+ const rel = d.key.startsWith(prefix) ? d.key.slice(prefix.length) : d.key;
25
+ const file = safeJoin(state.staticDir, decodeURIComponent(rel));
26
+ return file ? { kind: 'file', path: file } : { kind: 'notfound' };
27
+ }
28
+ /** Local route record for an app (mirrors what the engine writes to the KVS). */
29
+ export function localRouteRecord(opts) {
30
+ return {
31
+ v: 1,
32
+ app: opts.app,
33
+ stage: opts.stage,
34
+ d: opts.deploymentId,
35
+ ...(opts.hasServer ? { o: 'local-server' } : {}),
36
+ ...(opts.hasStatic ? { s: 'local' } : {}),
37
+ ...(opts.hasServer && opts.hasStatic ? { r: opts.serverRoutes } : {}),
38
+ spa: opts.spa ? 1 : 0,
39
+ };
40
+ }
41
+ export function startLocalRouter(opts) {
42
+ const server = createServer(async (req, res) => {
43
+ const started = Date.now();
44
+ const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
45
+ let kind = 'intercept';
46
+ res.once('finish', () => opts.onRequest?.({
47
+ method: req.method ?? 'GET',
48
+ url: req.url ?? '/',
49
+ status: res.statusCode,
50
+ ms: Date.now() - started,
51
+ kind,
52
+ }));
53
+ try {
54
+ for (const fn of opts.intercept ?? [])
55
+ if (await fn(req, res, url))
56
+ return;
57
+ const d = localDecision(opts.state(), req.url ?? '/');
58
+ kind = d.kind;
59
+ switch (d.kind) {
60
+ case 'server':
61
+ return proxy(req, res, d.target, true);
62
+ case 'webdev':
63
+ return proxy(req, res, d.target, false);
64
+ case 'file':
65
+ return serveFile(res, d.path);
66
+ case 'unavailable':
67
+ return text(res, 503, `${d.reason}\n`);
68
+ case 'notfound':
69
+ return text(res, 404, 'Not found\n');
70
+ }
71
+ }
72
+ catch (err) {
73
+ if (!res.headersSent)
74
+ text(res, 502, `plat dev router error: ${err.message}\n`);
75
+ else
76
+ res.destroy();
77
+ }
78
+ });
79
+ server.on('upgrade', (req, socket, head) => {
80
+ const d = localDecision(opts.state(), req.url ?? '/');
81
+ // Web Functions don't do WebSockets; only the frontend dev server (HMR) gets upgrades.
82
+ if (d.kind !== 'webdev') {
83
+ socket.end('HTTP/1.1 501 Not Implemented\r\n\r\n');
84
+ return;
85
+ }
86
+ tunnel(req, socket, head, d.target);
87
+ });
88
+ return new Promise((resolve, reject) => {
89
+ server.once('error', reject);
90
+ server.listen(opts.port, opts.host ?? '127.0.0.1', () => resolve(server));
91
+ });
92
+ }
93
+ /** Headers the edge would add, so apps see their public host. */
94
+ export function forwardedHeaders(req, toServer) {
95
+ const headers = { ...req.headers };
96
+ const host = req.headers.host ?? 'localhost';
97
+ if (toServer) {
98
+ headers[HOST_HEADER] = host;
99
+ headers['x-forwarded-host'] = host;
100
+ headers['x-forwarded-proto'] = 'http';
101
+ }
102
+ return headers;
103
+ }
104
+ function proxy(req, res, target, toServer) {
105
+ return new Promise((resolve) => {
106
+ const upstream = httpRequest({
107
+ host: target.host,
108
+ port: target.port,
109
+ method: req.method,
110
+ path: req.url,
111
+ headers: forwardedHeaders(req, toServer),
112
+ }, (up) => {
113
+ res.writeHead(up.statusCode ?? 502, up.statusMessage, up.headers);
114
+ up.pipe(res);
115
+ up.once('end', resolve);
116
+ });
117
+ upstream.once('error', (err) => {
118
+ if (!res.headersSent)
119
+ text(res, 502, `upstream ${target.host}:${target.port} unavailable: ${err.message}\n`);
120
+ resolve();
121
+ });
122
+ req.pipe(upstream);
123
+ });
124
+ }
125
+ function tunnel(req, socket, head, target) {
126
+ const up = connect(target.port, target.host, () => {
127
+ const lines = [`${req.method} ${req.url} HTTP/${req.httpVersion}`];
128
+ for (let i = 0; i < req.rawHeaders.length; i += 2)
129
+ lines.push(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}`);
130
+ up.write(`${lines.join('\r\n')}\r\n\r\n`);
131
+ if (head.length)
132
+ up.write(head);
133
+ up.pipe(socket);
134
+ socket.pipe(up);
135
+ });
136
+ const close = () => {
137
+ up.destroy();
138
+ socket.destroy();
139
+ };
140
+ up.once('error', close);
141
+ socket.once('error', close);
142
+ }
143
+ const MIME = {
144
+ '.html': 'text/html; charset=utf-8',
145
+ '.js': 'text/javascript; charset=utf-8',
146
+ '.mjs': 'text/javascript; charset=utf-8',
147
+ '.css': 'text/css; charset=utf-8',
148
+ '.json': 'application/json',
149
+ '.map': 'application/json',
150
+ '.svg': 'image/svg+xml',
151
+ '.png': 'image/png',
152
+ '.jpg': 'image/jpeg',
153
+ '.jpeg': 'image/jpeg',
154
+ '.gif': 'image/gif',
155
+ '.webp': 'image/webp',
156
+ '.ico': 'image/x-icon',
157
+ '.woff': 'font/woff',
158
+ '.woff2': 'font/woff2',
159
+ '.txt': 'text/plain; charset=utf-8',
160
+ '.wasm': 'application/wasm',
161
+ '.webmanifest': 'application/manifest+json',
162
+ };
163
+ function serveFile(res, path) {
164
+ if (!existsSync(path) || !statSync(path).isFile()) {
165
+ text(res, 404, 'Not found\n');
166
+ return;
167
+ }
168
+ res.writeHead(200, {
169
+ 'content-type': MIME[extname(path)] ?? 'application/octet-stream',
170
+ 'cache-control': 'no-cache',
171
+ });
172
+ createReadStream(path).pipe(res);
173
+ }
174
+ function text(res, status, body) {
175
+ res.writeHead(status, { 'content-type': 'text/plain; charset=utf-8' });
176
+ res.end(body);
177
+ }
178
+ /** Join without escaping `root` (path traversal). */
179
+ export function safeJoin(root, rel) {
180
+ const full = normalize(join(root, rel));
181
+ const base = normalize(root.endsWith(sep) ? root : root + sep);
182
+ return full === normalize(root) || full.startsWith(base) ? full : null;
183
+ }
184
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/dev/router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ // Entry for the local web server under `plat dev` (run with `tsx watch`, so
10
+ // edits to the app restart it). Loads the app's fetch handler and starts it
11
+ // with the SDK server wrapper — the same wrapper the Web Function runs — using
12
+ // the app's own copy of the SDK so app code and wrapper share state.
13
+ import { pathToFileURL } from 'node:url';
14
+ const entry = process.env.PLATFORM_ENTRY;
15
+ const sdkServer = process.env.PLATFORM_SDK_SERVER ?? '@ericminassian/platform/server';
16
+ if (!entry) {
17
+ console.error('PLATFORM_ENTRY is not set (this file is started by `plat dev`)');
18
+ process.exit(1);
19
+ }
20
+ const mod = (await import(__rewriteRelativeImportExtension(pathToFileURL(entry).href)));
21
+ if (!mod.default) {
22
+ console.error(`${entry} has no default export; export your app (e.g. \`export default app\`)`);
23
+ process.exit(1);
24
+ }
25
+ const sdkUrl = sdkServer.startsWith('/') ? pathToFileURL(sdkServer).href : sdkServer;
26
+ const { startServer } = (await import(__rewriteRelativeImportExtension(sdkUrl)));
27
+ await startServer(mod.default);
28
+ //# sourceMappingURL=server-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-entry.js","sourceRoot":"","sources":["../../src/dev/server-entry.ts"],"names":[],"mappings":";;;;;;;;AAAA,4EAA4E;AAC5E,4EAA4E;AAC5E,+EAA+E;AAC/E,qEAAqE;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;AACzC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,gCAAgC,CAAC;AACtF,IAAI,CAAC,KAAK,EAAE,CAAC;IACX,OAAO,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAC;IAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,kCAAC,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,EAAC,CAA0B,CAAC;AAC/E,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACjB,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,uEAAuE,CAAC,CAAC;IAC/F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AACD,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACrF,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,MAAM,kCAAC,MAAM,EAAC,CAAoD,CAAC;AAClG,MAAM,WAAW,CAAC,GAAG,CAAC,OAA4C,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export declare const INTEGRATION_CONFIG = "vitest.integration.config.ts";
2
+ /**
3
+ * `plat test`: a throwaway emulator with this app's resources (stage `test`),
4
+ * then vitest with the same environment the deployed code gets. Uses
5
+ * `vitest.integration.config.ts` when present.
6
+ */
7
+ export declare function runTests(opts: {
8
+ cwd: string;
9
+ args: string[];
10
+ }): Promise<number>;
@@ -0,0 +1,62 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { EMULATOR_INSTALL_HINT } from '@ericminassian/platform-engine';
5
+ import { withLocalBin } from '../build.js';
6
+ import { loadProject } from '../project.js';
7
+ import { CliError, step, success } from '../util/output.js';
8
+ import { localEnv, startLocalStage } from './local-stage.js';
9
+ import { startLocalWorker, startWorkerDispatch } from './worker.js';
10
+ export const INTEGRATION_CONFIG = 'vitest.integration.config.ts';
11
+ /**
12
+ * `plat test`: a throwaway emulator with this app's resources (stage `test`),
13
+ * then vitest with the same environment the deployed code gets. Uses
14
+ * `vitest.integration.config.ts` when present.
15
+ */
16
+ export async function runTests(opts) {
17
+ const { root, manifest } = await loadProject(opts.cwd);
18
+ step('starting the AWS Emulator');
19
+ let stage;
20
+ try {
21
+ stage = await startLocalStage({ root, manifest, stage: 'test', port: 3999 });
22
+ }
23
+ catch (err) {
24
+ if (err.message === EMULATOR_INSTALL_HINT)
25
+ throw new CliError('the AWS Emulator is not installed', { hint: EMULATOR_INSTALL_HINT });
26
+ throw err;
27
+ }
28
+ success(`provisioned ${manifest.name} (stage test)`);
29
+ let worker;
30
+ let dispatcher;
31
+ try {
32
+ if (manifest.worker) {
33
+ worker = await startLocalWorker(stage, manifest, root, { watch: false });
34
+ worker.child.stdout?.pipe(process.stdout);
35
+ worker.child.stderr?.pipe(process.stderr);
36
+ dispatcher = await startWorkerDispatch(stage, manifest, worker);
37
+ }
38
+ const args = ['run'];
39
+ if (existsSync(join(root, INTEGRATION_CONFIG)) && !opts.args.includes('--config'))
40
+ args.push('--config', INTEGRATION_CONFIG);
41
+ args.push(...opts.args);
42
+ return await new Promise((resolve, reject) => {
43
+ const child = spawn('vitest', args, {
44
+ cwd: root,
45
+ stdio: 'inherit',
46
+ shell: process.platform === 'win32',
47
+ env: withLocalBin(root, localEnv(stage, manifest, {
48
+ PLATFORM_TEST: '1',
49
+ ...(worker ? { PLATFORM_LOCAL_WORKER_URL: `http://127.0.0.1:${worker.port}/` } : {}),
50
+ })),
51
+ });
52
+ child.once('exit', (code) => resolve(code ?? 1));
53
+ child.once('error', (err) => reject(new CliError(`could not run vitest: ${err.message}`, { hint: 'add vitest to devDependencies' })));
54
+ });
55
+ }
56
+ finally {
57
+ dispatcher?.stop();
58
+ worker?.stop();
59
+ await stage.stop();
60
+ }
61
+ }
62
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../../src/dev/test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,67 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ // Local worker host for `plat dev` / `plat test`. The AWS Emulator's Lambda
10
+ // runtime is an embedded JS sandbox without node:http/https/net/async_hooks,
11
+ // so a worker that talks to AWS (through the SDK) can't run inside it. This
12
+ // process runs the worker in real Node instead: it builds the same handler the
13
+ // Lambda would (`createWorkerHandler(def)` from the app's SDK) and accepts
14
+ // Lambda-shaped events over a loopback HTTP endpoint. The CLI feeds it SQS
15
+ // messages, EventBridge events and crons from the emulator.
16
+ import { randomUUID } from 'node:crypto';
17
+ import { createServer } from 'node:http';
18
+ import { pathToFileURL } from 'node:url';
19
+ const entry = process.env.PLATFORM_WORKER_ENTRY;
20
+ const sdkWorker = process.env.PLATFORM_SDK_WORKER ?? '@ericminassian/platform/worker';
21
+ const port = Number(process.env.PLATFORM_WORKER_PORT);
22
+ const timeoutMs = Number(process.env.PLATFORM_WORKER_TIMEOUT_MS ?? 60_000);
23
+ const functionName = process.env.PLATFORM_WORKER_FUNCTION ?? 'local-worker';
24
+ if (!entry || !port) {
25
+ console.error('PLATFORM_WORKER_ENTRY / PLATFORM_WORKER_PORT not set (this file is started by `plat dev`)');
26
+ process.exit(1);
27
+ }
28
+ const mod = (await import(__rewriteRelativeImportExtension(pathToFileURL(entry).href)));
29
+ if (!mod.default) {
30
+ console.error(`${entry} has no default export; export your worker (\`export default defineWorker({...})\`)`);
31
+ process.exit(1);
32
+ }
33
+ const sdkUrl = sdkWorker.startsWith('/') ? pathToFileURL(sdkWorker).href : sdkWorker;
34
+ const { createWorkerHandler } = (await import(__rewriteRelativeImportExtension(sdkUrl)));
35
+ const handler = createWorkerHandler(mod.default);
36
+ createServer((req, res) => {
37
+ let body = '';
38
+ req.setEncoding('utf8');
39
+ req.on('data', (c) => (body += c));
40
+ req.on('end', async () => {
41
+ const started = Date.now();
42
+ const context = {
43
+ awsRequestId: randomUUID(),
44
+ functionName,
45
+ functionVersion: '$LATEST',
46
+ invokedFunctionArn: `arn:aws:lambda:${process.env.AWS_REGION ?? 'us-east-1'}:000000000000:function:${functionName}`,
47
+ memoryLimitInMB: '512',
48
+ logGroupName: `/aws/lambda/${functionName}`,
49
+ logStreamName: 'local',
50
+ callbackWaitsForEmptyEventLoop: false,
51
+ getRemainingTimeInMillis: () => Math.max(0, started + timeoutMs - Date.now()),
52
+ };
53
+ let reply;
54
+ try {
55
+ reply = { ok: true, result: (await handler(JSON.parse(body || '{}'), context)) ?? null };
56
+ }
57
+ catch (err) {
58
+ const e = err;
59
+ reply = {
60
+ ok: false,
61
+ error: { name: e?.name ?? 'Error', message: e?.message ?? String(err), stack: e?.stack },
62
+ };
63
+ }
64
+ res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(reply));
65
+ });
66
+ }).listen(port, '127.0.0.1');
67
+ //# sourceMappingURL=worker-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-host.js","sourceRoot":"","sources":["../../src/dev/worker-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import { type ChildProcess } from 'node:child_process';
2
+ import { type Message } from '@aws-sdk/client-sqs';
3
+ import { type ResolvedManifest } from '@ericminassian/platform-core';
4
+ import { type LocalStage } from './local-stage.ts';
5
+ /**
6
+ * Local worker: the app's worker in a real Node process (see worker-host.ts
7
+ * for why), fed by a dispatcher that does what Lambda's event sources would:
8
+ * polls each SQS queue (batches, partial failures, retries via visibility
9
+ * timeout, DLQ via the queue's redrive policy), receives EventBridge events
10
+ * through a tap queue, and runs crons on demand.
11
+ */
12
+ export interface InvokeResult {
13
+ ok: boolean;
14
+ result?: unknown;
15
+ error?: {
16
+ name: string;
17
+ message: string;
18
+ stack?: string;
19
+ };
20
+ }
21
+ export interface LocalWorker {
22
+ port: number;
23
+ child: ChildProcess;
24
+ invoke(event: unknown): Promise<InvokeResult>;
25
+ stop(): void;
26
+ }
27
+ export declare function startLocalWorker(stage: LocalStage, manifest: ResolvedManifest, root: string, opts?: {
28
+ watch?: boolean;
29
+ }): Promise<LocalWorker>;
30
+ export type DispatchLog = (kind: 'queue' | 'event' | 'cron', name: string, outcome: 'ok' | 'error', detail?: string) => void;
31
+ export interface Dispatcher {
32
+ runCron(name: string): Promise<InvokeResult>;
33
+ stop(): void;
34
+ }
35
+ export declare function startWorkerDispatch(stage: LocalStage, manifest: ResolvedManifest, worker: Pick<LocalWorker, 'invoke'>, log?: DispatchLog): Promise<Dispatcher>;
36
+ /** Lambda's SQS event shape. */
37
+ export declare function sqsEvent(messages: Message[], queueArn: string, region: string): {
38
+ Records: {
39
+ messageId: string | undefined;
40
+ receiptHandle: string | undefined;
41
+ body: string;
42
+ attributes: Partial<Record<import("@aws-sdk/client-sqs").MessageSystemAttributeName, string>>;
43
+ messageAttributes: {
44
+ [k: string]: {
45
+ stringValue: string | undefined;
46
+ binaryValue: Uint8Array<ArrayBufferLike> | undefined;
47
+ dataType: string | undefined;
48
+ stringListValues: never[];
49
+ binaryListValues: never[];
50
+ };
51
+ };
52
+ md5OfBody: string | undefined;
53
+ eventSource: string;
54
+ eventSourceARN: string;
55
+ awsRegion: string;
56
+ }[];
57
+ };