@kirkelliott/zap 0.1.24 → 0.1.26

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/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  > Drop a `.zap` file in S3. It becomes an API endpoint.
4
4
 
5
- You already know S3. You've been dropping files in buckets for years. This makes those files executable.
6
-
7
5
  Drop `hello.zap` → GET `/hello` returns a response. Change the file → behavior changes instantly. No redeploy. No CI. No config.
8
6
 
9
7
  ---
@@ -15,7 +13,7 @@ mkdir my-project && cd my-project
15
13
  npx @kirkelliott/zap init
16
14
  ```
17
15
 
18
- This provisions everything on your AWS account and prints a URL. Takes about 30 seconds. Requires AWS credentials — run `aws configure` first if you haven't.
16
+ Provisions everything on your AWS account and prints a URL. Takes about 30 seconds. Requires AWS credentials — run `aws configure` first if you haven't.
19
17
 
20
18
  ```
21
19
  packaging runtime ✓
@@ -47,7 +45,7 @@ zap deploy hello.zap
47
45
 
48
46
  ## The .zap format
49
47
 
50
- A `.zap` file is a JavaScript module that exports one function. That function handles the request.
48
+ A `.zap` file exports one function. The runtime rewrites `export default` to `module.exports` and runs it in a Node.js `vm` context isolated from the Lambda environment, with only the globals listed below in scope.
51
49
 
52
50
  ```js
53
51
  export default async (req) => {
@@ -77,20 +75,30 @@ req.body // string | null
77
75
 
78
76
  ---
79
77
 
80
- ## Built-ins
78
+ ## Globals
79
+
80
+ These are the only names in scope inside a `.zap` file.
81
+
82
+ **Injected by zap:**
83
+
84
+ | Name | What it does |
85
+ |---|---|
86
+ | `kv` | Persistent key/value storage (DynamoDB-backed) |
87
+ | `zap(name)` | Load another `.zap` from the same S3 bucket |
81
88
 
82
- These are available in every `.zap` file — no imports needed:
89
+ **Standard Node.js 20 globals passed through:**
83
90
 
84
91
  | Name | What it does |
85
92
  |---|---|
86
93
  | `fetch` | HTTP requests |
87
- | `kv` | Persistent key/value storage |
88
- | `zap(name)` | Load another `.zap` from the same bucket |
89
94
  | `crypto` | Web Crypto API |
90
95
  | `URL`, `URLSearchParams` | URL parsing |
91
96
  | `Buffer` | Binary data |
97
+ | `setTimeout`, `clearTimeout` | Timers |
92
98
  | `console` | Logs to CloudWatch |
93
- | `process.env` | Environment variables |
99
+ | `process.env` | Environment variables (`process` is not otherwise available) |
100
+
101
+ `require`, the file system, and the outer Lambda scope are not accessible.
94
102
 
95
103
  ---
96
104
 
@@ -263,6 +271,32 @@ No revert commits. No redeploy. The old code is live immediately.
263
271
 
264
272
  ---
265
273
 
274
+ ## Observability
275
+
276
+ Every request emits a structured JSON log line to CloudWatch:
277
+
278
+ ```json
279
+ {"handler":"hello","method":"GET","status":200,"ms":43,"requestId":"abc-123"}
280
+ ```
281
+
282
+ Every cache refresh (S3 fetch) logs the handler, ETag, and last-modified timestamp:
283
+
284
+ ```json
285
+ {"cache":"refresh","handler":"hello","etag":"\"d41d8cd9\"","modified":"2026-02-28T19:35:00.000Z"}
286
+ ```
287
+
288
+ The `requestId` correlates with Lambda's own `REPORT` line, which adds total duration, billed duration, and memory. Nothing to configure — it's all in CloudWatch Logs.
289
+
290
+ Query with CloudWatch Logs Insights:
291
+
292
+ ```
293
+ filter ispresent(handler)
294
+ | stats avg(ms), count() as reqs by handler
295
+ | sort avg(ms) desc
296
+ ```
297
+
298
+ ---
299
+
266
300
  ## CLI
267
301
 
268
302
  ```
@@ -310,15 +344,15 @@ All within the AWS permanent free tier:
310
344
 
311
345
  ## How it works
312
346
 
313
- One Lambda function runs permanently. Every request:
347
+ One Lambda function (Node.js 20) runs permanently. Every request:
314
348
 
315
- 1. Parses the path — `/proxy` → `proxy.zap`
316
- 2. Fetches that file from S3
317
- 3. Evaluates it in a sandboxed JS environment
349
+ 1. Parses the path — `/hello` → fetches `hello.zap` from S3
350
+ 2. Rewrites `export default` to `module.exports`
351
+ 3. Runs the code in a `vm.runInNewContext` sandbox with the globals listed above
318
352
  4. Calls the exported function with the request
319
353
  5. Returns the response
320
354
 
321
- Updating a handler means updating a file in S3. The runtime always reads fresh.
355
+ Source is cached in Lambda memory for 5 seconds. Deploys propagate within 5 seconds on warm containers.
322
356
 
323
357
  ---
324
358
 
package/dist/handler.js CHANGED
@@ -12,9 +12,10 @@ const loader = async (name) => {
12
12
  const hit = cache.get(name);
13
13
  if (hit && Date.now() - hit.at < TTL)
14
14
  return hit.source;
15
- const { Body } = await s3.send(new client_s3_1.GetObjectCommand({ Bucket: BUCKET, Key: `${name}.zap` }));
15
+ const { Body, ETag, LastModified } = await s3.send(new client_s3_1.GetObjectCommand({ Bucket: BUCKET, Key: `${name}.zap` }));
16
16
  const source = await Body.transformToString();
17
17
  cache.set(name, { source, at: Date.now() });
18
+ console.log(JSON.stringify({ cache: 'refresh', handler: name, etag: ETag, modified: LastModified?.toISOString() }));
18
19
  return source;
19
20
  };
20
21
  const handler = async (event, context) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kirkelliott/zap",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "Drop a .zap file in S3. It becomes an API endpoint.",
5
5
  "main": "dist/handler.js",
6
6
  "bin": {