@holonograph/client 0.4.0 → 0.6.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.js CHANGED
@@ -4010,6 +4010,7 @@ var ALL_SUBSTRATE_COLUMN_SEMANTICS = ["deployment-state", "diagnostic"];
4010
4010
  var SubstrateColumnSemanticSchema = external_exports.enum(ALL_SUBSTRATE_COLUMN_SEMANTICS);
4011
4011
  var ALL_EMISSION_EXPECTATIONS = ["always", "conditional"];
4012
4012
  var EmissionExpectationSchema = external_exports.enum(ALL_EMISSION_EXPECTATIONS);
4013
+ var SubstrateColumnStatusSchema = external_exports.enum(["active", "forward-declared", "deprecated"]);
4013
4014
  var SubstrateColumnSchema = external_exports.object({
4014
4015
  id: external_exports.string().min(1).max(128),
4015
4016
  control: ControlAttributeSchema,
@@ -4020,7 +4021,9 @@ var SubstrateColumnSchema = external_exports.object({
4020
4021
 
4021
4022
  values: external_exports.array(external_exports.string().min(1)).min(1).optional(),
4022
4023
 
4023
- emissionExpectation: EmissionExpectationSchema.optional()
4024
+ emissionExpectation: EmissionExpectationSchema.optional(),
4025
+
4026
+ status: SubstrateColumnStatusSchema.optional()
4024
4027
  });
4025
4028
  var SubstrateSnapshotSchema = external_exports.object({
4026
4029
  lensVersion: LensVersionIdSchema,
@@ -4064,7 +4067,9 @@ var SYSTEM_ROLE_SURFACE_IDS = {
4064
4067
 
4065
4068
  conformanceClerk: "system:conformance-clerk",
4066
4069
 
4067
- blastRadiusJudge: "system:blast-radius-judging"
4070
+ blastRadiusJudge: "system:blast-radius-judging",
4071
+
4072
+ orthogonalityHypothesisDrafter: "system:orthogonality-hypothesis-drafting"
4068
4073
  };
4069
4074
  var PLATFORM_SYSTEM_ROLE_SURFACES = [
4070
4075
  {
@@ -4081,12 +4086,45 @@ var PLATFORM_SYSTEM_ROLE_SURFACES = [
4081
4086
  role: "judge",
4082
4087
  surfaceId: SYSTEM_ROLE_SURFACE_IDS.blastRadiusJudge,
4083
4088
  label: "blast-radius-judge"
4089
+ },
4090
+ {
4091
+ role: "clerk",
4092
+ surfaceId: SYSTEM_ROLE_SURFACE_IDS.orthogonalityHypothesisDrafter,
4093
+ label: "orthogonality-hypothesis-drafter"
4084
4094
  }
4085
4095
  ];
4086
4096
  new Set(
4087
4097
  PLATFORM_SYSTEM_ROLE_SURFACES.map((s) => s.surfaceId)
4088
4098
  );
4089
4099
 
4100
+ var JUDGE_RUBRIC_HASH_COLUMN_ID = "judge.rubric_hash";
4101
+ var JUDGE_CONTEXT_SCOPE_COLUMN_ID = "judge.context_scope";
4102
+ var JUDGE_MODEL_COLUMN_ID = "judge.model";
4103
+ var JUDGE_RESERVED_COLUMN_NAMESPACE_PREFIX = "judge.";
4104
+ var JUDGE_CONTEXT_SCOPES = [
4105
+ "rubric_actual",
4106
+ "rubric_actual_expected",
4107
+ "rubric_actual_expected_toolaware",
4108
+ "full_generating_prompt"
4109
+ ];
4110
+ var JudgeContextScopeSchema = external_exports.enum(JUDGE_CONTEXT_SCOPES);
4111
+ external_exports.object({
4112
+
4113
+ rubricHash: external_exports.string().min(1).optional(),
4114
+
4115
+ contextScope: JudgeContextScopeSchema.optional(),
4116
+
4117
+ model: external_exports.string().min(1).optional()
4118
+ });
4119
+ function judgeIdentityColumns(identity) {
4120
+ const columns = {};
4121
+ if (identity.rubricHash !== void 0) columns[JUDGE_RUBRIC_HASH_COLUMN_ID] = identity.rubricHash;
4122
+ if (identity.contextScope !== void 0)
4123
+ columns[JUDGE_CONTEXT_SCOPE_COLUMN_ID] = identity.contextScope;
4124
+ if (identity.model !== void 0) columns[JUDGE_MODEL_COLUMN_ID] = identity.model;
4125
+ return columns;
4126
+ }
4127
+
4090
4128
  var CannedMessageRefSchema = external_exports.string().min(1).max(256);
4091
4129
  var ALL_GATE_FAILURE_ACTIONS = ["retry", "flag", "block"];
4092
4130
  var GateFailureActionSchema = external_exports.enum(ALL_GATE_FAILURE_ACTIONS);
@@ -4206,6 +4244,8 @@ var SurfaceDeclarationSchema = external_exports.object({
4206
4244
 
4207
4245
  status: SurfaceStatusSchema.default("active"),
4208
4246
 
4247
+ captureJudgedFailures: external_exports.boolean().optional(),
4248
+
4209
4249
  removedDimensions: external_exports.array(RemovedDimensionSchema).default([]),
4210
4250
 
4211
4251
  removedCohortTags: external_exports.array(RemovedCohortTagSchema).default([])
@@ -4250,6 +4290,7 @@ external_exports.object({
4250
4290
 
4251
4291
  systemSurfaces: SystemSurfacesSchema.optional()
4252
4292
  });
4293
+ var SERVED_MODEL_COLUMN_ID = "light_source_served_model";
4253
4294
 
4254
4295
  var LightSourceRoleSchema = external_exports.enum(["system", "user", "assistant", "tool"]);
4255
4296
  var LightSourceContentBlockSchema = external_exports.discriminatedUnion("kind", [
@@ -4353,6 +4394,8 @@ external_exports.discriminatedUnion("ok", [
4353
4394
 
4354
4395
  vendorMetadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
4355
4396
 
4397
+ servedModel: external_exports.string().optional(),
4398
+
4356
4399
  stopReason: external_exports.enum(["end_turn", "max_tokens", "tool_use", "stop_sequence", "refusal", "other"]).optional()
4357
4400
  }),
4358
4401
  external_exports.object({
@@ -4691,6 +4734,8 @@ var LightSnapshotSchema = external_exports.object({
4691
4734
 
4692
4735
  vendorMetadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
4693
4736
 
4737
+ servedModel: external_exports.string().optional(),
4738
+
4694
4739
  rawRequestOmitted: external_exports.boolean().optional(),
4695
4740
 
4696
4741
  rawResponsePresence: external_exports.enum(["present", "omitted-by-policy", "pending-attach", "failed-attach"]).optional()
@@ -4771,7 +4816,9 @@ var EvaluationEventKindSchema = external_exports.enum([
4771
4816
 
4772
4817
  "analysis.emission-coverage",
4773
4818
 
4774
- "analysis.delivery-orphan"
4819
+ "analysis.delivery-orphan",
4820
+
4821
+ "analysis.orthogonality-hypothesis"
4775
4822
  ]);
4776
4823
  var AvailabilityTransitionDirectionSchema = external_exports.enum([
4777
4824
  "reachable->unreachable",
@@ -4947,6 +4994,29 @@ var ConformanceFixtureRecordSchema = external_exports.object({
4947
4994
 
4948
4995
  sourceSurfaceId: external_exports.string().min(1).optional()
4949
4996
  });
4997
+ var OrthogonalityHypothesisRecordSchema = external_exports.object({
4998
+
4999
+ draftId: external_exports.string().min(1),
5000
+
5001
+ targetSurfaceId: external_exports.string().min(1),
5002
+
5003
+ targetDimensionId: external_exports.string().min(1),
5004
+
5005
+ candidateColumnId: external_exports.string().min(1),
5006
+
5007
+ proposedHypothesis: external_exports.string().min(1),
5008
+
5009
+ evidence: external_exports.object({
5010
+ scoredEventCount: external_exports.number().int().nonnegative(),
5011
+ sigmaObservedSquared: external_exports.number().nonnegative(),
5012
+ sigmaAgentSquared: external_exports.number().nonnegative(),
5013
+ sigmaAgentCILower: external_exports.number().nonnegative()
5014
+ }),
5015
+
5016
+ approverId: external_exports.string().min(1),
5017
+
5018
+ occurredAt: external_exports.string().min(1)
5019
+ });
4950
5020
  var EmissionCoverageSchema = external_exports.object({
4951
5021
 
4952
5022
  streamId: external_exports.string().min(1),
@@ -5005,37 +5075,42 @@ var EvaluationEventSchema = external_exports.object({
5005
5075
 
5006
5076
  emissionCoverage: EmissionCoverageSchema.optional(),
5007
5077
 
5008
- deliveryOrphan: DeliveryOrphanSchema.optional()
5078
+ deliveryOrphan: DeliveryOrphanSchema.optional(),
5079
+
5080
+ orthogonalityHypothesis: OrthogonalityHypothesisRecordSchema.optional()
5009
5081
  }).refine(
5010
5082
  (e) => {
5011
5083
  if (e.kind === "evaluation") {
5012
- return e.light !== void 0 && e.substrate !== void 0 && e.surface !== void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0;
5084
+ return e.light !== void 0 && e.substrate !== void 0 && e.surface !== void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0 && e.orthogonalityHypothesis === void 0;
5013
5085
  }
5014
5086
  if (e.kind === "availability.transition") {
5015
- return e.availability !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0;
5087
+ return e.availability !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0 && e.orthogonalityHypothesis === void 0;
5016
5088
  }
5017
5089
  if (e.kind === "analysis.noise-floor-calibration") {
5018
- return e.noiseFloorCalibration !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0;
5090
+ return e.noiseFloorCalibration !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0 && e.orthogonalityHypothesis === void 0;
5019
5091
  }
5020
5092
  if (e.kind === "analysis.variance-decomposition") {
5021
- return e.varianceDecomposition !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0;
5093
+ return e.varianceDecomposition !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0 && e.orthogonalityHypothesis === void 0;
5022
5094
  }
5023
5095
  if (e.kind === "analysis.lifecycle-transition") {
5024
- return e.lifecycleTransition !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0;
5096
+ return e.lifecycleTransition !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0 && e.orthogonalityHypothesis === void 0;
5025
5097
  }
5026
5098
  if (e.kind === "analysis.eval-recommendation") {
5027
- return e.evalRecommendation !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0;
5099
+ return e.evalRecommendation !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.conformanceFixtureRecord === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0 && e.orthogonalityHypothesis === void 0;
5028
5100
  }
5029
5101
  if (e.kind === "analysis.conformance-fixture") {
5030
- return e.conformanceFixtureRecord !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0;
5102
+ return e.conformanceFixtureRecord !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.emissionCoverage === void 0 && e.deliveryOrphan === void 0 && e.orthogonalityHypothesis === void 0;
5031
5103
  }
5032
5104
  if (e.kind === "analysis.emission-coverage") {
5033
- return e.emissionCoverage !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.deliveryOrphan === void 0;
5105
+ return e.emissionCoverage !== void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.deliveryOrphan === void 0 && e.orthogonalityHypothesis === void 0;
5106
+ }
5107
+ if (e.kind === "analysis.delivery-orphan") {
5108
+ return e.deliveryOrphan !== void 0 && e.emissionCoverage === void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0 && e.orthogonalityHypothesis === void 0;
5034
5109
  }
5035
- return e.deliveryOrphan !== void 0 && e.emissionCoverage === void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0;
5110
+ return e.orthogonalityHypothesis !== void 0 && e.deliveryOrphan === void 0 && e.emissionCoverage === void 0 && e.light === void 0 && e.substrate === void 0 && e.surface === void 0 && e.availability === void 0 && e.noiseFloorCalibration === void 0 && e.varianceDecomposition === void 0 && e.lifecycleTransition === void 0 && e.evalRecommendation === void 0 && e.conformanceFixtureRecord === void 0;
5036
5111
  },
5037
5112
  {
5038
- message: "EvaluationEvent shape invariant: each kind requires exactly its corresponding payload and forbids the others. kind='evaluation' \u2192 light+substrate+surface; kind='availability.transition' \u2192 availability; kind='analysis.noise-floor-calibration' \u2192 noiseFloorCalibration; kind='analysis.variance-decomposition' \u2192 varianceDecomposition; kind='analysis.lifecycle-transition' \u2192 lifecycleTransition; kind='analysis.eval-recommendation' \u2192 evalRecommendation; kind='analysis.conformance-fixture' \u2192 conformanceFixtureRecord; kind='analysis.emission-coverage' \u2192 emissionCoverage; kind='analysis.delivery-orphan' \u2192 deliveryOrphan."
5113
+ message: "EvaluationEvent shape invariant: each kind requires exactly its corresponding payload and forbids the others. kind='evaluation' \u2192 light+substrate+surface; kind='availability.transition' \u2192 availability; kind='analysis.noise-floor-calibration' \u2192 noiseFloorCalibration; kind='analysis.variance-decomposition' \u2192 varianceDecomposition; kind='analysis.lifecycle-transition' \u2192 lifecycleTransition; kind='analysis.eval-recommendation' \u2192 evalRecommendation; kind='analysis.conformance-fixture' \u2192 conformanceFixtureRecord; kind='analysis.emission-coverage' \u2192 emissionCoverage; kind='analysis.delivery-orphan' \u2192 deliveryOrphan; kind='analysis.orthogonality-hypothesis' \u2192 orthogonalityHypothesis."
5039
5114
  }
5040
5115
  );
5041
5116
  function newEventId(occurredAt = new Date()) {
@@ -5578,8 +5653,10 @@ var EvaluationEventEmitter = class {
5578
5653
  }, opts.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS);
5579
5654
  this.stopSentinelInterval = this.emitsSentinels ? schedule(() => {
5580
5655
  this.sentinelTick();
5581
- }, opts.sentinelIntervalMs ?? DEFAULT_SENTINEL_INTERVAL_MS) : () => {
5582
- };
5656
+ }, opts.sentinelIntervalMs ?? DEFAULT_SENTINEL_INTERVAL_MS) : (
5657
+
5658
+ () => void 0
5659
+ );
5583
5660
  }
5584
5661
 
5585
5662
  enqueue(event) {
@@ -6068,6 +6145,18 @@ var HttpTransport = class {
6068
6145
  async getContract() {
6069
6146
  return this.get("/holonograph/contract");
6070
6147
  }
6148
+ async getReconciliation(query = {}) {
6149
+ const { since, until, emissionStreamId } = query;
6150
+ return this.get(
6151
+ `/holonograph/reconciliation${toQueryString({ since, until, emissionStreamId })}`
6152
+ );
6153
+ }
6154
+ async getEmission(query = {}) {
6155
+ const { since, until, surfaceId } = query;
6156
+ return this.get(
6157
+ `/holonograph/emission${toQueryString({ since, until, surfaceId })}`
6158
+ );
6159
+ }
6071
6160
  async markAvailability(req) {
6072
6161
  return this.post("/lens/availability/mark", req);
6073
6162
  }
@@ -6175,6 +6264,14 @@ function lightSnapshotFromResult(result) {
6175
6264
  }
6176
6265
  return snapshot;
6177
6266
  }
6267
+ function toQueryString(query) {
6268
+ const params = new URLSearchParams();
6269
+ for (const [k, v] of Object.entries(query)) {
6270
+ if (v !== void 0) params.set(k, v);
6271
+ }
6272
+ const s = params.toString();
6273
+ return s.length > 0 ? `?${s}` : "";
6274
+ }
6178
6275
 
6179
6276
  function isHttpOptions(opts) {
6180
6277
  return "endpoint" in opts;
@@ -6314,11 +6411,20 @@ var HolonographClient = class {
6314
6411
  return this.declaredColumns;
6315
6412
  }
6316
6413
 
6414
+ apparatusStampedColumns(light, judgeIdentity) {
6415
+ const columns = { ...this.substrate.columns };
6416
+ if (light.servedModel !== void 0) columns[SERVED_MODEL_COLUMN_ID] = light.servedModel;
6417
+ if (judgeIdentity !== void 0) Object.assign(columns, judgeIdentityColumns(judgeIdentity));
6418
+ return columns;
6419
+ }
6420
+
6317
6421
  async validateSubstrateColumns(columns) {
6318
6422
  if (columns === void 0 || this.onUnknownColumn === "ignore" || this.transport.getContract === void 0) {
6319
6423
  return;
6320
6424
  }
6321
- const keys = Object.keys(columns);
6425
+ const keys = Object.keys(columns).filter(
6426
+ (k) => k !== SERVED_MODEL_COLUMN_ID && !k.startsWith(JUDGE_RESERVED_COLUMN_NAMESPACE_PREFIX)
6427
+ );
6322
6428
  if (keys.length === 0) return;
6323
6429
  const declared = await this.fetchDeclaredColumns();
6324
6430
  if (declared === null || declared.size === 0) return;
@@ -6435,7 +6541,11 @@ var HolonographClient = class {
6435
6541
  lensVersion: this.lensVersion,
6436
6542
  light: callResult.record.light,
6437
6543
 
6438
- substrate: { ...this.substrate, lightSourceIdentifier: callResult.record.lightSourceId },
6544
+ substrate: {
6545
+ ...this.substrate,
6546
+ lightSourceIdentifier: callResult.record.lightSourceId,
6547
+ columns: this.apparatusStampedColumns(callResult.record.light, outcome.judgeIdentity)
6548
+ },
6439
6549
  surface
6440
6550
  });
6441
6551
  await this.validateSubstrateColumns(event.substrate?.columns);
@@ -6517,13 +6627,139 @@ var HolonographClient = class {
6517
6627
  lensVersion: this.lensVersion,
6518
6628
  light: record.light,
6519
6629
 
6520
- substrate: { ...this.substrate, lightSourceIdentifier: record.lightSourceId },
6630
+ substrate: {
6631
+ ...this.substrate,
6632
+ lightSourceIdentifier: record.lightSourceId,
6633
+ columns: this.apparatusStampedColumns(record.light)
6634
+ },
6521
6635
  surface: observerSurface
6522
6636
  });
6523
6637
  await this.transport.appendEvent(event);
6524
6638
  }
6525
6639
  };
6526
6640
 
6641
+ function hasChanges(diff) {
6642
+ return diff.added.length > 0 || diff.removed.length > 0 || diff.changed.length > 0;
6643
+ }
6644
+ function structuralEquals(a, b) {
6645
+ if (a === b) return true;
6646
+ try {
6647
+ return JSON.stringify(a) === JSON.stringify(b);
6648
+ } catch {
6649
+ return false;
6650
+ }
6651
+ }
6652
+ function diffByKey(prev, next, keyOf, opts = {}) {
6653
+ const equals = opts.equals ?? structuralEquals;
6654
+ const prevByKey = new Map();
6655
+ for (const item of prev) prevByKey.set(keyOf(item), item);
6656
+ const nextByKey = new Map();
6657
+ for (const item of next) nextByKey.set(keyOf(item), item);
6658
+ const diff = { added: [], removed: [], changed: [], unchanged: [] };
6659
+ for (const [key, nextItem] of nextByKey) {
6660
+ const prevItem = prevByKey.get(key);
6661
+ if (prevItem === void 0) {
6662
+ diff.added.push(nextItem);
6663
+ } else if (equals(prevItem, nextItem)) {
6664
+ diff.unchanged.push(nextItem);
6665
+ } else {
6666
+ diff.changed.push({ key, prev: prevItem, next: nextItem });
6667
+ }
6668
+ }
6669
+ for (const [key, prevItem] of prevByKey) {
6670
+ if (!nextByKey.has(key)) diff.removed.push(prevItem);
6671
+ }
6672
+ return diff;
6673
+ }
6674
+
6675
+ function createPoller(opts) {
6676
+ const { poll, intervalMs, onResult, onError, signal, immediate = true } = opts;
6677
+ let timer;
6678
+ const controller = new AbortController();
6679
+ const stopped = () => controller.signal.aborted;
6680
+ const stop = () => {
6681
+ if (stopped()) return;
6682
+ if (timer !== void 0) clearTimeout(timer);
6683
+ controller.abort();
6684
+ };
6685
+ if (signal !== void 0) {
6686
+ if (signal.aborted) {
6687
+ stop();
6688
+ return { stop };
6689
+ }
6690
+ signal.addEventListener("abort", stop, { once: true });
6691
+ }
6692
+ const scheduleNext = () => {
6693
+ if (stopped()) return;
6694
+ timer = setTimeout(() => void tick(), intervalMs);
6695
+ };
6696
+ const tick = async () => {
6697
+ if (stopped()) return;
6698
+ try {
6699
+ const result = await poll(controller.signal);
6700
+ if (stopped()) return;
6701
+ await onResult(result);
6702
+ } catch (err) {
6703
+ if (!stopped()) onError?.(err);
6704
+ }
6705
+ scheduleNext();
6706
+ };
6707
+ if (immediate) void tick();
6708
+ else scheduleNext();
6709
+ return { stop };
6710
+ }
6711
+ function watchByKey(opts) {
6712
+ const { poll, intervalMs, keyOf, onChange, equals, onError, signal, emitInitial = false } = opts;
6713
+ const diffOpts = equals !== void 0 ? { equals } : {};
6714
+ let baseline;
6715
+ return createPoller({
6716
+ poll,
6717
+ intervalMs,
6718
+ ...onError !== void 0 ? { onError } : {},
6719
+ ...signal !== void 0 ? { signal } : {},
6720
+ onResult: async (next) => {
6721
+ if (baseline === void 0) {
6722
+ baseline = next;
6723
+ if (emitInitial) {
6724
+ const diff2 = diffByKey([], next, keyOf, diffOpts);
6725
+ if (hasChanges(diff2)) await onChange(diff2);
6726
+ }
6727
+ return;
6728
+ }
6729
+ const diff = diffByKey(baseline, next, keyOf, diffOpts);
6730
+ baseline = next;
6731
+ if (hasChanges(diff)) await onChange(diff);
6732
+ }
6733
+ });
6734
+ }
6735
+
6736
+ function watchReconciliation(opts) {
6737
+ const { reader, query, intervalMs, onChange, equals, onError, signal, emitInitial } = opts;
6738
+ return watchByKey({
6739
+ intervalMs,
6740
+ onChange,
6741
+ keyOf: (e) => e.emissionStreamId,
6742
+ poll: async () => (await reader.getReconciliation(query)).emitters,
6743
+ ...equals !== void 0 ? { equals } : {},
6744
+ ...onError !== void 0 ? { onError } : {},
6745
+ ...signal !== void 0 ? { signal } : {},
6746
+ ...emitInitial !== void 0 ? { emitInitial } : {}
6747
+ });
6748
+ }
6749
+ function watchEmission(opts) {
6750
+ const { reader, query, intervalMs, onChange, equals, onError, signal, emitInitial } = opts;
6751
+ return watchByKey({
6752
+ intervalMs,
6753
+ onChange,
6754
+ keyOf: (s) => s.surfaceId,
6755
+ poll: async () => (await reader.getEmission(query)).surfaces,
6756
+ ...equals !== void 0 ? { equals } : {},
6757
+ ...onError !== void 0 ? { onError } : {},
6758
+ ...signal !== void 0 ? { signal } : {},
6759
+ ...emitInitial !== void 0 ? { emitInitial } : {}
6760
+ });
6761
+ }
6762
+
6527
6763
  var HolonographClient2 = HolonographClient;
6528
6764
 
6529
- export { HolonographClient2 as HolonographClient, HolonographGradeAfterReportError, HolonographHttpError, HolonographMarkerPairingWarningError, HolonographNoObserversError, HolonographObserverNotFoundError, HttpTransport };
6765
+ export { HolonographClient2 as HolonographClient, HolonographGradeAfterReportError, HolonographHttpError, HolonographMarkerPairingWarningError, HolonographNoObserversError, HolonographObserverNotFoundError, HttpTransport, createPoller, diffByKey, hasChanges, watchByKey, watchEmission, watchReconciliation };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holonograph/client",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "TypeScript HTTP client for a Holonograph lens. Send messages through the lens and report scored outcomes back to it.",
5
5
  "license": "MIT",
6
6
  "type": "module",