@xplane/core 0.16.0 → 1.0.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/dist/index.d.mts CHANGED
@@ -1,148 +1,345 @@
1
1
  import { Construct, Construct as Construct$1 } from "constructs";
2
+ import { AsyncLocalStorage } from "node:async_hooks";
2
3
 
3
- //#region src/tracking/types.d.ts
4
+ //#region src/contract.d.ts
5
+ /**
6
+ * Contract types defining the boundary between the runtime (@xplane/function)
7
+ * and the framework (@xplane/core).
8
+ *
9
+ * The runtime provides CompositionInput (plain data extracted from Crossplane),
10
+ * the framework returns CompositionResult (plain serializable data).
11
+ * No class instances, no WeakMaps, no shared state crosses this boundary.
12
+ */
13
+ /**
14
+ * Plain data input provided by the runtime to the composition.
15
+ * All values are serializable Records — no class instances.
16
+ */
17
+ interface CompositionInput {
18
+ /** Observed XR data (spec, status, metadata). */
19
+ xr: Record<string, unknown>;
20
+ /** Crossplane function pipeline context keys. */
21
+ pipelineContext: Record<string, unknown>;
22
+ /** Observed composed resources, keyed by resource name (e.g. "Composition/vpc"). */
23
+ observedComposed: Record<string, Record<string, unknown>>;
24
+ /** Observed existing/required resources, keyed by refKey. */
25
+ observedRequired: Record<string, Record<string, unknown>>;
26
+ }
27
+ /**
28
+ * Plain data output returned by the composition to the runtime.
29
+ * Fully serializable — no class instances, proxies, or internal state.
30
+ */
31
+ interface CompositionResult {
32
+ /** Resources to emit to Crossplane as desired composed resources. */
33
+ resources: DesiredResource[];
34
+ /** External resources that need to be fetched via requireResource. */
35
+ externalResources: ExternalResourceRequest[];
36
+ /** Desired XR status patches (from this.xr.status assignments). */
37
+ xrStatus: Record<string, unknown>;
38
+ /** Diagnostic reports for blocked/unresolved resources. */
39
+ diagnostics: Diagnostic[];
40
+ }
41
+ /** A desired composed resource ready for emission. */
42
+ interface DesiredResource {
43
+ /** The resource name (construct path without "Composition/" prefix). */
44
+ name: string;
45
+ /** The fully-resolved desired Kubernetes resource document. */
46
+ document: Record<string, unknown>;
47
+ /** Whether this resource is ready (readiness already evaluated). */
48
+ ready: boolean;
49
+ }
50
+ /** A request to fetch an external (existing) resource. */
51
+ interface ExternalResourceRequest {
52
+ /** The refKey used to match the resource. */
53
+ refKey: string;
54
+ apiVersion: string;
55
+ kind: string;
56
+ /** The name to match. */
57
+ name: string;
58
+ /** Optional namespace. */
59
+ namespace?: string;
60
+ }
61
+ /** A diagnostic report for a blocked resource. */
62
+ interface Diagnostic {
63
+ /** The resource name (construct path). */
64
+ resource: string;
65
+ /** Why the resource is blocked. */
66
+ reason: 'pending' | 'cycle' | 'not-found';
67
+ /** For 'pending': what paths are waiting on what. */
68
+ pendingPaths?: Array<{
69
+ path: string;
70
+ waitingOn: {
71
+ resource: string;
72
+ path: string;
73
+ };
74
+ }>;
75
+ /** For 'cycle': the cycle path. */
76
+ cycle?: string[];
77
+ /** Human-readable detail. */
78
+ detail?: string;
79
+ }
4
80
  /**
5
- * Symbols used to access tracking metadata on proxy-wrapped values.
6
- * These are not enumerable and won't leak into serialized output.
81
+ * The shape of what a composition bundle must export.
82
+ * Both full bundles and thin bundles export this.
7
83
  */
8
- declare const TRACKING_META: unique symbol;
9
- declare const IS_TRACKED: unique symbol;
10
- /** Identifies which resource a tracked value belongs to. */
84
+ interface CompositionModule {
85
+ run(input: CompositionInput): CompositionResult;
86
+ }
87
+ //#endregion
88
+ //#region src/tracking/types.d.ts
89
+ /** Identifies a resource in the dependency graph. */
11
90
  interface ResourceRef {
12
91
  readonly id: string;
13
92
  }
14
- /** A single dependency edge between two resource fields. */
93
+ /** A dependency edge between two resources at specific field paths. */
15
94
  interface DependencyEdge {
16
- /** Resource whose field is being read (the dependency). */
95
+ /** Resource being read from (observed state). */
17
96
  readonly from: ResourceRef;
18
- /** Dot-separated path on the source resource. */
97
+ /** Path in the source resource's observed data. */
19
98
  readonly fromPath: string;
20
- /** Resource whose field is being set (the dependent). */
99
+ /** Resource being written to (desired state). */
21
100
  readonly to: ResourceRef;
22
- /** Dot-separated path on the target resource. */
101
+ /** Path in the target resource's desired data. */
23
102
  readonly toPath: string;
24
103
  }
25
- /** Metadata attached to every tracked proxy. */
26
- interface TrackingMeta {
27
- /** The resource this value belongs to. */
28
- readonly owner: ResourceRef;
29
- /** The dot-separated path from root of the resource object. */
104
+ /**
105
+ * A Pending marker stored in a desired document when a ReadProxy value
106
+ * is assigned. Carries full source info so the resolve phase knows
107
+ * where to look for the concrete value.
108
+ */
109
+ declare const PENDING_TAG: unique symbol;
110
+ declare class Pending {
111
+ /** The resource that owns the observed data. */
112
+ readonly source: ResourceRef;
113
+ /** The path within that resource's observed data. */
30
114
  readonly path: string;
31
- /** Whether this value originates from observed state (read-only). */
32
- readonly observed: boolean;
115
+ static readonly TAG: symbol;
116
+ readonly [PENDING_TAG] = true;
117
+ constructor(/** The resource that owns the observed data. */
118
+
119
+ source: ResourceRef, /** The path within that resource's observed data. */
120
+
121
+ path: string);
122
+ static is(value: unknown): value is Pending;
33
123
  }
34
- /** Reference to an existing cluster resource requested via Crossplane's required resources mechanism. */
35
- interface ExistingResourceRef {
36
- /** API version of the resource (e.g. "example.io/v1"). */
37
- readonly apiVersion: string;
38
- /** Kind of the resource (e.g. "Project"). */
39
- readonly kind: string;
40
- /** Name of the resource. May be a raw string or a tracked proxy value (resolved later). */
41
- readonly name: unknown;
42
- /** Optional namespace of the resource. */
43
- readonly namespace?: string;
44
- /** Deterministic key for this reference (apiVersion/kind/[namespace/]name). */
45
- readonly refKey: string;
124
+ /** Metadata associated with a ReadProxy instance. */
125
+ interface ReadProxyMeta {
126
+ readonly owner: ResourceRef;
127
+ readonly path: string;
46
128
  }
47
129
  //#endregion
48
130
  //#region src/tracking/dependency-graph.d.ts
49
131
  /**
50
- * Directed acyclic graph of resource dependencies.
51
- * Supports topological sorting to determine resource creation order
52
- * and cycle detection to surface configuration errors.
132
+ * Tracks dependency relationships between resources as a directed acyclic graph.
133
+ * Edges are added as resources reference each other's observed state.
53
134
  */
54
135
  declare class DependencyGraph {
55
- /** adjacency list: resource id → set of resource ids it depends on */
56
- private readonly _deps;
57
- /** all registered resource refs by id */
136
+ private readonly _adjacency;
58
137
  private readonly _resources;
59
- /** raw edges for introspection */
60
138
  private readonly _edges;
61
- /** Register a resource node in the graph. */
62
139
  addResource(ref: ResourceRef): void;
63
- /** Add dependency edges from the collector. */
140
+ addEdge(edge: DependencyEdge): void;
64
141
  addEdges(edges: ReadonlyArray<DependencyEdge>): void;
65
- /** Add an explicit dependency: `dependent` depends on `dependency`. */
66
142
  addExplicitDependency(dependent: ResourceRef, dependency: ResourceRef): void;
67
- /** Get all resource IDs that `resourceId` directly depends on. */
143
+ /** Get the set of resource IDs that `resourceId` depends on. */
68
144
  getDependencies(resourceId: string): ReadonlySet<string>;
69
- /** Get all registered resource IDs. */
70
145
  get resourceIds(): ReadonlyArray<string>;
71
- /** Get all raw edges. */
72
146
  get edges(): ReadonlyArray<DependencyEdge>;
73
147
  /**
74
- * Returns resource IDs in topological order (dependencies first).
75
- * Throws if the graph contains cycles.
148
+ * Returns a topological ordering of resources.
149
+ * If a cycle is detected, returns { order: null, cycle: string[] }.
76
150
  */
77
- topologicalSort(): string[];
151
+ topologicalSort(): {
152
+ order: string[];
153
+ } | {
154
+ order: null;
155
+ cycle: string[];
156
+ };
78
157
  }
79
158
  //#endregion
80
- //#region src/tracking/proxy.d.ts
159
+ //#region src/tracking/read-proxy.d.ts
81
160
  /**
82
- * Registry that collects dependency edges discovered during proxy access.
83
- * Shared across all tracked values within a single composition run.
161
+ * Check if a value is a ReadProxy.
162
+ */
163
+ declare function isReadProxy(value: unknown): value is object;
164
+ /**
165
+ * Get the metadata for a ReadProxy value.
166
+ */
167
+ declare function getReadProxyMeta(value: unknown): ReadProxyMeta | undefined;
168
+ /**
169
+ * Creates a ReadProxy that wraps observed data.
170
+ *
171
+ * - Property access navigates into the data, building up the path.
172
+ * - Missing paths return `undefined` (no placeholder proxies).
173
+ * - The proxy carries owner + path metadata so that when it's assigned
174
+ * to a WriteProxy, the dependency edge can be recorded.
84
175
  */
85
- declare class DependencyCollector {
176
+ declare function createReadProxy<T extends object>(target: T, owner: ResourceRef, basePath: string): T;
177
+ /**
178
+ * Wraps a concrete primitive value so it carries ReadProxy metadata.
179
+ * This allows the WriteProxy to detect it during assignment and
180
+ * record the dependency edge, while the value itself resolves correctly.
181
+ */
182
+ declare function createPrimitiveReadProxy(value: string | number | boolean, owner: ResourceRef, path: string): object;
183
+ //#endregion
184
+ //#region src/tracking/write-proxy.d.ts
185
+ /**
186
+ * Collector that accumulates dependency edges discovered during
187
+ * WriteProxy assignments.
188
+ */
189
+ declare class EdgeCollector {
86
190
  private readonly _edges;
87
- addEdge(edge: DependencyEdge): void;
191
+ add(edge: DependencyEdge): void;
88
192
  get edges(): ReadonlyArray<DependencyEdge>;
89
- clear(): void;
90
193
  }
91
- /** Options for creating a tracked proxy. */
92
- interface TrackedProxyOptions {
93
- /** Which resource this value belongs to. */
194
+ interface WriteProxyOptions {
195
+ /** The resource that owns this desired document. */
94
196
  owner: ResourceRef;
95
- /** Dot-path from the resource root (e.g. "spec.forProvider.region"). */
96
- path: string;
97
- /** Whether this originates from observed state. */
98
- observed: boolean;
99
- /** Shared collector for discovered dependencies. */
100
- collector: DependencyCollector;
197
+ /** The collector to record edges into. */
198
+ collector: EdgeCollector;
199
+ /** Base path prefix (e.g., "spec" when wrapping the spec subtree). */
200
+ basePath?: string;
201
+ }
202
+ /**
203
+ * Creates a WriteProxy that wraps a desired document.
204
+ *
205
+ * - Writes store values in the target.
206
+ * - When a ReadProxy value is assigned, it records a dependency edge
207
+ * and stores a Pending marker if the value is not yet concrete.
208
+ * - Reads return the stored value (desired-first).
209
+ */
210
+ declare function createWriteProxy<T extends object>(target: T, opts: WriteProxyOptions): T;
211
+ //#endregion
212
+ //#region src/core/composition.d.ts
213
+ /** The shape of the XR proxy exposed via `this.xr`. */
214
+ interface XrProxy<TSpec = Record<string, unknown>, TStatus = Record<string, unknown>> {
215
+ spec: TSpec;
216
+ status: TStatus;
217
+ metadata: {
218
+ name: string;
219
+ namespace?: string;
220
+ labels?: Record<string, string>;
221
+ annotations?: Record<string, string>;
222
+ [key: string]: unknown;
223
+ };
224
+ [key: string]: unknown;
225
+ }
226
+ /**
227
+ * Base class for user-authored Crossplane compositions.
228
+ *
229
+ * Users extend this class and define resources in the constructor:
230
+ *
231
+ * ```ts
232
+ * class MyComposition extends Composition<MySpec, MyStatus> {
233
+ * constructor() {
234
+ * super();
235
+ * const vpc = new Vpc(this, 'vpc', { spec: { ... } });
236
+ * this.xr.status.vpcId = vpc.status.atProvider.vpcId;
237
+ * }
238
+ * }
239
+ * ```
240
+ *
241
+ * `this.xr` is a "desired-first, fallback-to-observed" proxy over the XR.
242
+ * `this.pipelineContext` provides typed read-only access to Crossplane function context.
243
+ */
244
+ declare class Composition<TSpec = Record<string, unknown>, TStatus = Record<string, unknown>, TContext extends object = Record<string, unknown>> extends Construct$1 {
245
+ /**
246
+ * The XR proxy — reads from desired first (status writes), falls through to observed.
247
+ * Writing to `this.xr.status.*` sets the composite status output.
248
+ */
249
+ readonly xr: XrProxy<TSpec, TStatus>;
250
+ /** The dependency graph tracking resource relationships. */
251
+ readonly graph: DependencyGraph;
252
+ /** The edge collector accumulating dependency edges. */
253
+ readonly collector: EdgeCollector;
254
+ constructor();
101
255
  /**
102
- * When true, missing keys on observed proxies return `undefined` instead of
103
- * placeholder proxies. This allows optional chaining (`?.`) to short-circuit
104
- * correctly. Used for XR proxies whose data is fully available at composition time.
256
+ * Read-only accessor for Crossplane function pipeline context.
257
+ * Keys are the context keys set by Crossplane or prior functions in the pipeline.
105
258
  */
106
- strict?: boolean;
259
+ get pipelineContext(): PipelineContextAccessor<TContext>;
260
+ }
261
+ /** Typed read-only interface for pipeline context. */
262
+ interface PipelineContextAccessor<TContext extends object = Record<string, unknown>> {
263
+ get<K extends keyof TContext>(key: K): TContext[K] | undefined;
264
+ has(key: keyof TContext): boolean;
265
+ keys(): IterableIterator<keyof TContext>;
107
266
  }
108
267
  /**
109
- * Returns true if `value` is a tracked proxy created by `createTrackedProxy`.
268
+ * Extract the desired XR status from a Composition instance.
269
+ * Used by the emit pipeline phase to produce composite status output.
110
270
  */
111
- declare function isTracked(value: unknown): value is object & {
112
- [TRACKING_META]: TrackingMeta;
113
- };
271
+ declare function getXrDesiredStatus(composition: Composition): Record<string, unknown>;
272
+ //#endregion
273
+ //#region src/core/context.d.ts
114
274
  /**
115
- * Retrieves the tracking metadata from a tracked proxy.
116
- * Returns undefined if the value is not tracked.
275
+ * Runtime context passed into a Composition during construction
276
+ * via AsyncLocalStorage. Holds everything the Composition needs
277
+ * without relying on static state or globalThis.
117
278
  */
118
- declare function getTrackingMeta(value: unknown): TrackingMeta | undefined;
279
+ interface CompositionContext {
280
+ /** Observed XR data. */
281
+ xr: Record<string, unknown>;
282
+ /** Full Crossplane function pipeline context (all keys). */
283
+ pipelineContext: ReadonlyMap<string, unknown>;
284
+ /** Pre-populated data for existing resources (from prior iterations). */
285
+ requiredResources: ReadonlyMap<string, Record<string, unknown>>;
286
+ /** The dependency graph for this composition run. */
287
+ graph: DependencyGraph;
288
+ /** The edge collector for this composition run. */
289
+ collector: EdgeCollector;
290
+ }
291
+ /**
292
+ * AsyncLocalStorage instance that carries the CompositionContext.
293
+ * The handler sets it before constructing the user's Composition,
294
+ * and the Composition constructor reads from it.
295
+ */
296
+ declare const compositionStorage: AsyncLocalStorage<CompositionContext>;
297
+ /**
298
+ * Get the current composition context from AsyncLocalStorage.
299
+ * Throws if called outside of a composition construction scope.
300
+ */
301
+ declare function getCompositionContext(): CompositionContext;
302
+ //#endregion
303
+ //#region src/readiness/types.d.ts
119
304
  /**
120
- * Creates a proxy around `target` that:
121
- * 1. Returns nested proxies for property access (building up dot-paths).
122
- * 2. On set: if the assigned value is itself a tracked proxy from a *different*
123
- * resource, records a DependencyEdge in the collector.
124
- * 3. Stores concrete values normally so the underlying object is populated.
305
+ * A readiness check function that evaluates whether a resource is ready
306
+ * based on its observed state.
125
307
  *
126
- * For "observed" proxies, property reads on missing keys return a nested
127
- * tracked proxy (representing an "unknown" value) rather than undefined.
128
- * This lets `vpc.status.atProvider.vpcId` work even before the VPC exists.
308
+ * @returns `true` if ready, `false` if not ready, `undefined` if unable to determine
129
309
  */
130
- declare function createTrackedProxy<T extends object>(target: T, opts: TrackedProxyOptions): T;
310
+ type ReadyCheckFn = (observed: Record<string, unknown>) => boolean | undefined;
131
311
  /**
132
- * Sentinel value used when a tracked reference cannot be resolved yet
133
- * (the source resource hasn't been observed).
312
+ * A readiness check with an associated priority.
313
+ * Lower priority numbers are evaluated first.
314
+ * Checks at the same priority level are AND-ed together.
134
315
  */
135
- declare const UNRESOLVED: unique symbol;
316
+ interface ReadyCheck {
317
+ fn: ReadyCheckFn;
318
+ priority: number;
319
+ name?: string;
320
+ }
136
321
  //#endregion
137
- //#region src/core/resource.d.ts
322
+ //#region src/readiness/defaults.d.ts
323
+ /**
324
+ * Built-in default readiness checks, appended at low priority.
325
+ */
326
+ declare const DEFAULT_CHECKS: ReadyCheck[];
327
+ //#endregion
328
+ //#region src/readiness/evaluate.d.ts
138
329
  /**
139
- * Recursive type that allows arbitrary deep property access without undefined.
140
- * Uses a known-key mapped type to bypass noUncheckedIndexedAccess.
141
- * Used as the default for untyped Resource spec/status so that
142
- * `resource.spec.forProvider.vpcId` compiles without casts.
330
+ * Evaluate readiness for a resource by running checks grouped by priority.
331
+ *
332
+ * - If `observed` is undefined, the resource doesn't exist yet not ready.
333
+ * - Checks are grouped by priority (ascending). Within a group, all checks
334
+ * are AND-ed: if any returns `false`, the resource is not ready. If at least
335
+ * one returns `true` and none return `false`, the resource is ready.
336
+ * If all return `undefined`, cascade to the next priority group.
337
+ * - Final fallback (no group had a definitive answer): not ready.
143
338
  */
144
- type AnyFields = Record<string, any>;
145
- /** Minimal Kubernetes resource shape. */
339
+ declare function evaluateReadiness(checks: ReadyCheck[], observed: Record<string, unknown> | undefined): boolean;
340
+ //#endregion
341
+ //#region src/core/resource.d.ts
342
+ /** Minimal shape for a Kubernetes resource — only apiVersion + kind required. */
146
343
  interface KubernetesResource {
147
344
  apiVersion: string;
148
345
  kind: string;
@@ -153,278 +350,257 @@ interface KubernetesResource {
153
350
  annotations?: Record<string, string>;
154
351
  [key: string]: unknown;
155
352
  };
156
- spec?: Record<string, unknown>;
157
- status?: Record<string, unknown>;
158
353
  [key: string]: unknown;
159
354
  }
160
- /** Props for constructing a Resource. */
355
+ /** Props passed to the Resource constructor — becomes the desired document. */
161
356
  interface ResourceProps {
162
357
  apiVersion: string;
163
358
  kind: string;
164
- metadata?: {
359
+ [key: string]: unknown;
360
+ }
361
+ /** Framework configuration accessible via `resource.resource`. */
362
+ interface ResourceConfig {
363
+ autoReady: boolean;
364
+ addReadyCheck(fn: ReadyCheckFn, priority?: number): void;
365
+ }
366
+ /** Internal metadata stored per Resource (not exposed on the proxy). */
367
+ interface ResourceInternals {
368
+ /** Unique reference for the dependency graph. */
369
+ ref: ResourceRef;
370
+ /** The desired document (what the user wrote). */
371
+ desired: Record<string, unknown>;
372
+ /** The observed document (populated by the hydrate phase). */
373
+ observed: Record<string, unknown>;
374
+ /** Whether this is an external (existing) resource. */
375
+ external: boolean;
376
+ /** For external resources: the lookup key. */
377
+ externalRef?: ExternalResourceRef;
378
+ /** Framework config. */
379
+ config: ResourceConfig;
380
+ /** Custom readiness checks registered by the composition author. */
381
+ readyChecks: ReadyCheck[];
382
+ /** The dependency graph. */
383
+ graph: DependencyGraph;
384
+ /** The edge collector. */
385
+ collector: EdgeCollector;
386
+ }
387
+ interface ExternalResourceRef {
388
+ apiVersion: string;
389
+ kind: string;
390
+ name: unknown;
391
+ namespace?: string;
392
+ refKey: string;
393
+ }
394
+ /**
395
+ * A Kubernetes resource within a Composition.
396
+ *
397
+ * The Resource instance acts as a "desired-first, fallback-to-observed" proxy:
398
+ * - Reading a path that exists in the desired document returns the desired value.
399
+ * - Reading a path that does NOT exist in desired falls through to a tracked
400
+ * ReadProxy over observed state (creates dependency edges).
401
+ * - Writing always goes to the desired document.
402
+ *
403
+ * The only reserved properties are `node` (from Construct) and `resource`
404
+ * (framework config namespace).
405
+ */
406
+ declare class Resource extends Construct$1 {
407
+ readonly resource: ResourceConfig;
408
+ metadata: {
165
409
  name?: string;
166
410
  namespace?: string;
167
411
  labels?: Record<string, string>;
168
412
  annotations?: Record<string, string>;
169
413
  [key: string]: unknown;
170
414
  };
171
- spec?: Record<string, unknown>;
172
- /** Top-level extra fields for resources that don't use spec (e.g. Secret's data/stringData/type). */
173
- [key: string]: unknown;
174
- }
175
- /** Configuration options for a resource. */
176
- interface ResourceOptions {
177
- /** Whether auto-ready detection is enabled for this resource. Default: true. */
178
- autoReady?: boolean;
179
- }
180
- /**
181
- * A Construct that represents a single Crossplane managed/composed resource.
182
- *
183
- * The `spec` and `status` properties are proxy-wrapped for automatic
184
- * dependency tracking. Assigning a value from another resource's status
185
- * to this resource's spec automatically records a dependency edge.
186
- */
187
- declare class Resource<TSpec extends object = AnyFields, TStatus extends object = AnyFields> extends Construct$1 {
188
- readonly apiVersion: string;
189
- readonly kind: string;
190
- readonly resourceRef: ResourceRef;
191
- /** Proxy-wrapped desired spec — writes are tracked. */
192
- readonly spec: TSpec;
193
- /** Proxy-wrapped observed status — reads create dependency tracking. */
194
- readonly status: TStatus;
195
- /** Proxy-wrapped desired metadata. */
196
- readonly metadata: NonNullable<KubernetesResource['metadata']>;
415
+ constructor(scope: Construct$1, id: string, props: ResourceProps);
197
416
  /**
198
- * Observed-mode tracked proxy over the entire resource document.
199
- * Use this to access arbitrary top-level fields on existing resources
200
- * (e.g., `secret.root.data.myKey` for a Secret, `configMap.root.data.key` for a ConfigMap).
417
+ * Look up an existing cluster resource by name.
418
+ * Returns a Resource that only has observed state (no desired output).
419
+ *
420
+ * The `name` parameter accepts either a plain string or a PrimitiveReadProxy
421
+ * (returned when reading a tracked property like `ns.metadata.labels['x']`).
422
+ * Proxies are coerced to their underlying string via `Symbol.toPrimitive`.
201
423
  */
202
- readonly root: AnyFields;
203
- /** Whether auto-ready is enabled for this resource. */
204
- autoReady: boolean;
205
- /** Whether this is a read-only reference to an existing cluster resource. */
206
- readonly isExisting: boolean;
207
- /** If this is an existing resource, holds the reference metadata for the handler. */
208
- readonly existingRef: ExistingResourceRef | undefined;
209
- /** Extra top-level fields (e.g. data/stringData for Secret). Not proxy-tracked. */
210
- private readonly _extra;
211
- /** Observed state populated by the bridge before construction. */
212
- private _observed;
213
- /** Backing object for the status proxy — populated by setObserved(). */
214
- private readonly _statusTarget;
215
- /** Backing object for the spec proxy (existing resources only) — populated by setObservedFull(). */
216
- private readonly _specTarget;
217
- /** Backing object for the root proxy — populated by setObservedFull(). */
218
- private readonly _rootTarget;
219
- /** Backing object for the metadata proxy — populated by setObserved(). */
220
- private readonly _metaTarget;
221
- /** Keys the user explicitly declared in constructor metadata props. */
222
- private readonly _desiredMetaKeys;
223
- /** Explicit dependency refs. */
224
- private readonly _explicitDeps;
225
- /** @internal */
226
- private readonly _graph;
227
- constructor(scope: Construct$1, id: string, props: ResourceProps, options?: ResourceOptions);
228
- /** Fully qualified path in the construct tree. */
229
- get path(): string;
230
- /** Add an explicit dependency on another resource. */
231
- addDependency(other: Resource): void;
232
- /** Get explicit dependency refs. */
233
- get explicitDependencies(): ReadonlyArray<ResourceRef>;
234
- /** Set observed state (called by the bridge before compose). */
235
- setObserved(observed: KubernetesResource): void;
236
- /** Get observed state. */
237
- get observed(): KubernetesResource | undefined;
424
+ static fromExistingByName(scope: Construct$1, apiVersion: string, kind: string, name: unknown, namespace?: string): Resource;
238
425
  /**
239
- * Compute a unique name for a resource based on its construct node path,
240
- * similar to `cdk.Names.uniqueResourceName`.
241
- *
242
- * The name is structured as:
243
- * `[namespace-]claimName-PathSegments[-extra]-hash8`
244
- *
245
- * - XR namespace (if present) and XR name are always prepended.
246
- * - Path segments (construct tree, root skipped) are appended next.
247
- * - An optional `extra` string is appended after the path.
248
- * - An 8-char hash of the full untruncated string is always appended for uniqueness.
249
- * - Whitespace in each segment is stripped (CDK convention).
250
- * - Disallowed characters are replaced by the separator; consecutive separators are collapsed.
251
- * - The result is truncated to `maxLength` while keeping the hash suffix.
252
- *
253
- * @param scope - The construct whose node path is used.
254
- * @param options - Optional tuning.
426
+ * Generate a deterministic unique name based on the XR identity and construct path.
427
+ * Useful for resource fields that need unique names (e.g., AWS resource names).
255
428
  */
256
429
  static uniqueName(scope: Construct$1, options?: {
257
- /** Maximum length of the resulting name. Default: 63. */maxLength?: number; /** Separator inserted between path segments (also replaces disallowed chars). Default: "-". */
258
- separator?: string; /** Regex of characters to keep. Anything else is replaced by the separator. Default: /[^a-zA-Z0-9]/g */
259
- allowedPattern?: RegExp; /** Extra string appended after the path segments and before the hash. */
430
+ maxLength?: number;
431
+ separator?: string;
432
+ allowedPattern?: RegExp;
260
433
  extra?: string;
261
434
  }): string;
262
- /**
263
- * Serialize to a plain Kubernetes resource object for the desired state.
264
- * Strips proxy wrappers, UNRESOLVED sentinels, and server-managed metadata
265
- * fields (uid, resourceVersion, etc.) that must not appear in desired state.
266
- */
267
- toDesired(): KubernetesResource;
268
- /**
269
- * Populate the full observed state for an existing resource.
270
- * Feeds data into `root`, `status`, `spec`, and `metadata` backing targets
271
- * so that proxy reads resolve to real values.
272
- */
273
- setObservedFull(resource: KubernetesResource): void;
274
- /**
275
- * Create a read-only reference to an existing cluster resource.
276
- * The resource will be fetched by Crossplane via the Required Resources mechanism.
277
- * Its `status`, `spec`, and `root` proxies can be read to create dependency edges.
278
- *
279
- * @param scope - Parent construct (typically `this` inside a Composition constructor)
280
- * @param apiVersion - API version of the resource (e.g. "example.io/v1")
281
- * @param kind - Kind of the resource (e.g. "Project")
282
- * @param name - Name of the resource (can be a literal string or a tracked proxy value)
283
- * @param namespace - Optional namespace of the resource
284
- */
285
- static fromExistingByName(scope: Construct$1, apiVersion: string, kind: string, name: unknown, namespace?: string): Resource;
286
435
  }
436
+ declare function getResourceInternals(resource: Resource): ResourceInternals;
437
+ declare function getResourceRef(resource: Resource): ResourceRef;
438
+ declare function getDesiredDocument(resource: Resource): Record<string, unknown>;
439
+ declare function getObservedDocument(resource: Resource): Record<string, unknown>;
440
+ declare function hydrateObserved(resource: Resource, data: Record<string, unknown>): void;
441
+ declare function isExternal(resource: Resource): boolean;
442
+ declare function getExternalRef(resource: Resource): ExternalResourceRef | undefined;
443
+ declare function getReadyChecks(resource: Resource): ReadyCheck[];
444
+ //#endregion
445
+ //#region src/logging/types.d.ts
287
446
  /**
288
- * Compute a deterministic reference key for an existing resource.
289
- * Format: "apiVersion/kind/[namespace/]name" or "apiVersion/kind/__unresolved__" if name is not yet known.
447
+ * Minimal logger interface for the @xplane/core library.
448
+ * Compatible with pino, the Crossplane function-sdk Logger, and console.
290
449
  */
291
- declare function computeRefKey(apiVersion: string, kind: string, name: string | undefined, namespace?: string): string;
450
+ interface XplaneLogger {
451
+ debug(msg: string, data?: Record<string, unknown>): void;
452
+ info(msg: string, data?: Record<string, unknown>): void;
453
+ warn(msg: string, data?: Record<string, unknown>): void;
454
+ }
292
455
  //#endregion
293
- //#region src/core/composition.d.ts
456
+ //#region src/logging/context.d.ts
294
457
  /**
295
- * A Composition is the root Construct for a Crossplane composition function.
296
- * Like CDK's `App` or cdk8s's `Chart`, it is the root of the construct tree.
297
- * Resources and constructs are created in the constructor.
298
- *
299
- * Usage:
300
- * ```ts
301
- * class MyComposition extends Composition {
302
- * constructor() {
303
- * super();
304
- * const vpc = new aws.ec2.VPC(this, 'vpc', { ... });
305
- * const subnet = new aws.ec2.Subnet(this, 'subnet', {
306
- * spec: { forProvider: { vpcId: vpc.status.atProvider.vpcId } }
307
- * });
308
- * }
309
- * }
310
- * ```
458
+ * Get the current logger from async context.
459
+ * Returns a no-op logger if none has been set (silent by default).
311
460
  */
312
- declare class Composition extends Construct$1 {
313
- /**
314
- * Pending XR data, set by the framework before instantiation.
315
- * @internal
316
- */
317
- static _pendingXR: Record<string, unknown> | undefined;
318
- /**
319
- * Pending environment data, set by the framework before instantiation.
320
- * Populated from the Crossplane context key `apiextensions.crossplane.io/environment`.
321
- * @internal
322
- */
323
- static _pendingEnvironment: Record<string, unknown> | undefined;
324
- /** The composite resource (XR) — proxy-wrapped for tracking. */
325
- readonly xr: AnyFields;
326
- /** Environment data from function-environment-configs or other pipeline steps. */
327
- readonly environment: AnyFields;
328
- /** Raw name from the XR metadata (not proxy-tracked). */
329
- readonly xrName: string | undefined;
330
- /** Raw namespace from the XR metadata (not proxy-tracked). */
331
- readonly xrNamespace: string | undefined;
332
- /** Dependency collector shared across all resources. */
333
- readonly collector: DependencyCollector;
334
- /** Dependency graph built during compose(). */
335
- readonly graph: DependencyGraph;
336
- /** Registry of existing (read-only) resource references keyed by refKey. */
337
- private readonly _existingResources;
338
- /** Registered status output function. @internal */
339
- private _statusFn?;
340
- constructor();
341
- /**
342
- * Register a function that computes the desired XR status output.
343
- *
344
- * The function is called by the framework **after** observed state has been
345
- * fed into all resources, so `resource.observed` contains real data.
346
- *
347
- * @example
348
- * ```ts
349
- * this.setStatusOutput(() => ({
350
- * config: {
351
- * projectHostedZoneId: hostedZone.observed?.status?.atProvider?.id,
352
- * },
353
- * }));
354
- * ```
355
- */
356
- setStatusOutput(fn: () => Record<string, unknown>): void;
357
- /**
358
- * Compute and return the desired status output.
359
- * Returns an empty object if no status function was registered.
360
- * @internal
361
- */
362
- computeStatusOutput(): Record<string, unknown>;
363
- /**
364
- * Walk up the construct tree and return the root Composition.
365
- * Throws if the scope is not within a Composition.
366
- */
367
- static of(scope: Construct$1): Composition;
368
- /** Get all composed (non-existing) resources keyed by construct path. */
369
- get resources(): ReadonlyMap<string, Resource>;
370
- /** Get all existing (read-only) resource references keyed by refKey. */
371
- get existingResources(): ReadonlyMap<string, Resource>;
372
- }
461
+ declare function getLogger(): XplaneLogger;
462
+ /**
463
+ * Run a function with a logger available in async context.
464
+ * All code within `fn` (and any nested async calls) can access
465
+ * the logger via `getLogger()`.
466
+ */
467
+ declare function withLogger<T>(logger: XplaneLogger, fn: () => T): T;
373
468
  //#endregion
374
- //#region src/ready/auto-ready.d.ts
375
- /** Condition from a Kubernetes resource's status.conditions array. */
376
- interface StatusCondition {
377
- type: string;
378
- status: string;
379
- reason?: string;
380
- message?: string;
469
+ //#region src/pipeline/types.d.ts
470
+ interface PipelineState {
471
+ /** The fully-constructed Composition instance. */
472
+ composition: Composition;
473
+ /** All resources in the composition tree (including external). */
474
+ resources: Resource[];
475
+ /** The dependency graph. */
476
+ graph: DependencyGraph;
477
+ /** Observed composed resources from Crossplane (keyed by resource name). */
478
+ observedComposed: ReadonlyMap<string, Record<string, unknown>>;
479
+ /** Observed existing/required resources from Crossplane (keyed by refKey). */
480
+ observedRequired: ReadonlyMap<string, Record<string, unknown>>;
481
+ /** Classification of resources after sequencing. */
482
+ classification: Map<string, ResourceClassification>;
483
+ /** Diagnostics produced by the diagnose phase. */
484
+ diagnostics: DiagnosticReport[];
485
+ /** Emitted desired resources (final output). */
486
+ emitted: EmittedResource[];
487
+ /** Desired XR status patches (from this.xr.status assignments). */
488
+ xrStatusPatches: Record<string, unknown>;
489
+ }
490
+ type ResourceClassification = 'emit' | 'blocked' | 'external';
491
+ interface DiagnosticReport {
492
+ resource: string;
493
+ reason: 'pending' | 'cycle' | 'not-found';
494
+ pendingPaths?: Array<{
495
+ path: string;
496
+ waitingOn: {
497
+ resource: string;
498
+ path: string;
499
+ };
500
+ }>;
501
+ cycle?: string[];
502
+ detail?: string;
503
+ }
504
+ interface EmittedResource {
505
+ /** The construct path (used as resource name in Crossplane). */
506
+ name: string;
507
+ /** The desired Kubernetes resource document. */
508
+ document: Record<string, unknown>;
509
+ /** Whether autoReady was enabled for this resource. */
510
+ autoReady: boolean;
511
+ /** Custom readiness checks registered by the composition author. */
512
+ readyChecks: ReadyCheck[];
381
513
  }
514
+ //#endregion
515
+ //#region src/pipeline/diagnose.d.ts
516
+ /**
517
+ * DIAGNOSE phase: produce structured diagnostics for blocked resources.
518
+ *
519
+ * For each resource classified as 'blocked':
520
+ * - Find all Pending markers in the desired document
521
+ * - Report what they're waiting on (source resource + path)
522
+ *
523
+ * For circular dependencies (detected in sequence phase):
524
+ * - Produce a 'cycle' diagnostic with the full cycle path
525
+ */
526
+ declare function diagnose(state: PipelineState): PipelineState;
527
+ //#endregion
528
+ //#region src/pipeline/emit.d.ts
382
529
  /**
383
- * Determines if a Crossplane managed resource is ready based on its
384
- * observed status conditions.
530
+ * EMIT phase: serialize each resource classified as 'emit' into a plain
531
+ * Kubernetes resource document ready for Crossplane.
385
532
  *
386
- * - If the resource has a `Ready: True` condition → ready.
387
- * - If the resource has a `Ready: False` condition → not ready.
388
- * - If the resource exists but has no `Ready` condition at all (e.g. Namespace,
389
- * ProviderConfig) → considered ready (the resource exists and is functional).
390
- * - If not yet observed → not ready.
533
+ * Also extracts the XR desired status from this.xr.status assignments.
391
534
  */
392
- declare function isResourceReady(observed: KubernetesResource | undefined): boolean;
535
+ declare function emit(state: PipelineState): PipelineState;
536
+ //#endregion
537
+ //#region src/pipeline/hydrate.d.ts
393
538
  /**
394
- * Gets the Ready condition from a resource, if present.
539
+ * HYDRATE phase: feed observed state from Crossplane into each resource.
540
+ *
541
+ * - Composed resources are matched by their construct path (resource name).
542
+ * - External resources are matched by their refKey.
395
543
  */
396
- declare function getReadyCondition(observed: KubernetesResource | undefined): StatusCondition | undefined;
544
+ declare function hydrate(state: PipelineState): PipelineState;
397
545
  //#endregion
398
- //#region src/sequencing/resolver.d.ts
399
- /** Result of dependency resolution for a single resource. */
400
- interface ResolutionResult {
401
- /** The resource. */
402
- resource: Resource;
403
- /** Whether all dependencies are satisfied and the resource can be emitted. */
404
- ready: boolean;
405
- /** Paths that are still unresolved (waiting on upstream). */
406
- unresolvedPaths: string[];
407
- }
408
- /** Result of resolving all resources in a composition. */
409
- interface SequencingResult {
410
- /** Resources in dependency order that are ready to emit. */
411
- emit: Resource[];
412
- /** Resources blocked on unresolved dependencies. */
413
- blocked: Resource[];
414
- /** Topologically sorted resource IDs. */
415
- order: string[];
546
+ //#region src/pipeline/resolve.d.ts
547
+ /**
548
+ * RESOLVE phase: walk dependency edges and replace Pending markers
549
+ * with concrete values from observed state where available.
550
+ *
551
+ * For each resource's desired document, recursively find Pending values.
552
+ * Look up the source resource's observed state at the source path.
553
+ * If concrete replace. If not available → leave Pending in place.
554
+ */
555
+ declare function resolve(state: PipelineState): PipelineState;
556
+ //#endregion
557
+ //#region src/pipeline/sequence.d.ts
558
+ /**
559
+ * SEQUENCE phase: topological sort and classification.
560
+ *
561
+ * - Runs topological sort on the dependency graph.
562
+ * - Classifies each resource as 'emit', 'blocked', or 'external'.
563
+ * - A resource is 'blocked' if it still has Pending markers in its desired document.
564
+ * - External resources are classified as 'external' (never emitted).
565
+ * - Circular dependencies are detected via the graph's topological sort.
566
+ */
567
+ declare function sequence(state: PipelineState): PipelineState;
568
+ //#endregion
569
+ //#region src/pipeline/index.d.ts
570
+ /**
571
+ * Input to the pipeline — provided by the handler or simulator.
572
+ */
573
+ interface PipelineInput {
574
+ /** The constructed Composition instance. */
575
+ composition: Composition;
576
+ /** Observed composed resources from Crossplane (keyed by resource name). */
577
+ observedComposed: ReadonlyMap<string, Record<string, unknown>>;
578
+ /** Observed existing/required resources (keyed by refKey). */
579
+ observedRequired: ReadonlyMap<string, Record<string, unknown>>;
416
580
  }
417
581
  /**
418
- * Resolves resource dependencies and determines which resources can be
419
- * emitted in the current pass.
582
+ * Run the full rendering pipeline.
583
+ *
584
+ * Phases: hydrate → resolve → sequence → diagnose → emit
585
+ */
586
+ declare function runPipeline(input: PipelineInput): PipelineState;
587
+ //#endregion
588
+ //#region src/run.d.ts
589
+ /**
590
+ * Run a composition class with the given input and return a plain-data result.
591
+ *
592
+ * This is the single entry point that bridges the composition author's class
593
+ * with the runtime. It handles:
594
+ * 1. Setting up internal context (DependencyGraph, EdgeCollector, ALS)
595
+ * 2. Instantiating the Composition class
596
+ * 3. Running the full pipeline (hydrate → resolve → sequence → diagnose → emit)
597
+ * 4. Evaluating readiness per resource
598
+ * 5. Returning a fully serializable CompositionResult
420
599
  *
421
- * Algorithm:
422
- * 1. Topologically sort resources using the dependency graph.
423
- * 2. For each resource (in order), check if upstream dependencies have
424
- * resolved values in observed state.
425
- * 3. If all deps resolved → emit. If any dep unresolved → block.
600
+ * The runtime never needs to access framework internals — everything it needs
601
+ * is in the returned plain-data structure.
426
602
  */
427
- declare function resolveSequencing(resources: ReadonlyMap<string, Resource>, graph: DependencyGraph, observedResources: ReadonlyMap<string, KubernetesResource>): SequencingResult;
603
+ declare function runComposition<TSpec, TStatus, TContext extends object>(CompositionClass: new () => Composition<TSpec, TStatus, TContext>, input: CompositionInput): CompositionResult;
428
604
  //#endregion
429
- export { type AnyFields, Composition, Construct, DependencyCollector, type DependencyEdge, DependencyGraph, type ExistingResourceRef, IS_TRACKED, type KubernetesResource, type ResolutionResult, Resource, type ResourceOptions, type ResourceProps, type ResourceRef, type SequencingResult, TRACKING_META, type TrackingMeta, UNRESOLVED, computeRefKey, createTrackedProxy, getReadyCondition, getTrackingMeta, isResourceReady, isTracked, resolveSequencing };
605
+ export { Composition, type CompositionContext, type CompositionInput, type CompositionModule, type CompositionResult, Construct, DEFAULT_CHECKS, type DependencyEdge, DependencyGraph, type DesiredResource, type Diagnostic, type DiagnosticReport, EdgeCollector, type EmittedResource, type ExternalResourceRef, type ExternalResourceRequest, type KubernetesResource, Pending, type PipelineContextAccessor, type PipelineInput, type PipelineState, type ReadProxyMeta, type ReadyCheck, type ReadyCheckFn, Resource, type ResourceClassification, type ResourceConfig, type ResourceProps, type ResourceRef, type XplaneLogger, type XrProxy, compositionStorage, createPrimitiveReadProxy, createReadProxy, createWriteProxy, diagnose, emit, evaluateReadiness, getCompositionContext, getDesiredDocument, getExternalRef, getLogger, getObservedDocument, getReadProxyMeta, getReadyChecks, getResourceInternals, getResourceRef, getXrDesiredStatus, hydrate, hydrateObserved, isExternal, isReadProxy, resolve, runComposition, runPipeline, sequence, withLogger };
430
606
  //# sourceMappingURL=index.d.mts.map