@neondatabase/functions 0.2.0 → 0.4.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/README.md CHANGED
@@ -13,7 +13,7 @@ npm install @neondatabase/functions
13
13
  The API mirrors [`@vercel/functions`](https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package): import `waitUntil` and call it directly with the promise you want to keep alive.
14
14
 
15
15
  ```ts
16
- import { waitUntil } from "@neondatabase/functions/v1";
16
+ import { waitUntil } from "@neondatabase/functions";
17
17
 
18
18
  export default {
19
19
  async fetch(req: Request): Promise<Response> {
@@ -33,16 +33,17 @@ without branching. Passing a non-`Promise` throws a `TypeError`.
33
33
  ## Runtime integration
34
34
 
35
35
  The runtime carries the per-invocation context in an `AsyncLocalStorage` and publishes
36
- an accessor at `globalThis[NEON_FUNCTIONS_CONTEXT]` (a
37
- `Symbol.for("@neondatabase/functions/request-context")`) shaped like the providers used
38
- by Vercel and Next.js a stable object exposing `get()`.
36
+ it at `globalThis.NEON_REQUEST_CONTEXT` (the key exported as `NEON_REQUEST_CONTEXT_KEY`)
37
+ as a getter that returns the live context object **directly** — `{ waitUntil }` during
38
+ an invocation, `undefined` outside one. `waitUntil` reads that value straight off the
39
+ key, so there is no `.get()` provider indirection.
39
40
 
40
41
  To make `waitUntil` resolve to a given invocation, wrap the handler with
41
42
  `runWithRequestContext`. This is intended for the Neon Functions runtime; application
42
43
  code should not need it.
43
44
 
44
45
  ```ts
45
- import { runWithRequestContext } from "@neondatabase/functions/v1";
46
+ import { runWithRequestContext } from "@neondatabase/functions";
46
47
 
47
48
  runWithRequestContext({ waitUntil: realWaitUntil }, () => handler(req));
48
49
  ```
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { NEON_FUNCTIONS_CONTEXT, NeonFunctionsContext, WaitUntil, runWithRequestContext, waitUntil } from "./lib/wait-until.js";
2
- export { NEON_FUNCTIONS_CONTEXT, NeonFunctionsContext, WaitUntil, runWithRequestContext, waitUntil };
1
+ import { NEON_REQUEST_CONTEXT_KEY, NeonFunctionsContext, WaitUntil, runWithRequestContext, waitUntil } from "./lib/wait-until.js";
2
+ export { NEON_REQUEST_CONTEXT_KEY, type NeonFunctionsContext, type WaitUntil, runWithRequestContext, waitUntil };
package/dist/index.js CHANGED
@@ -1,3 +1,2 @@
1
- import { NEON_FUNCTIONS_CONTEXT, runWithRequestContext, waitUntil } from "./lib/wait-until.js";
2
- import "./v1.js";
3
- export { NEON_FUNCTIONS_CONTEXT, runWithRequestContext, waitUntil };
1
+ import { NEON_REQUEST_CONTEXT_KEY, runWithRequestContext, waitUntil } from "./lib/wait-until.js";
2
+ export { NEON_REQUEST_CONTEXT_KEY, runWithRequestContext, waitUntil };
@@ -8,17 +8,21 @@ type NeonFunctionsContext = {
8
8
  waitUntil?: WaitUntil;
9
9
  };
10
10
  /**
11
- * Well-known `globalThis` symbol under which the per-invocation context provider is
12
- * published. Mirrors the convention used by Vercel and Next.js.
11
+ * Well-known `globalThis` key under which the Neon Functions runtime publishes the
12
+ * current invocation context. The runtime installs it as a getter that returns the
13
+ * live context object DIRECTLY — `globalThis.NEON_REQUEST_CONTEXT` is `{ waitUntil }`
14
+ * during an invocation and `undefined` outside one — so it is read as the context
15
+ * itself, not via a `.get()`-style provider.
13
16
  */
14
- declare const NEON_FUNCTIONS_CONTEXT: unique symbol;
17
+ declare const NEON_REQUEST_CONTEXT_KEY = "NEON_REQUEST_CONTEXT";
15
18
  /**
16
19
  * Defers async work past the response by forwarding the promise to the Neon Functions
17
20
  * runtime, which keeps the invocation alive until the promise settles.
18
21
  *
19
22
  * The context is resolved at call time from the enclosing invocation, so this stays
20
23
  * correct under concurrency. When no invocation context is in scope (local dev, tests,
21
- * non-Neon hosts), this is a no-op: the promise is accepted and ignored.
24
+ * non-Neon hosts), this is a no-op: the promise is accepted and ignored (it still runs
25
+ * on its own — the caller already started it — it just isn't tracked).
22
26
  */
23
27
  declare function waitUntil(promise: Promise<unknown>): void;
24
28
  /**
@@ -29,5 +33,5 @@ declare function waitUntil(promise: Promise<unknown>): void;
29
33
  */
30
34
  declare function runWithRequestContext<T>(context: NeonFunctionsContext, fn: () => T): T;
31
35
  //#endregion
32
- export { NEON_FUNCTIONS_CONTEXT, NeonFunctionsContext, WaitUntil, runWithRequestContext, waitUntil };
36
+ export { NEON_REQUEST_CONTEXT_KEY, NeonFunctionsContext, WaitUntil, runWithRequestContext, waitUntil };
33
37
  //# sourceMappingURL=wait-until.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wait-until.d.ts","names":[],"sources":["../../src/lib/wait-until.ts"],"mappings":";KAWY,SAAA,aAAsB;AAAlC;AAMA;AAkBA;AAmDA;AAegB,KApFJ,oBAAA,GAoFyB;EAAA,SAAA,CAAA,EAnFxB,SAmFwB;;;;AAGjC;;cArES;;;;;;;;;iBAmDG,SAAA,UAAmB;;;;;;;iBAenB,kCACN,gCACC,IACR"}
1
+ {"version":3,"file":"wait-until.d.ts","names":[],"sources":["../../src/lib/wait-until.ts"],"mappings":";KAWY,SAAA,aAAsB;AAAlC;AAMA;AAWA;AAkDA;AAegB,KA5EJ,oBAAA,GA4EyB;EAAA,SAAA,CAAA,EA3ExB,SA2EwB;;;;AAGjC;;;;;cApES,wBAAA;;;;;;;;;;iBAkDG,SAAA,UAAmB;;;;;;;iBAenB,kCACN,gCACC,IACR"}
@@ -5,34 +5,37 @@ import { AsyncLocalStorage } from "node:async_hooks";
5
5
  * (logging, cache writes, analytics, …) can finish after the response has been sent.
6
6
  *
7
7
  * The public API mirrors Vercel's `@vercel/functions`: import `waitUntil` and call it
8
- * directly with a promise (`waitUntil(promise)`). Under the hood the per-invocation
9
- * context is carried by an `AsyncLocalStorage`, so concurrent invocations sharing the
10
- * same isolate never clobber each other's context.
8
+ * directly with a promise (`waitUntil(promise)`). The active invocation context is
9
+ * published by the runtime on `globalThis`, so it can be read without importing the
10
+ * runtime and stays correct under concurrency.
11
11
  */
12
12
  /**
13
- * Well-known `globalThis` symbol under which the per-invocation context provider is
14
- * published. Mirrors the convention used by Vercel and Next.js.
13
+ * Well-known `globalThis` key under which the Neon Functions runtime publishes the
14
+ * current invocation context. The runtime installs it as a getter that returns the
15
+ * live context object DIRECTLY — `globalThis.NEON_REQUEST_CONTEXT` is `{ waitUntil }`
16
+ * during an invocation and `undefined` outside one — so it is read as the context
17
+ * itself, not via a `.get()`-style provider.
15
18
  */
16
- const NEON_FUNCTIONS_CONTEXT = Symbol.for("@neondatabase/functions/request-context");
17
- /**
18
- * Holds the per-invocation context. Each `runWithRequestContext(...)` call binds a
19
- * fresh context for the duration of its callback (and any async work it spawns), so
20
- * `waitUntil` always resolves to the right invocation even under concurrency.
21
- */
22
- const requestContextStore = new AsyncLocalStorage();
19
+ const NEON_REQUEST_CONTEXT_KEY = "NEON_REQUEST_CONTEXT";
23
20
  const globalWithContext = globalThis;
24
21
  /**
25
- * Publish a stable provider whose `get()` reads the current store. Installed once;
26
- * if another copy of this module (or the host runtime) has already published a
27
- * provider, we leave it in place rather than clobber it.
22
+ * Backs `runWithRequestContext` for local dev and tests. When the runtime is present
23
+ * it has already published its own accessor under the same key, so we leave that in
24
+ * place and never publish over it.
28
25
  */
29
- globalWithContext[NEON_FUNCTIONS_CONTEXT] ??= { get: () => requestContextStore.getStore() };
26
+ const requestContextStore = new AsyncLocalStorage();
27
+ if (!("NEON_REQUEST_CONTEXT" in globalWithContext)) Object.defineProperty(globalWithContext, NEON_REQUEST_CONTEXT_KEY, {
28
+ configurable: true,
29
+ get() {
30
+ return requestContextStore.getStore();
31
+ }
32
+ });
30
33
  /**
31
- * Reads the current invocation context off the `globalThis` provider, falling back to
32
- * an empty context when no provider is published or no invocation is in scope.
34
+ * Reads the current invocation context off `globalThis.NEON_REQUEST_CONTEXT`, falling
35
+ * back to an empty context outside an invocation (local dev, tests, non-Neon hosts).
33
36
  */
34
37
  function getContext() {
35
- return globalWithContext[NEON_FUNCTIONS_CONTEXT]?.get?.() ?? {};
38
+ return globalWithContext["NEON_REQUEST_CONTEXT"] ?? {};
36
39
  }
37
40
  function isPromise(value) {
38
41
  return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
@@ -43,7 +46,8 @@ function isPromise(value) {
43
46
  *
44
47
  * The context is resolved at call time from the enclosing invocation, so this stays
45
48
  * correct under concurrency. When no invocation context is in scope (local dev, tests,
46
- * non-Neon hosts), this is a no-op: the promise is accepted and ignored.
49
+ * non-Neon hosts), this is a no-op: the promise is accepted and ignored (it still runs
50
+ * on its own — the caller already started it — it just isn't tracked).
47
51
  */
48
52
  function waitUntil(promise) {
49
53
  if (!isPromise(promise)) throw new TypeError(`waitUntil can only be called with a Promise, got ${typeof promise}`);
@@ -59,6 +63,6 @@ function runWithRequestContext(context, fn) {
59
63
  return requestContextStore.run(context, fn);
60
64
  }
61
65
  //#endregion
62
- export { NEON_FUNCTIONS_CONTEXT, runWithRequestContext, waitUntil };
66
+ export { NEON_REQUEST_CONTEXT_KEY, runWithRequestContext, waitUntil };
63
67
 
64
68
  //# sourceMappingURL=wait-until.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"wait-until.js","names":[],"sources":["../../src/lib/wait-until.ts"],"sourcesContent":["/**\n * `waitUntil` extends the lifetime of a Neon Function invocation so background work\n * (logging, cache writes, analytics, …) can finish after the response has been sent.\n *\n * The public API mirrors Vercel's `@vercel/functions`: import `waitUntil` and call it\n * directly with a promise (`waitUntil(promise)`). Under the hood the per-invocation\n * context is carried by an `AsyncLocalStorage`, so concurrent invocations sharing the\n * same isolate never clobber each other's context.\n */\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nexport type WaitUntil = (promise: Promise<unknown>) => void;\n\n/**\n * The slice of the runtime context this package reads. The runtime may attach\n * additional fields; only `waitUntil` is consumed here.\n */\nexport type NeonFunctionsContext = {\n\twaitUntil?: WaitUntil;\n};\n\n/**\n * The accessor published on `globalThis` so consumers can read the current\n * invocation context without importing the runtime. Mirrors the provider shape\n * used by Vercel (`Symbol.for(\"@vercel/request-context\")`) and Next.js\n * (`Symbol.for(\"@next/request-context\")`): a stable object exposing `get()`.\n */\ntype RequestContextProvider = {\n\tget(): NeonFunctionsContext | undefined;\n};\n\n/**\n * Well-known `globalThis` symbol under which the per-invocation context provider is\n * published. Mirrors the convention used by Vercel and Next.js.\n */\nexport const NEON_FUNCTIONS_CONTEXT: unique symbol = Symbol.for(\n\t\"@neondatabase/functions/request-context\",\n);\n\ntype GlobalWithContext = typeof globalThis & {\n\t[NEON_FUNCTIONS_CONTEXT]?: RequestContextProvider;\n};\n\n/**\n * Holds the per-invocation context. Each `runWithRequestContext(...)` call binds a\n * fresh context for the duration of its callback (and any async work it spawns), so\n * `waitUntil` always resolves to the right invocation even under concurrency.\n */\nconst requestContextStore = new AsyncLocalStorage<NeonFunctionsContext>();\n\nconst globalWithContext: GlobalWithContext = globalThis;\n\n/**\n * Publish a stable provider whose `get()` reads the current store. Installed once;\n * if another copy of this module (or the host runtime) has already published a\n * provider, we leave it in place rather than clobber it.\n */\nglobalWithContext[NEON_FUNCTIONS_CONTEXT] ??= {\n\tget: () => requestContextStore.getStore(),\n};\n\n/**\n * Reads the current invocation context off the `globalThis` provider, falling back to\n * an empty context when no provider is published or no invocation is in scope.\n */\nfunction getContext(): NeonFunctionsContext {\n\treturn globalWithContext[NEON_FUNCTIONS_CONTEXT]?.get?.() ?? {};\n}\n\nfunction isPromise(value: unknown): value is Promise<unknown> {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"then\" in value &&\n\t\ttypeof value.then === \"function\"\n\t);\n}\n\n/**\n * Defers async work past the response by forwarding the promise to the Neon Functions\n * runtime, which keeps the invocation alive until the promise settles.\n *\n * The context is resolved at call time from the enclosing invocation, so this stays\n * correct under concurrency. When no invocation context is in scope (local dev, tests,\n * non-Neon hosts), this is a no-op: the promise is accepted and ignored.\n */\nexport function waitUntil(promise: Promise<unknown>): void {\n\tif (!isPromise(promise)) {\n\t\tthrow new TypeError(\n\t\t\t`waitUntil can only be called with a Promise, got ${typeof promise}`,\n\t\t);\n\t}\n\tgetContext().waitUntil?.(promise);\n}\n\n/**\n * Runtime entry point: binds `context` as the current invocation context for the\n * duration of `fn` (and any async work it spawns), so calls to `waitUntil` inside it\n * forward to `context.waitUntil`. Intended for the Neon Functions runtime to wrap each\n * invocation; application code should not need this.\n */\nexport function runWithRequestContext<T>(\n\tcontext: NeonFunctionsContext,\n\tfn: () => T,\n): T {\n\treturn requestContextStore.run(context, fn);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAmCA,MAAa,yBAAwC,OAAO,IAC3D,yCACD;;;;;;AAWA,MAAM,sBAAsB,IAAI,kBAAwC;AAExE,MAAM,oBAAuC;;;;;;AAO7C,kBAAkB,4BAA4B,EAC7C,WAAW,oBAAoB,SAAS,EACzC;;;;;AAMA,SAAS,aAAmC;CAC3C,OAAO,kBAAkB,uBAAuB,EAAE,MAAM,KAAK,CAAC;AAC/D;AAEA,SAAS,UAAU,OAA2C;CAC7D,OACC,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,OAAO,MAAM,SAAS;AAExB;;;;;;;;;AAUA,SAAgB,UAAU,SAAiC;CAC1D,IAAI,CAAC,UAAU,OAAO,GACrB,MAAM,IAAI,UACT,oDAAoD,OAAO,SAC5D;CAED,WAAW,CAAC,CAAC,YAAY,OAAO;AACjC;;;;;;;AAQA,SAAgB,sBACf,SACA,IACI;CACJ,OAAO,oBAAoB,IAAI,SAAS,EAAE;AAC3C"}
1
+ {"version":3,"file":"wait-until.js","names":[],"sources":["../../src/lib/wait-until.ts"],"sourcesContent":["/**\n * `waitUntil` extends the lifetime of a Neon Function invocation so background work\n * (logging, cache writes, analytics, …) can finish after the response has been sent.\n *\n * The public API mirrors Vercel's `@vercel/functions`: import `waitUntil` and call it\n * directly with a promise (`waitUntil(promise)`). The active invocation context is\n * published by the runtime on `globalThis`, so it can be read without importing the\n * runtime and stays correct under concurrency.\n */\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nexport type WaitUntil = (promise: Promise<unknown>) => void;\n\n/**\n * The slice of the runtime context this package reads. The runtime may attach\n * additional fields; only `waitUntil` is consumed here.\n */\nexport type NeonFunctionsContext = {\n\twaitUntil?: WaitUntil;\n};\n\n/**\n * Well-known `globalThis` key under which the Neon Functions runtime publishes the\n * current invocation context. The runtime installs it as a getter that returns the\n * live context object DIRECTLY `globalThis.NEON_REQUEST_CONTEXT` is `{ waitUntil }`\n * during an invocation and `undefined` outside one so it is read as the context\n * itself, not via a `.get()`-style provider.\n */\nexport const NEON_REQUEST_CONTEXT_KEY = \"NEON_REQUEST_CONTEXT\";\n\ntype GlobalWithContext = typeof globalThis & {\n\t[NEON_REQUEST_CONTEXT_KEY]?: NeonFunctionsContext;\n};\n\nconst globalWithContext: GlobalWithContext = globalThis;\n\n/**\n * Backs `runWithRequestContext` for local dev and tests. When the runtime is present\n * it has already published its own accessor under the same key, so we leave that in\n * place and never publish over it.\n */\nconst requestContextStore = new AsyncLocalStorage<NeonFunctionsContext>();\n\nif (!(NEON_REQUEST_CONTEXT_KEY in globalWithContext)) {\n\tObject.defineProperty(globalWithContext, NEON_REQUEST_CONTEXT_KEY, {\n\t\tconfigurable: true,\n\t\tget() {\n\t\t\treturn requestContextStore.getStore();\n\t\t},\n\t});\n}\n\n/**\n * Reads the current invocation context off `globalThis.NEON_REQUEST_CONTEXT`, falling\n * back to an empty context outside an invocation (local dev, tests, non-Neon hosts).\n */\nfunction getContext(): NeonFunctionsContext {\n\treturn globalWithContext[NEON_REQUEST_CONTEXT_KEY] ?? {};\n}\n\nfunction isPromise(value: unknown): value is Promise<unknown> {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"then\" in value &&\n\t\ttypeof value.then === \"function\"\n\t);\n}\n\n/**\n * Defers async work past the response by forwarding the promise to the Neon Functions\n * runtime, which keeps the invocation alive until the promise settles.\n *\n * The context is resolved at call time from the enclosing invocation, so this stays\n * correct under concurrency. When no invocation context is in scope (local dev, tests,\n * non-Neon hosts), this is a no-op: the promise is accepted and ignored (it still runs\n * on its own — the caller already started it — it just isn't tracked).\n */\nexport function waitUntil(promise: Promise<unknown>): void {\n\tif (!isPromise(promise)) {\n\t\tthrow new TypeError(\n\t\t\t`waitUntil can only be called with a Promise, got ${typeof promise}`,\n\t\t);\n\t}\n\tgetContext().waitUntil?.(promise);\n}\n\n/**\n * Runtime entry point: binds `context` as the current invocation context for the\n * duration of `fn` (and any async work it spawns), so calls to `waitUntil` inside it\n * forward to `context.waitUntil`. Intended for the Neon Functions runtime to wrap each\n * invocation; application code should not need this.\n */\nexport function runWithRequestContext<T>(\n\tcontext: NeonFunctionsContext,\n\tfn: () => T,\n): T {\n\treturn requestContextStore.run(context, fn);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA4BA,MAAa,2BAA2B;AAMxC,MAAM,oBAAuC;;;;;;AAO7C,MAAM,sBAAsB,IAAI,kBAAwC;AAExE,IAAI,EAAA,0BAA8B,oBACjC,OAAO,eAAe,mBAAmB,0BAA0B;CAClE,cAAc;CACd,MAAM;EACL,OAAO,oBAAoB,SAAS;CACrC;AACD,CAAC;;;;;AAOF,SAAS,aAAmC;CAC3C,OAAO,kBAAA,2BAA+C,CAAC;AACxD;AAEA,SAAS,UAAU,OAA2C;CAC7D,OACC,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,OAAO,MAAM,SAAS;AAExB;;;;;;;;;;AAWA,SAAgB,UAAU,SAAiC;CAC1D,IAAI,CAAC,UAAU,OAAO,GACrB,MAAM,IAAI,UACT,oDAAoD,OAAO,SAC5D;CAED,WAAW,CAAC,CAAC,YAAY,OAAO;AACjC;;;;;;;AAQA,SAAgB,sBACf,SACA,IACI;CACJ,OAAO,oBAAoB,IAAI,SAAS,EAAE;AAC3C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neondatabase/functions",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Runtime helpers for Neon Functions. Currently provides a `waitUntil` primitive for deferring async work past a response.",
5
5
  "keywords": [
6
6
  "neon",
@@ -27,11 +27,6 @@
27
27
  "types": "./dist/index.d.ts",
28
28
  "import": "./dist/index.js",
29
29
  "default": "./dist/index.js"
30
- },
31
- "./v1": {
32
- "types": "./dist/v1.d.ts",
33
- "import": "./dist/v1.js",
34
- "default": "./dist/v1.js"
35
30
  }
36
31
  },
37
32
  "files": [
package/dist/v1.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import { NEON_FUNCTIONS_CONTEXT, NeonFunctionsContext, WaitUntil, runWithRequestContext, waitUntil } from "./lib/wait-until.js";
2
- export { NEON_FUNCTIONS_CONTEXT, type NeonFunctionsContext, type WaitUntil, runWithRequestContext, waitUntil };
package/dist/v1.js DELETED
@@ -1,2 +0,0 @@
1
- import { NEON_FUNCTIONS_CONTEXT, runWithRequestContext, waitUntil } from "./lib/wait-until.js";
2
- export { NEON_FUNCTIONS_CONTEXT, runWithRequestContext, waitUntil };