@lunora/container 1.0.0-alpha.1 → 1.0.0-alpha.3

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.
@@ -1,5 +1,19 @@
1
1
  import { Container, StopParams } from '@cloudflare/containers';
2
2
  import { C as ContainerDefinition } from "../packem_shared/types.d-D2l2SYol.mjs";
3
+ /**
4
+ * Cloudflare Durable Object data-residency jurisdiction. Widening union —
5
+ * Cloudflare adds values over time.
6
+ * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
7
+ */
8
+ type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
9
+ /**
10
+ * Forward one lifecycle envelope to the root ShardDO's log buffer, best-effort.
11
+ *
12
+ * `env` is the Container DO's worker `env`: we read the `SHARD` namespace and
13
+ * the `LUNORA_ADMIN_TOKEN` from it. Returns a promise that NEVER rejects — every
14
+ * failure path (missing binding, missing token, fetch error) resolves to
15
+ * `undefined` — so the caller can `void` it from a lifecycle hook safely.
16
+ */
3
17
  type DurableObjectContext = ConstructorParameters<typeof Container>[0];
4
18
  /**
5
19
  * Base class for the generated Container DO classes. Applies a
@@ -19,9 +33,15 @@ type DurableObjectContext = ConstructorParameters<typeof Container>[0];
19
33
  * ```
20
34
  */
21
35
  declare class LunoraContainer<Env = unknown> extends Container<Env> {
36
+ /**
37
+ * Data-residency jurisdiction the app's DOs are pinned to (codegen passes the
38
+ * schema's `.jurisdiction("…")`). Used to pin the best-effort lifecycle report
39
+ * to the same region as the root shard. `undefined` ⇒ un-pinned.
40
+ */
41
+ private readonly lunoraJurisdiction?;
22
42
  /** The `lunora/containers.ts` export name, for lifecycle log correlation. */
23
43
  private readonly lunoraName;
24
- constructor(context: DurableObjectContext, env: Env, definition: ContainerDefinition, exportName?: string);
44
+ constructor(context: DurableObjectContext, env: Env, definition: ContainerDefinition, exportName?: string, jurisdiction?: DurableObjectJurisdiction);
25
45
  override onError(error: unknown): unknown;
26
46
  override onStart(): Promise<void>;
27
47
  override onStop(parameters: StopParams): Promise<void>;
@@ -1,5 +1,19 @@
1
1
  import { Container, StopParams } from '@cloudflare/containers';
2
2
  import { C as ContainerDefinition } from "../packem_shared/types.d-D2l2SYol.js";
3
+ /**
4
+ * Cloudflare Durable Object data-residency jurisdiction. Widening union —
5
+ * Cloudflare adds values over time.
6
+ * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
7
+ */
8
+ type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
9
+ /**
10
+ * Forward one lifecycle envelope to the root ShardDO's log buffer, best-effort.
11
+ *
12
+ * `env` is the Container DO's worker `env`: we read the `SHARD` namespace and
13
+ * the `LUNORA_ADMIN_TOKEN` from it. Returns a promise that NEVER rejects — every
14
+ * failure path (missing binding, missing token, fetch error) resolves to
15
+ * `undefined` — so the caller can `void` it from a lifecycle hook safely.
16
+ */
3
17
  type DurableObjectContext = ConstructorParameters<typeof Container>[0];
4
18
  /**
5
19
  * Base class for the generated Container DO classes. Applies a
@@ -19,9 +33,15 @@ type DurableObjectContext = ConstructorParameters<typeof Container>[0];
19
33
  * ```
20
34
  */
21
35
  declare class LunoraContainer<Env = unknown> extends Container<Env> {
36
+ /**
37
+ * Data-residency jurisdiction the app's DOs are pinned to (codegen passes the
38
+ * schema's `.jurisdiction("…")`). Used to pin the best-effort lifecycle report
39
+ * to the same region as the root shard. `undefined` ⇒ un-pinned.
40
+ */
41
+ private readonly lunoraJurisdiction?;
22
42
  /** The `lunora/containers.ts` export name, for lifecycle log correlation. */
23
43
  private readonly lunoraName;
24
- constructor(context: DurableObjectContext, env: Env, definition: ContainerDefinition, exportName?: string);
44
+ constructor(context: DurableObjectContext, env: Env, definition: ContainerDefinition, exportName?: string, jurisdiction?: DurableObjectJurisdiction);
25
45
  override onError(error: unknown): unknown;
26
46
  override onStart(): Promise<void>;
27
47
  override onStop(parameters: StopParams): Promise<void>;
package/dist/do/index.mjs CHANGED
@@ -27,6 +27,17 @@ const emitContainerLifecycle = (container, instance, event, message) => {
27
27
 
28
28
  const RECORD_CONTAINER_EVENT_OP = "__lunora_admin__:recordContainerEvent";
29
29
  const ROOT_SHARD_NAME = "__root__";
30
+ const applyJurisdiction = (namespace, jurisdiction) => {
31
+ if (jurisdiction === void 0) {
32
+ return namespace;
33
+ }
34
+ if (typeof namespace.jurisdiction !== "function") {
35
+ throw new TypeError(
36
+ `@lunora/container: Durable Object namespace does not support jurisdiction("${jurisdiction}") — update @cloudflare/workers-types or remove the jurisdiction option`
37
+ );
38
+ }
39
+ return namespace.jurisdiction(jurisdiction);
40
+ };
30
41
  const isShardNamespace = (value) => {
31
42
  if (value === null || typeof value !== "object") {
32
43
  return false;
@@ -34,13 +45,14 @@ const isShardNamespace = (value) => {
34
45
  const candidate = value;
35
46
  return typeof candidate.get === "function" && typeof candidate.idFromName === "function";
36
47
  };
37
- const resolveRootShard = (namespace) => {
38
- if (typeof namespace.getByName === "function") {
39
- return namespace.getByName(ROOT_SHARD_NAME);
48
+ const resolveRootShard = (namespace, jurisdiction) => {
49
+ const pinned = applyJurisdiction(namespace, jurisdiction);
50
+ if (typeof pinned.getByName === "function") {
51
+ return pinned.getByName(ROOT_SHARD_NAME);
40
52
  }
41
- return namespace.get(namespace.idFromName(ROOT_SHARD_NAME));
53
+ return pinned.get(pinned.idFromName(ROOT_SHARD_NAME));
42
54
  };
43
- const reportContainerLifecycle = async (env, envelope) => {
55
+ const reportContainerLifecycle = async (env, envelope, jurisdiction) => {
44
56
  try {
45
57
  const envRecord = env ?? {};
46
58
  const namespace = envRecord["SHARD"];
@@ -56,15 +68,21 @@ const reportContainerLifecycle = async (env, envelope) => {
56
68
  headers: { authorization: `Bearer ${adminBearer}`, "content-type": "application/json" },
57
69
  method: "POST"
58
70
  });
59
- await resolveRootShard(namespace).fetch(request);
71
+ await resolveRootShard(namespace, jurisdiction).fetch(request);
60
72
  } catch {
61
73
  }
62
74
  };
63
75
 
64
76
  class LunoraContainer extends Container {
77
+ /**
78
+ * Data-residency jurisdiction the app's DOs are pinned to (codegen passes the
79
+ * schema's `.jurisdiction("…")`). Used to pin the best-effort lifecycle report
80
+ * to the same region as the root shard. `undefined` ⇒ un-pinned.
81
+ */
82
+ lunoraJurisdiction;
65
83
  /** The `lunora/containers.ts` export name, for lifecycle log correlation. */
66
84
  lunoraName;
67
- constructor(context, env, definition, exportName) {
85
+ constructor(context, env, definition, exportName, jurisdiction) {
68
86
  super(context, env, {
69
87
  defaultPort: definition.defaultPort,
70
88
  envVars: resolveContainerEnvVariables(definition, env, exportName),
@@ -74,6 +92,7 @@ class LunoraContainer extends Container {
74
92
  this.enableInternet = definition.enableInternet;
75
93
  }
76
94
  this.lunoraName = exportName ?? "container";
95
+ this.lunoraJurisdiction = jurisdiction;
77
96
  }
78
97
  onError(error) {
79
98
  const envelope = emitContainerLifecycle(this.lunoraName, this.instanceId(), "error", error instanceof Error ? error.message : String(error));
@@ -98,7 +117,7 @@ class LunoraContainer extends Container {
98
117
  * out of a lifecycle hook — the `console` path stays the source of truth.
99
118
  */
100
119
  surfaceInStudioLogs(envelope) {
101
- reportContainerLifecycle(this.env, envelope).catch(() => {
120
+ reportContainerLifecycle(this.env, envelope, this.lunoraJurisdiction).catch(() => {
102
121
  });
103
122
  }
104
123
  /**
package/dist/index.d.mts CHANGED
@@ -33,10 +33,21 @@ interface ContainerStubLike {
33
33
  start?: (options?: ContainerStartOptions) => Promise<void>;
34
34
  stop?: (signal?: number | string) => Promise<void>;
35
35
  }
36
+ /**
37
+ * Cloudflare Durable Object data-residency jurisdiction. Widening union —
38
+ * Cloudflare adds values over time.
39
+ * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
40
+ */
41
+ type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
36
42
  /** What the client needs from a Durable Object namespace binding. */
37
43
  interface ContainerNamespaceLike {
38
44
  get: (id: unknown) => ContainerStubLike;
39
45
  idFromName: (name: string) => unknown;
46
+ /**
47
+ * Derive a jurisdiction-restricted subnamespace. Optional because older
48
+ * workers-types releases (and test doubles) may not expose it.
49
+ */
50
+ jurisdiction?: (jurisdiction: DurableObjectJurisdiction) => ContainerNamespaceLike;
40
51
  }
41
52
  /** A handle on one container instance (one Durable Object). */
42
53
  interface ContainerHandle {
@@ -126,7 +137,7 @@ interface ContainerBindingSpec {
126
137
  * handle is actually used — so one unprovisioned container never breaks
127
138
  * unrelated functions.
128
139
  */
129
- declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>) => Record<string, ContainerAccessor>;
140
+ declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction) => Record<string, ContainerAccessor>;
130
141
  /** A test handler: receives the request plus the targeted instance name. */
131
142
  type ContainerTestHandler = (request: Request, instance: {
132
143
  name: string;
@@ -186,4 +197,4 @@ declare const isContainerDefinition: (value: unknown) => value is ContainerDefin
186
197
  * credential it was promised yields far worse errors downstream.
187
198
  */
188
199
  declare const resolveContainerEnvVariables: (definition: ContainerDefinition, workerEnv: Record<string, unknown>, exportName?: string) => Record<string, string>;
189
- export { type ContainerAccessor, type ContainerBindingSpec, type ContainerConfig, type ContainerDefinition, type ContainerHandle, type ContainerImageSource, type ContainerInstanceHandle, type ContainerInstanceState, type ContainerNamespaceLike, type ContainerStartOptions, type ContainerTestHandler, type NormalizedContainerImage, type PoolOptions, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };
200
+ export { type ContainerAccessor, type ContainerBindingSpec, type ContainerConfig, type ContainerDefinition, type ContainerHandle, type ContainerImageSource, type ContainerInstanceHandle, type ContainerInstanceState, type ContainerNamespaceLike, type ContainerStartOptions, type ContainerTestHandler, type DurableObjectJurisdiction, type NormalizedContainerImage, type PoolOptions, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };
package/dist/index.d.ts CHANGED
@@ -33,10 +33,21 @@ interface ContainerStubLike {
33
33
  start?: (options?: ContainerStartOptions) => Promise<void>;
34
34
  stop?: (signal?: number | string) => Promise<void>;
35
35
  }
36
+ /**
37
+ * Cloudflare Durable Object data-residency jurisdiction. Widening union —
38
+ * Cloudflare adds values over time.
39
+ * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
40
+ */
41
+ type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
36
42
  /** What the client needs from a Durable Object namespace binding. */
37
43
  interface ContainerNamespaceLike {
38
44
  get: (id: unknown) => ContainerStubLike;
39
45
  idFromName: (name: string) => unknown;
46
+ /**
47
+ * Derive a jurisdiction-restricted subnamespace. Optional because older
48
+ * workers-types releases (and test doubles) may not expose it.
49
+ */
50
+ jurisdiction?: (jurisdiction: DurableObjectJurisdiction) => ContainerNamespaceLike;
40
51
  }
41
52
  /** A handle on one container instance (one Durable Object). */
42
53
  interface ContainerHandle {
@@ -126,7 +137,7 @@ interface ContainerBindingSpec {
126
137
  * handle is actually used — so one unprovisioned container never breaks
127
138
  * unrelated functions.
128
139
  */
129
- declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>) => Record<string, ContainerAccessor>;
140
+ declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction) => Record<string, ContainerAccessor>;
130
141
  /** A test handler: receives the request plus the targeted instance name. */
131
142
  type ContainerTestHandler = (request: Request, instance: {
132
143
  name: string;
@@ -186,4 +197,4 @@ declare const isContainerDefinition: (value: unknown) => value is ContainerDefin
186
197
  * credential it was promised yields far worse errors downstream.
187
198
  */
188
199
  declare const resolveContainerEnvVariables: (definition: ContainerDefinition, workerEnv: Record<string, unknown>, exportName?: string) => Record<string, string>;
189
- export { type ContainerAccessor, type ContainerBindingSpec, type ContainerConfig, type ContainerDefinition, type ContainerHandle, type ContainerImageSource, type ContainerInstanceHandle, type ContainerInstanceState, type ContainerNamespaceLike, type ContainerStartOptions, type ContainerTestHandler, type NormalizedContainerImage, type PoolOptions, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };
200
+ export { type ContainerAccessor, type ContainerBindingSpec, type ContainerConfig, type ContainerDefinition, type ContainerHandle, type ContainerImageSource, type ContainerInstanceHandle, type ContainerInstanceState, type ContainerNamespaceLike, type ContainerStartOptions, type ContainerTestHandler, type DurableObjectJurisdiction, type NormalizedContainerImage, type PoolOptions, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- export { createContainerContext, createContainerTestContext } from './packem_shared/createContainerContext-ChDD53ys.mjs';
1
+ export { createContainerContext, createContainerTestContext } from './packem_shared/createContainerContext-CTpyUQ4J.mjs';
2
2
  export { containerBindingName, containerBuildTag, containerClassName, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVars } from './packem_shared/containerBindingName-BGdSdFNA.mjs';
@@ -1,3 +1,14 @@
1
+ const applyJurisdiction = (namespace, jurisdiction) => {
2
+ if (jurisdiction === void 0) {
3
+ return namespace;
4
+ }
5
+ if (typeof namespace.jurisdiction !== "function") {
6
+ throw new TypeError(
7
+ `@lunora/container: Durable Object namespace does not support jurisdiction("${jurisdiction}") — update @cloudflare/workers-types or remove the jurisdiction option`
8
+ );
9
+ }
10
+ return namespace.jurisdiction(jurisdiction);
11
+ };
1
12
  const DEFAULT_POOL_SIZE = 3;
2
13
  const DEFAULT_MAX_BACKOFF_MS = 3e4;
3
14
  const toRequest = (input, init) => {
@@ -83,11 +94,11 @@ const missingBindingAccessor = (spec) => {
83
94
  };
84
95
  return { any: fail, get: fail, pool: fail };
85
96
  };
86
- const createContainerContext = (env, specs) => {
97
+ const createContainerContext = (env, specs, jurisdiction) => {
87
98
  const containers = {};
88
99
  for (const spec of specs) {
89
- const namespace = env[spec.binding];
90
- containers[spec.exportName] = namespace && typeof namespace.idFromName === "function" && typeof namespace.get === "function" ? accessorFor(namespace, spec) : missingBindingAccessor(spec);
100
+ const binding = env[spec.binding];
101
+ containers[spec.exportName] = binding && typeof binding.idFromName === "function" && typeof binding.get === "function" ? accessorFor(applyJurisdiction(binding, jurisdiction), spec) : missingBindingAccessor(spec);
91
102
  }
92
103
  return containers;
93
104
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/container",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.3",
4
4
  "description": "Cloudflare Containers for Lunora: defineContainer, generated Container DO classes, and the ctx.containers action surface",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -23,7 +23,7 @@
23
23
  "directory": "packages/container"
24
24
  },
25
25
  "files": [
26
- "dist",
26
+ "./dist",
27
27
  "README.md",
28
28
  "LICENSE.md",
29
29
  "__assets__"