@xplane/core 1.0.1 → 1.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.
package/dist/index.mjs CHANGED
@@ -46,6 +46,30 @@ var Composition = class extends Construct$1 {
46
46
  graph;
47
47
  /** The edge collector accumulating dependency edges. */
48
48
  collector;
49
+ /**
50
+ * When `true`, the runtime injects a structured `status.xplane` payload on
51
+ * the XR containing `emittedResources` and `blockedResources`. Defaults to
52
+ * `false` because writing this field requires the XRD's `openAPIV3Schema`
53
+ * to declare `status.xplane` (or `status` to allow unknown fields) — typed
54
+ * patches will otherwise be rejected by Crossplane.
55
+ *
56
+ * To enable, set in your composition's constructor:
57
+ *
58
+ * ```ts
59
+ * this.emitXplaneStatus = true;
60
+ * ```
61
+ *
62
+ * and add the following to your XRD's status schema:
63
+ *
64
+ * ```yaml
65
+ * status:
66
+ * properties:
67
+ * xplane:
68
+ * type: object
69
+ * x-kubernetes-preserve-unknown-fields: true
70
+ * ```
71
+ */
72
+ emitXplaneStatus = false;
49
73
  constructor() {
50
74
  super(void 0, "Composition");
51
75
  const ctx = getCompositionContext();
@@ -219,6 +243,114 @@ var DependencyGraph = class {
219
243
  }
220
244
  };
221
245
  //#endregion
246
+ //#region src/tracking/types.ts
247
+ /**
248
+ * A Pending marker stored in a desired document when a ReadProxy value
249
+ * is assigned. Carries full source info so the resolve phase knows
250
+ * where to look for the concrete value.
251
+ */
252
+ const PENDING_TAG = Symbol.for("xplane.pending");
253
+ var Pending = class {
254
+ source;
255
+ path;
256
+ static TAG = PENDING_TAG;
257
+ [PENDING_TAG] = true;
258
+ constructor(source, path) {
259
+ this.source = source;
260
+ this.path = path;
261
+ }
262
+ static is(value) {
263
+ return typeof value === "object" && value !== null && value[PENDING_TAG] === true;
264
+ }
265
+ };
266
+ /**
267
+ * A Pending-like marker for strings produced by template literals that
268
+ * interpolate one or more unresolved ReadProxy values.
269
+ *
270
+ * Holds the template structure so the resolve phase can reconstruct the
271
+ * final string once all dependency slots are available.
272
+ *
273
+ * Invariant: parts.length === slots.length + 1
274
+ * result = parts[0] + resolved[0] + parts[1] + … + resolved[n-1] + parts[n]
275
+ */
276
+ const PENDING_TEMPLATE_TAG = Symbol.for("xplane.pendingTemplate");
277
+ var PendingTemplate = class {
278
+ parts;
279
+ slots;
280
+ static TAG = PENDING_TEMPLATE_TAG;
281
+ [PENDING_TEMPLATE_TAG] = true;
282
+ constructor(parts, slots) {
283
+ this.parts = parts;
284
+ this.slots = slots;
285
+ }
286
+ static is(value) {
287
+ return typeof value === "object" && value !== null && value[PENDING_TEMPLATE_TAG] === true;
288
+ }
289
+ };
290
+ //#endregion
291
+ //#region src/tracking/token-registry.ts
292
+ /**
293
+ * Per-run AsyncLocalStorage for the template-literal token registry.
294
+ * Kept separate from compositionStorage (core/) to avoid circular deps.
295
+ */
296
+ const tokenRegistryStorage = new AsyncLocalStorage();
297
+ function createTokenRegistry() {
298
+ return {
299
+ byToken: /* @__PURE__ */ new Map(),
300
+ byKey: /* @__PURE__ */ new Map(),
301
+ counter: 0
302
+ };
303
+ }
304
+ /**
305
+ * Get or create a stable token for a given (owner, path) pair.
306
+ * Returns null when no registry is active (outside of a composition run).
307
+ */
308
+ function getOrCreateToken(owner, path) {
309
+ const registry = tokenRegistryStorage.getStore();
310
+ if (!registry) return null;
311
+ const key = `${owner.id}\0${path}`;
312
+ const existing = registry.byKey.get(key);
313
+ if (existing !== void 0) return existing;
314
+ const token = `__pending__tpl_${registry.counter++}__`;
315
+ registry.byToken.set(token, {
316
+ owner,
317
+ path
318
+ });
319
+ registry.byKey.set(key, token);
320
+ return token;
321
+ }
322
+ function lookupToken(token) {
323
+ return tokenRegistryStorage.getStore()?.byToken.get(token);
324
+ }
325
+ const TEMPLATE_TOKEN_RE = /__pending__tpl_\d+__/g;
326
+ /**
327
+ * Scan a string for pending template tokens. If any are found, calls
328
+ * `onSlot` for each registered token and returns a PendingTemplate.
329
+ * Returns the original string if no tokens are found or none are registered.
330
+ */
331
+ function processStringValue(value, onSlot) {
332
+ const parts = [];
333
+ const slots = [];
334
+ let lastIndex = 0;
335
+ let hasSlots = false;
336
+ TEMPLATE_TOKEN_RE.lastIndex = 0;
337
+ for (const match of value.matchAll(TEMPLATE_TOKEN_RE)) {
338
+ const meta = lookupToken(match[0]);
339
+ if (!meta) continue;
340
+ hasSlots = true;
341
+ parts.push(value.slice(lastIndex, match.index));
342
+ slots.push({
343
+ source: meta.owner,
344
+ path: meta.path
345
+ });
346
+ onSlot(meta);
347
+ lastIndex = match.index + match[0].length;
348
+ }
349
+ if (!hasSlots) return value;
350
+ parts.push(value.slice(lastIndex));
351
+ return new PendingTemplate(parts, slots);
352
+ }
353
+ //#endregion
222
354
  //#region src/tracking/read-proxy.ts
223
355
  /**
224
356
  * WeakMap storing metadata for ReadProxy instances.
@@ -277,13 +409,15 @@ function createReadProxy(target, owner, basePath) {
277
409
  * coerced to a primitive.
278
410
  */
279
411
  function createLeafReadProxy(owner, path) {
280
- const proxy = new Proxy(Object.create(null), {
412
+ const target = Object.create(null);
413
+ const getToken = () => getOrCreateToken(owner, path) ?? `__pending__${owner.id}__${path}`;
414
+ const proxy = new Proxy(target, {
281
415
  get(_obj, prop) {
282
416
  if (prop === READ_PROXY_TAG) return true;
283
- if (prop === Symbol.toPrimitive) return () => `__pending__${owner.id}__${path}`;
417
+ if (prop === Symbol.toPrimitive) return () => getToken();
284
418
  if (typeof prop === "symbol") return void 0;
285
419
  if (prop === "toJSON") return () => void 0;
286
- if (prop === "toString") return () => `__pending__${owner.id}__${path}`;
420
+ if (prop === "toString") return () => getToken();
287
421
  if (prop === "valueOf") return () => proxy;
288
422
  return createLeafReadProxy(owner, `${path}.${String(prop)}`);
289
423
  },
@@ -326,27 +460,6 @@ function createPrimitiveReadProxy(value, owner, path) {
326
460
  return proxy;
327
461
  }
328
462
  //#endregion
329
- //#region src/tracking/types.ts
330
- /**
331
- * A Pending marker stored in a desired document when a ReadProxy value
332
- * is assigned. Carries full source info so the resolve phase knows
333
- * where to look for the concrete value.
334
- */
335
- const PENDING_TAG = Symbol.for("xplane.pending");
336
- var Pending = class {
337
- source;
338
- path;
339
- static TAG = PENDING_TAG;
340
- [PENDING_TAG] = true;
341
- constructor(source, path) {
342
- this.source = source;
343
- this.path = path;
344
- }
345
- static is(value) {
346
- return typeof value === "object" && value !== null && value[PENDING_TAG] === true;
347
- }
348
- };
349
- //#endregion
350
463
  //#region src/tracking/write-proxy.ts
351
464
  /**
352
465
  * Collector that accumulates dependency edges discovered during
@@ -422,6 +535,17 @@ function createWriteProxy(target, opts) {
422
535
  return Reflect.set(obj, prop, new Pending(meta.owner, meta.path));
423
536
  }
424
537
  }
538
+ if (typeof value === "string") {
539
+ const processed = processStringValue(value, (meta) => {
540
+ collector.add({
541
+ from: meta.owner,
542
+ fromPath: meta.path,
543
+ to: owner,
544
+ toPath: targetPath
545
+ });
546
+ });
547
+ return Reflect.set(obj, prop, processed);
548
+ }
425
549
  if (typeof value === "object" && value !== null && !Pending.is(value)) {
426
550
  const processed = deepProcessValue(value, owner, targetPath, collector);
427
551
  return Reflect.set(obj, prop, processed);
@@ -453,6 +577,14 @@ function tryExtractPrimitive$2(proxy) {
453
577
  */
454
578
  function deepProcessValue(value, owner, basePath, collector) {
455
579
  if (value === null || value === void 0) return value;
580
+ if (typeof value === "string") return processStringValue(value, (meta) => {
581
+ collector.add({
582
+ from: meta.owner,
583
+ fromPath: meta.path,
584
+ to: owner,
585
+ toPath: basePath
586
+ });
587
+ });
456
588
  if (typeof value !== "object") return value;
457
589
  if (isReadProxy(value)) {
458
590
  const meta = getReadProxyMeta(value);
@@ -517,8 +649,14 @@ var Resource = class Resource extends Construct$1 {
517
649
  collector
518
650
  };
519
651
  internals.set(this, internal);
652
+ const ctx = compositionStorage.getStore();
653
+ if (ctx) {
654
+ const observed = ctx.observedComposed.get(this.node.path);
655
+ if (observed) Object.assign(internal.observed, observed);
656
+ }
520
657
  const proxy = createResourceProxy(this, internal);
521
658
  scope.node._children[this.node.id] = proxy;
659
+ this.node.host = proxy;
522
660
  return proxy;
523
661
  }
524
662
  /**
@@ -581,6 +719,33 @@ var Resource = class Resource extends Construct$1 {
581
719
  if (withHash.length <= maxLength) return withHash;
582
720
  return `${full.slice(0, maxLength - hash.length - separator.length)}${separator}${hash}`;
583
721
  }
722
+ /**
723
+ * Like {@link uniqueName} but produces names compliant with RFC 1123 DNS labels:
724
+ * lowercase alphanumeric characters and hyphens only, starting and ending with
725
+ * an alphanumeric character. Suitable for use as Kubernetes resource names.
726
+ */
727
+ static uniqueNameRfc1123(scope, options = {}) {
728
+ const maxLength = options.maxLength ?? 63;
729
+ const clean = (s) => s.toLowerCase().replace(/\s+/g, "").replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
730
+ const xrMeta = scope.node.tryGetContext("xplane:xr-meta");
731
+ const parts = [];
732
+ if (xrMeta?.namespace) parts.push(clean(xrMeta.namespace));
733
+ if (xrMeta?.name) parts.push(clean(xrMeta.name));
734
+ for (const s of scope.node.scopes.slice(1)) {
735
+ const c = clean(s.node.id);
736
+ if (c) parts.push(c);
737
+ }
738
+ if (options.extra) {
739
+ const c = clean(options.extra);
740
+ if (c) parts.push(c);
741
+ }
742
+ const full = parts.join("-");
743
+ const hash = shortHash(full);
744
+ const withHash = `${full}-${hash}`;
745
+ if (withHash.length <= maxLength) return withHash;
746
+ const trimmedPrefix = full.slice(0, maxLength - hash.length - 1).replace(/-+$/, "");
747
+ return trimmedPrefix ? `${trimmedPrefix}-${hash}` : hash;
748
+ }
584
749
  };
585
750
  function getResourceInternals(resource) {
586
751
  const internal = internals.get(resource);
@@ -642,6 +807,14 @@ function processDesiredProps(props, owner, collector) {
642
807
  }
643
808
  function processValue(value, owner, path, collector) {
644
809
  if (value === null || value === void 0) return value;
810
+ if (typeof value === "string") return processStringValue(value, (meta) => {
811
+ collector.add({
812
+ from: meta.owner,
813
+ fromPath: meta.path,
814
+ to: owner,
815
+ toPath: path
816
+ });
817
+ });
645
818
  if (typeof value !== "object") return value;
646
819
  if (isReadProxy(value)) {
647
820
  const meta = getReadProxyMeta(value);
@@ -805,12 +978,12 @@ function diagnose(state) {
805
978
  if (!ref) continue;
806
979
  const observed = getObservedDocument(resource);
807
980
  if (Object.keys(observed).length > 0) continue;
808
- const nameDisplay = typeof ref.name === "string" ? ref.name : ref.name == null ? "(unresolved)" : "(unresolved)";
981
+ if (typeof ref.name !== "string" || ref.name.startsWith("__pending__")) continue;
809
982
  const nsDisplay = ref.namespace ? ` in namespace '${ref.namespace}'` : "";
810
983
  diagnostics.push({
811
984
  resource: getResourceRef(resource).id,
812
985
  reason: "not-found",
813
- detail: `External resource ${ref.apiVersion}/${ref.kind} '${nameDisplay}'${nsDisplay} was required but not found by Crossplane`
986
+ detail: `External resource ${ref.apiVersion}/${ref.kind} '${ref.name}'${nsDisplay} was required but not found by Crossplane`
814
987
  });
815
988
  }
816
989
  const pendingDiagnostics = [];
@@ -874,22 +1047,39 @@ function findPendingPaths(obj, basePath) {
874
1047
  * Kubernetes resource document ready for Crossplane.
875
1048
  *
876
1049
  * Also extracts the XR desired status from this.xr.status assignments.
1050
+ *
1051
+ * For resources classified as 'blocked' that have previously-observed state,
1052
+ * emits the observed document as-is (marked `preserved: true`) so that
1053
+ * Crossplane does not delete them while their dependencies are still resolving.
877
1054
  */
878
1055
  function emit(state) {
879
1056
  const emitted = [];
880
1057
  for (const resource of state.resources) {
881
1058
  if (isExternal(resource)) continue;
882
1059
  const ref = getResourceRef(resource);
883
- if (state.classification.get(ref.id) !== "emit") continue;
884
- const internal = getResourceInternals(resource);
885
- const desired = getDesiredDocument(resource);
886
- const name = ref.id.startsWith("Composition/") ? ref.id.slice(12) : ref.id;
887
- emitted.push({
888
- name,
889
- document: deepClean(desired),
890
- autoReady: internal.config.autoReady,
891
- readyChecks: getReadyChecks(resource)
892
- });
1060
+ const classification = state.classification.get(ref.id);
1061
+ if (classification === "emit") {
1062
+ const internal = getResourceInternals(resource);
1063
+ const desired = getDesiredDocument(resource);
1064
+ const name = ref.id.startsWith("Composition/") ? ref.id.slice(12) : ref.id;
1065
+ emitted.push({
1066
+ name,
1067
+ document: deepClean(desired),
1068
+ autoReady: internal.config.autoReady,
1069
+ readyChecks: getReadyChecks(resource)
1070
+ });
1071
+ } else if (classification === "blocked") {
1072
+ const observed = getObservedDocument(resource);
1073
+ if (Object.keys(observed).length === 0) continue;
1074
+ const name = ref.id.startsWith("Composition/") ? ref.id.slice(12) : ref.id;
1075
+ emitted.push({
1076
+ name,
1077
+ document: stripServerFields(deepClean(observed)),
1078
+ autoReady: false,
1079
+ readyChecks: [],
1080
+ preserved: true
1081
+ });
1082
+ }
893
1083
  }
894
1084
  const resourceById = new Map(state.resources.map((r) => [getResourceRef(r).id, r]));
895
1085
  const xrStatusPatches = resolveXrStatus(getXrDesiredStatus(state.composition), resourceById);
@@ -911,12 +1101,43 @@ function deepClean(obj) {
911
1101
  function cleanValue(value) {
912
1102
  if (value === null || value === void 0) return value;
913
1103
  if (typeof value !== "object") return value;
1104
+ if (PendingTemplate.is(value)) throw new Error(`PendingTemplate reached emit phase — resource should have been classified as blocked. Parts: ${JSON.stringify(value.parts)}`);
914
1105
  if (Array.isArray(value)) return value.map(cleanValue);
915
1106
  const result = {};
916
1107
  for (const [key, val] of Object.entries(value)) result[key] = cleanValue(val);
917
1108
  return result;
918
1109
  }
919
1110
  /**
1111
+ * Strip server-managed Kubernetes fields from an observed document before
1112
+ * emitting it as a preserved desired resource.
1113
+ *
1114
+ * These fields are set by the API server / controllers and must not appear
1115
+ * in the desired state — including them causes Crossplane to try propagating
1116
+ * them (e.g. `uid`) into XR resourceRefs fields that the XRD schema may not
1117
+ * declare, resulting in a typed-patch validation failure.
1118
+ */
1119
+ const SERVER_MANAGED_METADATA = new Set([
1120
+ "uid",
1121
+ "resourceVersion",
1122
+ "generation",
1123
+ "creationTimestamp",
1124
+ "deletionTimestamp",
1125
+ "deletionGracePeriodSeconds",
1126
+ "managedFields",
1127
+ "ownerReferences",
1128
+ "selfLink"
1129
+ ]);
1130
+ function stripServerFields(doc) {
1131
+ const result = { ...doc };
1132
+ delete result.status;
1133
+ if (result.metadata && typeof result.metadata === "object") {
1134
+ const meta = { ...result.metadata };
1135
+ for (const field of SERVER_MANAGED_METADATA) delete meta[field];
1136
+ result.metadata = meta;
1137
+ }
1138
+ return result;
1139
+ }
1140
+ /**
920
1141
  * Resolve ReadProxy values in XR status using observed resource data.
921
1142
  * This is needed because XR status is written at construction time (before hydration),
922
1143
  * so read proxy references need to be resolved post-hydration.
@@ -1017,6 +1238,9 @@ function resolvePending(obj, resourceById) {
1017
1238
  const resolved = getNestedValue(getObservedDocument(sourceResource), value.path);
1018
1239
  if (resolved !== void 0) obj[key] = resolved;
1019
1240
  }
1241
+ } else if (PendingTemplate.is(value)) {
1242
+ const resolved = resolvePendingTemplate(value, resourceById);
1243
+ if (resolved !== void 0) obj[key] = resolved;
1020
1244
  } else if (Array.isArray(value)) resolveArray(value, resourceById);
1021
1245
  else if (value !== null && typeof value === "object") resolvePending(value, resourceById);
1022
1246
  }
@@ -1029,6 +1253,9 @@ function resolveArray(arr, resourceById) {
1029
1253
  const resolved = getNestedValue(getObservedDocument(sourceResource), value.path);
1030
1254
  if (resolved !== void 0) arr[i] = resolved;
1031
1255
  }
1256
+ } else if (PendingTemplate.is(value)) {
1257
+ const resolved = resolvePendingTemplate(value, resourceById);
1258
+ if (resolved !== void 0) arr[i] = resolved;
1032
1259
  } else if (Array.isArray(value)) resolveArray(value, resourceById);
1033
1260
  else if (value !== null && typeof value === "object") resolvePending(value, resourceById);
1034
1261
  }
@@ -1046,6 +1273,23 @@ function getNestedValue(obj, path) {
1046
1273
  }
1047
1274
  return current;
1048
1275
  }
1276
+ /**
1277
+ * Attempt to resolve all slots of a PendingTemplate.
1278
+ * Returns the reconstructed string if all slots resolve, undefined otherwise.
1279
+ */
1280
+ function resolvePendingTemplate(template, resourceById) {
1281
+ const values = [];
1282
+ for (const slot of template.slots) {
1283
+ const sourceResource = resourceById.get(slot.source.id);
1284
+ if (!sourceResource) return void 0;
1285
+ const resolved = getNestedValue(getObservedDocument(sourceResource), slot.path);
1286
+ if (resolved === void 0 || resolved === null) return void 0;
1287
+ values.push(String(resolved));
1288
+ }
1289
+ let result = template.parts[0];
1290
+ for (let i = 0; i < values.length; i++) result += values[i] + template.parts[i + 1];
1291
+ return result;
1292
+ }
1049
1293
  //#endregion
1050
1294
  //#region src/pipeline/sequence.ts
1051
1295
  /**
@@ -1083,6 +1327,7 @@ function sequence(state) {
1083
1327
  function containsPending(obj) {
1084
1328
  if (obj === null || obj === void 0) return false;
1085
1329
  if (Pending.is(obj)) return true;
1330
+ if (PendingTemplate.is(obj)) return true;
1086
1331
  if (typeof obj !== "object") return false;
1087
1332
  if (Array.isArray(obj)) return obj.some(containsPending);
1088
1333
  for (const value of Object.values(obj)) if (containsPending(value)) return true;
@@ -1267,15 +1512,23 @@ function runComposition(CompositionClass, input) {
1267
1512
  xr: input.xr,
1268
1513
  pipelineContext,
1269
1514
  requiredResources: observedRequired,
1515
+ observedComposed,
1270
1516
  graph,
1271
1517
  collector
1272
1518
  };
1519
+ const composition = compositionStorage.run(ctx, () => tokenRegistryStorage.run(createTokenRegistry(), () => new CompositionClass()));
1273
1520
  const state = runPipeline({
1274
- composition: compositionStorage.run(ctx, () => new CompositionClass()),
1521
+ composition,
1275
1522
  observedComposed,
1276
1523
  observedRequired
1277
1524
  });
1278
1525
  const resources = state.emitted.map((emitted) => {
1526
+ if (emitted.preserved) return {
1527
+ name: emitted.name,
1528
+ document: emitted.document,
1529
+ ready: false,
1530
+ preserved: true
1531
+ };
1279
1532
  const allChecks = [...emitted.readyChecks, ...DEFAULT_CHECKS];
1280
1533
  const observed = observedComposed.get(`Composition/${emitted.name}`);
1281
1534
  const ready = emitted.autoReady ? evaluateReadiness(allChecks, observed) : true;
@@ -1299,14 +1552,51 @@ function runComposition(CompositionClass, input) {
1299
1552
  ...ref.namespace ? { namespace: ref.namespace } : {}
1300
1553
  });
1301
1554
  }
1555
+ const blockedResources = [];
1556
+ for (const resource of state.resources) {
1557
+ if (isExternal(resource)) continue;
1558
+ const ref = getResourceRef(resource);
1559
+ if (state.classification.get(ref.id) !== "blocked") continue;
1560
+ const name = ref.id.startsWith("Composition/") ? ref.id.slice(12) : ref.id;
1561
+ const desired = getDesiredDocument(resource);
1562
+ const apiVersion = typeof desired.apiVersion === "string" ? desired.apiVersion : "";
1563
+ const kind = typeof desired.kind === "string" ? desired.kind : "";
1564
+ const metadata = desired.metadata && typeof desired.metadata === "object" ? desired.metadata : void 0;
1565
+ const resourceName = metadata && typeof metadata.name === "string" ? metadata.name : void 0;
1566
+ const waitingFor = describeWaitingFor(name, state.diagnostics);
1567
+ blockedResources.push({
1568
+ name,
1569
+ apiVersion,
1570
+ kind,
1571
+ ...resourceName ? { resourceName } : {},
1572
+ ...waitingFor && waitingFor.length > 0 ? { waitingFor } : {}
1573
+ });
1574
+ }
1302
1575
  return {
1303
1576
  resources,
1577
+ blockedResources,
1304
1578
  externalResources,
1305
1579
  xrStatus: state.xrStatusPatches,
1306
- diagnostics: state.diagnostics
1580
+ diagnostics: state.diagnostics,
1581
+ emitXplaneStatus: composition.emitXplaneStatus === true
1307
1582
  };
1308
1583
  }
1584
+ /**
1585
+ * Build a human-readable `waitingFor` list for a blocked resource from the
1586
+ * matching diagnostic. Each entry describes one thing the resource is waiting
1587
+ * on (one entry per pending path, or a single entry for cycle/not-found).
1588
+ */
1589
+ function describeWaitingFor(name, diagnostics) {
1590
+ const id = `Composition/${name}`;
1591
+ const diag = diagnostics.find((d) => d.resource === id || d.resource === name);
1592
+ if (!diag) return void 0;
1593
+ if (diag.reason === "cycle") return [`circular dependency: ${(diag.cycle ?? []).join(" → ")}`];
1594
+ if (diag.reason === "not-found") return [diag.detail ?? "external resource not found"];
1595
+ if (diag.pendingPaths && diag.pendingPaths.length > 0) return diag.pendingPaths.map((p) => {
1596
+ return `${p.waitingOn.resource.startsWith("Composition/") ? p.waitingOn.resource.slice(12) : p.waitingOn.resource}.${p.waitingOn.path}`;
1597
+ });
1598
+ }
1309
1599
  //#endregion
1310
- export { Composition, Construct, DEFAULT_CHECKS, DependencyGraph, EdgeCollector, Pending, Resource, 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 };
1600
+ export { Composition, Construct, DEFAULT_CHECKS, DependencyGraph, EdgeCollector, Pending, PendingTemplate, Resource, compositionStorage, createPrimitiveReadProxy, createReadProxy, createTokenRegistry, createWriteProxy, diagnose, emit, evaluateReadiness, getCompositionContext, getDesiredDocument, getExternalRef, getLogger, getObservedDocument, getReadProxyMeta, getReadyChecks, getResourceInternals, getResourceRef, getXrDesiredStatus, hydrate, hydrateObserved, isExternal, isReadProxy, resolve, runComposition, runPipeline, sequence, tokenRegistryStorage, withLogger };
1311
1601
 
1312
1602
  //# sourceMappingURL=index.mjs.map