@xema/omni-protocol 0.1.48 → 0.1.49

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/testing.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { browserSessionKey, effectiveLevels, sameCapabilities, } from "./index.js";
2
- import { assertNoViolations, validateAuthenticationState, validateEventEnvelope, validateHostGuarantees, validateHostReport, validateManifest, validateResult, validateSnapshot, validateTimeZone, validatePhone, isTimeZone, } from "./validation.js";
2
+ import { assertNoViolations, validateAuthenticationState, validateEventEnvelope, validateHostGuarantees, validateHostReport, validateManifest, validateResult, validateSnapshot, validateTimeZone, validatePhone, isTimeZone, sameTimeZone, } from "./validation.js";
3
3
  export { ProtocolConformanceError, assertNoViolations } from "./validation.js";
4
4
  /**
5
5
  * A part of the contract a run may never reach: state nothing obliges an adapter to publish, so
@@ -257,18 +257,6 @@ export async function exerciseAdapter(adapter, context, options = {}) {
257
257
  violations.push(...validateHostGuarantees(context.host.guarantees, "context.host.guarantees"));
258
258
  // The zone is the host's to state, so a context without one is a host that cannot exist.
259
259
  violations.push(...validateTimeZone(context.timeZone, "context.timeZone"));
260
- // A provider that reads its own clock passes the round trip on any machine in the host's zone,
261
- // which for a demo and a runner in one datacentre is most of them. A conformance run states a
262
- // zone this machine is not in, so the only way the identity carries it is that the provider
263
- // took it from the host.
264
- if (isTimeZone(context.timeZone)) {
265
- const canonical = (zone) => new Intl.DateTimeFormat("en", { timeZone: zone }).resolvedOptions().timeZone;
266
- const local = Intl.DateTimeFormat().resolvedOptions().timeZone;
267
- if (canonical(context.timeZone) === canonical(local)) {
268
- violations.push({ rule: "context.timeZone.local", path: "context.timeZone",
269
- message: `a conformance run states a zone this machine is not in; ${context.timeZone} is this machine's, so a provider reading its own clock would pass the round trip` });
270
- }
271
- }
272
260
  // How the agent hears the call decides what the host owns: on a softphone the host has the
273
261
  // audio and reports it; on a desk phone, or off voice, there is none for it to report.
274
262
  violations.push(...validatePhone(context.phone, adapter.manifest, "context.phone"));
@@ -354,7 +342,9 @@ export async function exerciseAdapter(adapter, context, options = {}) {
354
342
  // The provider republishes the agent's day: once connected, the identity carries the zone the
355
343
  // host sent. That proves the round trip, not the store -- an echo passes it -- and the name
356
344
  // says only what it tests; storage is proved by a colleague's zone arriving from describeUsers().
357
- if (isTimeZone(context.timeZone) && current().identity.timeZone !== context.timeZone) {
345
+ // A zone is judged by what it denotes: Asia/Kolkata and Asia/Calcutta are one zone, and a
346
+ // provider that keeps the canonical name has kept the zone.
347
+ if (isTimeZone(context.timeZone) && !sameTimeZone(current().identity.timeZone, context.timeZone)) {
358
348
  violations.push({
359
349
  rule: "authentication.identity.timeZone.republished",
360
350
  path: "authentication.identity.timeZone",
@@ -74,6 +74,8 @@ export type ResultMethod = "execute" | "dial" | "setCapacity" | "requestBreak" |
74
74
  export declare function validateResult(result: unknown, method: ResultMethod, path?: string, dialId?: string): ProtocolViolation[];
75
75
  /** An IANA zone name the runtime knows: `Asia/Kolkata` is one, an offset or a made-up name is not. */
76
76
  export declare function isTimeZone(value: unknown): value is string;
77
+ /** Whether two zone names denote one zone: `Asia/Kolkata` and `Asia/Calcutta` do, and a name is judged by what it denotes. */
78
+ export declare function sameTimeZone(a: unknown, b: unknown): boolean;
77
79
  /** The zone a host sends at connect: required, and an IANA name. */
78
80
  export declare function validateTimeZone(value: unknown, path?: string): ProtocolViolation[];
79
81
  /** What a login is validated against beyond its own shape. */
@@ -1788,6 +1788,13 @@ export function isTimeZone(value) {
1788
1788
  return false;
1789
1789
  }
1790
1790
  }
1791
+ /** Whether two zone names denote one zone: `Asia/Kolkata` and `Asia/Calcutta` do, and a name is judged by what it denotes. */
1792
+ export function sameTimeZone(a, b) {
1793
+ if (!isTimeZone(a) || !isTimeZone(b))
1794
+ return false;
1795
+ const canonical = (zone) => new Intl.DateTimeFormat("en", { timeZone: zone }).resolvedOptions().timeZone;
1796
+ return a === b || canonical(a) === canonical(b);
1797
+ }
1791
1798
  /** The zone a host sends at connect: required, and an IANA name. */
1792
1799
  export function validateTimeZone(value, path = "context.timeZone") {
1793
1800
  const into = new Collector();
package/guide.md CHANGED
@@ -1939,10 +1939,9 @@ field to fill in later: an identity without one is refused (`authentication.iden
1939
1939
  so is a zone that is not an IANA name, wherever it appears. Nothing is ever assumed in its place --
1940
1940
  not the viewer's browser, which gives a different answer per reader for the same record, and not the
1941
1941
  provider's clock. The harness holds a provider to the round trip: the identity carries the zone the
1942
- host stated (`authentication.identity.timeZone.republished`), and a conformance run states a zone
1943
- the machine running it is not in (`context.timeZone.local`): a provider that read its own clock
1944
- would otherwise pass the round trip on any runner in the host's zone, which for a demo and a runner
1945
- in one datacentre is most of them. **The round trip is not the store.**
1942
+ host stated (`authentication.identity.timeZone.republished`), judged by what the name denotes:
1943
+ `Asia/Kolkata` and `Asia/Calcutta` are one zone, and a provider that keeps the canonical name has
1944
+ kept the zone. **The round trip is not the store.**
1946
1945
  An adapter that echoes the stated zone back onto the identity passes that check with nothing kept,
1947
1946
  and a lead reading a colleague's day would still get the wrong one. What proves the store is a zone
1948
1947
  the run never sent: a colleague's `User` from `describeUsers()` carrying theirs. A provider's own
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xema/omni-protocol",
3
- "version": "0.1.48",
3
+ "version": "0.1.49",
4
4
  "description": "The Omni protocol: the contract every provider adapter implements",
5
5
  "type": "module",
6
6
  "license": "MIT",