@flighthq/entity 0.5.1-next.731.0ae0aa4 → 0.5.1-next.799.62ef0c7

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/README.md CHANGED
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/entity`. The `@fligh
13
13
  This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
14
14
 
15
15
  - [Flight project](https://github.com/flighthq/flight)
16
- - [Source for @flighthq/entity@0.5.1-next.731.0ae0aa4](https://github.com/flighthq/flight/tree/0ae0aa49d8251d23b546f64bc242f3ad59a98dcb/packages/entity)
17
- - [License](https://github.com/flighthq/flight/blob/0ae0aa49d8251d23b546f64bc242f3ad59a98dcb/LICENSE.md)
16
+ - [Source for @flighthq/entity@0.5.1-next.799.62ef0c7](https://github.com/flighthq/flight/tree/62ef0c74f09f24177aac3695b2487b2d743d4fe6/packages/entity)
17
+ - [License](https://github.com/flighthq/flight/blob/62ef0c74f09f24177aac3695b2487b2d743d4fe6/LICENSE.md)
@@ -3,6 +3,7 @@ export * from './clone';
3
3
  export * from './entity';
4
4
  export * from './enableEntityRuntimeGuards';
5
5
  export * from './entityUid';
6
+ export * from './explainEntityRuntimeWrite';
6
7
  export * from './guards';
7
8
  export * from './host';
8
9
  export * from './runtime';
@@ -1 +1 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,aAAa,CAAC;AAC5B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC"}
package/dist/contract.js CHANGED
@@ -3,6 +3,7 @@ export * from './clone';
3
3
  export * from './entity';
4
4
  export * from './enableEntityRuntimeGuards';
5
5
  export * from './entityUid';
6
+ export * from './explainEntityRuntimeWrite';
6
7
  export * from './guards';
7
8
  export * from './host';
8
9
  export * from './runtime';
@@ -1 +1 @@
1
- {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,aAAa,CAAC;AAC5B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { EntityRuntimeWriteGuard } from '@flighthq/types/contract';
1
2
  export declare function disableEntityRuntimeGuards(): void;
2
- export declare function enableEntityRuntimeGuards(): void;
3
+ export declare function enableEntityRuntimeGuards(report: EntityRuntimeWriteGuard): void;
3
4
  //# sourceMappingURL=enableEntityRuntimeGuards.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"enableEntityRuntimeGuards.d.ts","sourceRoot":"","sources":["../src/enableEntityRuntimeGuards.ts"],"names":[],"mappings":"AAOA,wBAAgB,0BAA0B,IAAI,IAAI,CAGjD;AAWD,wBAAgB,yBAAyB,IAAI,IAAI,CAGhD"}
1
+ {"version":3,"file":"enableEntityRuntimeGuards.d.ts","sourceRoot":"","sources":["../src/enableEntityRuntimeGuards.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAKxE,wBAAgB,0BAA0B,IAAI,IAAI,CAGjD;AAYD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAG/E"}
@@ -1,5 +1,3 @@
1
- import { logOnce } from '@flighthq/log/contract';
2
- import { LogLevel } from '@flighthq/types/contract';
3
1
  import { setEntityRuntimeGuardMode, setEntityRuntimeWriteGuard } from './guards';
4
2
  // Uninstalls the guard installed by enableEntityRuntimeGuards, restoring unguarded entities.
5
3
  export function disableEntityRuntimeGuards() {
@@ -7,27 +5,17 @@ export function disableEntityRuntimeGuards() {
7
5
  setEntityRuntimeWriteGuard(null);
8
6
  }
9
7
  // Opt-in development guard mode. When enabled, a direct write to an entity's runtime slot — or to an
10
- // EntityRuntime's binding slot — that bypasses attachEntityBinding is reported,
11
- // making "the write landed on the wrong entity" and raw-slot-poke bugs visible early. The write is still
8
+ // EntityRuntime's binding slot — that bypasses attachEntityBinding is reported to `report`, making
9
+ // "the write landed on the wrong entity" and raw-slot-poke bugs visible early. The write is still
12
10
  // allowed: the guard observes, it does not block.
13
11
  //
14
- // @flighthq/entity is a CORE package, and core may otherwise depend only on types/core. This module is the
15
- // sanctioned exception: it is separately importable and shakeable, so a build that never imports it never
16
- // pulls @flighthq/log, and the layer rule's real concern — feature weight in core's always-loaded graph —
17
- // does not arise. `packages:check` enforces that the import appears in guard modules only. Idempotent.
18
- export function enableEntityRuntimeGuards() {
12
+ // The reporter is the CALLER's. @flighthq/entity is a core package and owns no sink: it does not reach
13
+ // for a logger, a console, or any other ambient singleton, so nothing about where diagnostics go is
14
+ // decided here. Pass explainEntityRuntimeWrite's result to whatever the app already logs through.
15
+ // Turning the proxies on and installing the reporter is one act, which is why this exists rather than
16
+ // leaving callers to pair setEntityRuntimeGuardMode with setEntityRuntimeWriteGuard. Idempotent.
17
+ export function enableEntityRuntimeGuards(report) {
19
18
  setEntityRuntimeGuardMode(true);
20
- setEntityRuntimeWriteGuard(warnOnDirectWrite);
21
- }
22
- function warnOnDirectWrite(slot) {
23
- if (slot === 'binding-slot') {
24
- logOnce('entity:direct-binding-write', LogLevel.Warn, {
25
- message: 'EntityRuntime.binding was written directly. Use attachEntityBinding or detachEntityBinding, which keep the binding and the runtime consistent; the write was allowed but is not tracked.',
26
- }, 'entity');
27
- return;
28
- }
29
- logOnce('entity:direct-runtime-write', LogLevel.Warn, {
30
- message: "An entity's runtime slot was written directly. Use attachEntityBinding, which allocates the slot for you; the write was allowed, but bypassing it is how a runtime ends up on the wrong entity.",
31
- }, 'entity');
19
+ setEntityRuntimeWriteGuard(report);
32
20
  }
33
21
  //# sourceMappingURL=enableEntityRuntimeGuards.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"enableEntityRuntimeGuards.js","sourceRoot":"","sources":["../src/enableEntityRuntimeGuards.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAEjF,6FAA6F;AAC7F,MAAM,UAAU,0BAA0B;IACxC,yBAAyB,CAAC,KAAK,CAAC,CAAC;IACjC,0BAA0B,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,qGAAqG;AACrG,gFAAgF;AAChF,yGAAyG;AACzG,kDAAkD;AAClD,EAAE;AACF,2GAA2G;AAC3G,0GAA0G;AAC1G,0GAA0G;AAC1G,uGAAuG;AACvG,MAAM,UAAU,yBAAyB;IACvC,yBAAyB,CAAC,IAAI,CAAC,CAAC;IAChC,0BAA0B,CAAC,iBAAiB,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,iBAAiB,CAAC,IAA4B;IACrD,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;QAC5B,OAAO,CACL,6BAA6B,EAC7B,QAAQ,CAAC,IAAI,EACb;YACE,OAAO,EACL,0LAA0L;SAC7L,EACD,QAAQ,CACT,CAAC;QACF,OAAO;IACT,CAAC;IACD,OAAO,CACL,6BAA6B,EAC7B,QAAQ,CAAC,IAAI,EACb;QACE,OAAO,EACL,iMAAiM;KACpM,EACD,QAAQ,CACT,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"enableEntityRuntimeGuards.js","sourceRoot":"","sources":["../src/enableEntityRuntimeGuards.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAEjF,6FAA6F;AAC7F,MAAM,UAAU,0BAA0B;IACxC,yBAAyB,CAAC,KAAK,CAAC,CAAC;IACjC,0BAA0B,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,qGAAqG;AACrG,mGAAmG;AACnG,kGAAkG;AAClG,kDAAkD;AAClD,EAAE;AACF,uGAAuG;AACvG,oGAAoG;AACpG,kGAAkG;AAClG,sGAAsG;AACtG,iGAAiG;AACjG,MAAM,UAAU,yBAAyB,CAAC,MAA+B;IACvE,yBAAyB,CAAC,IAAI,CAAC,CAAC;IAChC,0BAA0B,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { EntityRuntimeWriteExplanation, EntityRuntimeWriteSlot } from '@flighthq/types/contract';
2
+ export declare function explainEntityRuntimeWrite(slot: EntityRuntimeWriteSlot): EntityRuntimeWriteExplanation;
3
+ //# sourceMappingURL=explainEntityRuntimeWrite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explainEntityRuntimeWrite.d.ts","sourceRoot":"","sources":["../src/explainEntityRuntimeWrite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAMtG,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,sBAAsB,GAAG,6BAA6B,CAerG"}
@@ -0,0 +1,19 @@
1
+ // Describes what a guarded direct write means, as plain data. The guard seam reports only which slot was
2
+ // written; this turns that into the remedy, so a caller's reporter can phrase a warning without core
3
+ // owning a message format or a logger. Separately importable, so a build that never diagnoses these
4
+ // writes never carries the text.
5
+ export function explainEntityRuntimeWrite(slot) {
6
+ if (slot === 'binding-slot') {
7
+ return {
8
+ message: 'EntityRuntime.binding was written directly. The write was allowed but is not tracked, so the binding and the runtime can disagree.',
9
+ slot,
10
+ useInstead: ['attachEntityBinding', 'detachEntityBinding'],
11
+ };
12
+ }
13
+ return {
14
+ message: "An entity's runtime slot was written directly. The write was allowed, but bypassing the helper is how a runtime ends up on the wrong entity.",
15
+ slot,
16
+ useInstead: ['attachEntityBinding'],
17
+ };
18
+ }
19
+ //# sourceMappingURL=explainEntityRuntimeWrite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explainEntityRuntimeWrite.js","sourceRoot":"","sources":["../src/explainEntityRuntimeWrite.ts"],"names":[],"mappings":"AAEA,yGAAyG;AACzG,qGAAqG;AACrG,oGAAoG;AACpG,iCAAiC;AACjC,MAAM,UAAU,yBAAyB,CAAC,IAA4B;IACpE,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;QAC5B,OAAO;YACL,OAAO,EACL,oIAAoI;YACtI,IAAI;YACJ,UAAU,EAAE,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;SAC3D,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EACL,8IAA8I;QAChJ,IAAI;QACJ,UAAU,EAAE,CAAC,qBAAqB,CAAC;KACpC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flighthq/entity",
3
- "version": "0.5.1-next.731.0ae0aa4",
3
+ "version": "0.5.1-next.799.62ef0c7",
4
4
  "author": "Joshua Granick and other contributors",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,8 +38,7 @@
38
38
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
39
39
  },
40
40
  "dependencies": {
41
- "@flighthq/log": "0.5.1-next.731.0ae0aa4",
42
- "@flighthq/types": "0.5.1-next.731.0ae0aa4"
41
+ "@flighthq/types": "0.5.1-next.799.62ef0c7"
43
42
  },
44
43
  "devDependencies": {
45
44
  "typescript": "^5.3.0"
@@ -1,5 +1,4 @@
1
- import { addLogSink, createMemoryLogSink, getMemoryLogSinkEntries, removeLogSink } from '@flighthq/log/contract';
2
- import type { LogEntry } from '@flighthq/types/contract';
1
+ import type { EntityRuntimeWriteSlot } from '@flighthq/types/contract';
3
2
  import { EntityRuntimeKey } from '@flighthq/types/contract';
4
3
 
5
4
  import { disableEntityRuntimeGuards, enableEntityRuntimeGuards } from './enableEntityRuntimeGuards';
@@ -7,69 +6,47 @@ import { createEntity } from './entity';
7
6
  import { areEntityRuntimeGuardsEnabled, createGuardedEntity, createGuardedEntityRuntime } from './guards';
8
7
  import { createEntityRuntime } from './runtime';
9
8
 
10
- function captureLog(run: () => void): readonly LogEntry[] {
11
- const sink = createMemoryLogSink(8);
12
- addLogSink(sink.sink);
9
+ // Every case runs the write under a reporter that records the slot, so what is asserted is what the
10
+ // caller's reporter actually received — not that some sink somewhere was touched.
11
+ function recordSlots(write: () => void): readonly EntityRuntimeWriteSlot[] {
12
+ const slots: EntityRuntimeWriteSlot[] = [];
13
+ enableEntityRuntimeGuards((slot) => slots.push(slot));
13
14
  try {
14
- run();
15
- return getMemoryLogSinkEntries(sink);
15
+ write();
16
16
  } finally {
17
- removeLogSink(sink.sink);
17
+ disableEntityRuntimeGuards();
18
18
  }
19
- }
20
-
21
- function messageOf(entry: Readonly<LogEntry>): string {
22
- const data = entry.data;
23
- return typeof data === 'string' ? data : String(data.message);
19
+ return slots;
24
20
  }
25
21
 
26
22
  describe('disableEntityRuntimeGuards', () => {
27
23
  it('uninstalls both the proxies and the reporter', () => {
28
- const entries = captureLog(() => {
29
- enableEntityRuntimeGuards();
30
- disableEntityRuntimeGuards();
31
- createGuardedEntity(createEntity())[EntityRuntimeKey] = undefined;
32
- });
33
- expect(entries.length).toBe(0);
24
+ const slots: EntityRuntimeWriteSlot[] = [];
25
+ enableEntityRuntimeGuards((slot) => slots.push(slot));
26
+ disableEntityRuntimeGuards();
27
+ createGuardedEntity(createEntity())[EntityRuntimeKey] = undefined;
28
+ expect(slots).toEqual([]);
34
29
  expect(areEntityRuntimeGuardsEnabled()).toBe(false);
35
30
  });
36
31
  });
37
32
 
38
33
  describe('enableEntityRuntimeGuards', () => {
39
- it('WARNS through the log sink on a direct runtime-slot write', () => {
40
- const entries = captureLog(() => {
41
- enableEntityRuntimeGuards();
42
- try {
43
- createGuardedEntity(createEntity())[EntityRuntimeKey] = undefined;
44
- } finally {
45
- disableEntityRuntimeGuards();
46
- }
47
- });
48
- expect(entries.length).toBe(1);
49
- // Names a function that actually exists: the point of the warning is that the caller can act on it.
50
- expect(messageOf(entries[0])).toContain('attachEntityBinding');
34
+ it('REPORTS a direct runtime-slot write to the caller-supplied reporter', () => {
35
+ expect(recordSlots(() => (createGuardedEntity(createEntity())[EntityRuntimeKey] = undefined))).toEqual([
36
+ 'runtime-slot',
37
+ ]);
51
38
  });
52
39
 
53
- it('WARNS separately for a direct binding-slot write', () => {
54
- const entries = captureLog(() => {
55
- enableEntityRuntimeGuards();
56
- try {
57
- createGuardedEntityRuntime(createEntityRuntime()).binding = null;
58
- } finally {
59
- disableEntityRuntimeGuards();
60
- }
61
- });
62
- expect(entries.length).toBe(1);
63
- expect(messageOf(entries[0])).toContain('attachEntityBinding');
64
- expect(messageOf(entries[0])).toContain('detachEntityBinding');
40
+ it('REPORTS a direct binding-slot write separately', () => {
41
+ expect(recordSlots(() => (createGuardedEntityRuntime(createEntityRuntime()).binding = null))).toEqual([
42
+ 'binding-slot',
43
+ ]);
65
44
  });
66
45
 
67
46
  it('stays SILENT without the guard — the production default', () => {
68
- const entries = captureLog(() => {
69
- // Unguarded entities are returned as-is, so there is nothing to intercept.
70
- createGuardedEntity(createEntity())[EntityRuntimeKey] = undefined;
71
- });
72
- expect(entries.length).toBe(0);
47
+ const slots: EntityRuntimeWriteSlot[] = [];
48
+ createGuardedEntity(createEntity())[EntityRuntimeKey] = undefined;
49
+ expect(slots).toEqual([]);
73
50
  expect(areEntityRuntimeGuardsEnabled()).toBe(false);
74
51
  });
75
52
  });
@@ -0,0 +1,21 @@
1
+ import { explainEntityRuntimeWrite } from './explainEntityRuntimeWrite';
2
+
3
+ describe('explainEntityRuntimeWrite', () => {
4
+ it('names both binding helpers for a binding-slot write', () => {
5
+ const explanation = explainEntityRuntimeWrite('binding-slot');
6
+ expect(explanation.slot).toBe('binding-slot');
7
+ expect(explanation.useInstead).toEqual(['attachEntityBinding', 'detachEntityBinding']);
8
+ });
9
+
10
+ it('names the allocating helper for a runtime-slot write', () => {
11
+ const explanation = explainEntityRuntimeWrite('runtime-slot');
12
+ expect(explanation.slot).toBe('runtime-slot');
13
+ expect(explanation.useInstead).toEqual(['attachEntityBinding']);
14
+ });
15
+
16
+ it('distinguishes the two slots by message, not only by name', () => {
17
+ expect(explainEntityRuntimeWrite('binding-slot').message).not.toBe(
18
+ explainEntityRuntimeWrite('runtime-slot').message,
19
+ );
20
+ });
21
+ });