@flighthq/textshaper 0.3.0-next.1315.1d4eb64 → 0.3.0-next.1392.f0bd0cc
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/contract.d.ts +1 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +1 -0
- package/dist/contract.js.map +1 -1
- package/dist/enableTextShaperGuards.d.ts +13 -0
- package/dist/enableTextShaperGuards.d.ts.map +1 -0
- package/dist/enableTextShaperGuards.js +25 -0
- package/dist/enableTextShaperGuards.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/textShaperPool.d.ts +4 -0
- package/dist/textShaperPool.d.ts.map +1 -1
- package/dist/textShaperPool.js +10 -1
- package/dist/textShaperPool.js.map +1 -1
- package/package.json +4 -3
- package/src/enableTextShaperGuards.test.ts +62 -0
- package/src/textShaperPool.test.ts +31 -1
package/dist/contract.d.ts
CHANGED
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC"}
|
package/dist/contract.js
CHANGED
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Uninstalls the guard installed by `enableTextShaperGuards`. */
|
|
2
|
+
export declare function disableTextShaperGuards(): void;
|
|
3
|
+
/**
|
|
4
|
+
* Installs the caller-facing shaped-run pool guard (opt-in, dev-only). `acquireShapedRun` and
|
|
5
|
+
* `releaseShapedRun` are paired brackets: each acquired run must be released exactly once and must not
|
|
6
|
+
* be used afterwards. Core already ignores a repeated release to preserve the pool invariant, but that
|
|
7
|
+
* silent recovery otherwise hides the caller bug that unbalanced the bracket.
|
|
8
|
+
*
|
|
9
|
+
* The guard warns once through `@flighthq/log`. Not importing this module costs production nothing: the
|
|
10
|
+
* message and logging dependency live only here, while the pool keeps a null callback by default.
|
|
11
|
+
*/
|
|
12
|
+
export declare function enableTextShaperGuards(): void;
|
|
13
|
+
//# sourceMappingURL=enableTextShaperGuards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enableTextShaperGuards.d.ts","sourceRoot":"","sources":["../src/enableTextShaperGuards.ts"],"names":[],"mappings":"AAMA,kEAAkE;AAClE,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { logOnce } from '@flighthq/log/contract';
|
|
2
|
+
import { LogLevel } from '@flighthq/types/contract';
|
|
3
|
+
import { setShapedRunReleaseGuard } from './textShaperPool';
|
|
4
|
+
/** Uninstalls the guard installed by `enableTextShaperGuards`. */
|
|
5
|
+
export function disableTextShaperGuards() {
|
|
6
|
+
setShapedRunReleaseGuard(null);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Installs the caller-facing shaped-run pool guard (opt-in, dev-only). `acquireShapedRun` and
|
|
10
|
+
* `releaseShapedRun` are paired brackets: each acquired run must be released exactly once and must not
|
|
11
|
+
* be used afterwards. Core already ignores a repeated release to preserve the pool invariant, but that
|
|
12
|
+
* silent recovery otherwise hides the caller bug that unbalanced the bracket.
|
|
13
|
+
*
|
|
14
|
+
* The guard warns once through `@flighthq/log`. Not importing this module costs production nothing: the
|
|
15
|
+
* message and logging dependency live only here, while the pool keeps a null callback by default.
|
|
16
|
+
*/
|
|
17
|
+
export function enableTextShaperGuards() {
|
|
18
|
+
setShapedRunReleaseGuard(warnOnDoubleRelease);
|
|
19
|
+
}
|
|
20
|
+
function warnOnDoubleRelease(_run) {
|
|
21
|
+
logOnce('textshaper:double-release', LogLevel.Warn, {
|
|
22
|
+
message: 'releaseShapedRun: this run is already in the pool, so it is being released twice. The repeated release was ignored to preserve the pool, but it indicates an unbalanced bracket. Every acquireShapedRun call must pair with exactly one releaseShapedRun call, and the run must not be used after release.',
|
|
23
|
+
}, 'textshaper');
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=enableTextShaperGuards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enableTextShaperGuards.js","sourceRoot":"","sources":["../src/enableTextShaperGuards.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,kEAAkE;AAClE,MAAM,UAAU,uBAAuB;IACrC,wBAAwB,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB;IACpC,wBAAwB,CAAC,mBAAmB,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAyB;IACpD,OAAO,CACL,2BAA2B,EAC3B,QAAQ,CAAC,IAAI,EACb;QACE,OAAO,EACL,4SAA4S;KAC/S,EACD,YAAY,CACb,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { acquireShapedRun, clearShapedRun, clearTextShaperCache, createShapedRun, createTextShaperCache, disposeTextShaperCache, disposeTextShaperSignals, enableTextShaperSignals, getCaretPositionsForRun, getClusterForIndex, getCodePointForGlyph, getFontMetrics, getFontMetricsInto, getFontUnitScale, getGlyphExtents, getGlyphExtentsBatch, getGlyphExtentsInto, getGlyphIndexForCodePoint, getGlyphName, getIndexRangeForCluster, getTextShaperSignals, itemizeText, measureText, releaseShapedRun, shapeTextRun, shapeTextRunCached, shapeTextRunInto, shapeTextRuns, } from './contract';
|
|
1
|
+
export { acquireShapedRun, clearShapedRun, clearTextShaperCache, createShapedRun, createTextShaperCache, disposeTextShaperCache, disposeTextShaperSignals, disableTextShaperGuards, enableTextShaperGuards, enableTextShaperSignals, getCaretPositionsForRun, getClusterForIndex, getCodePointForGlyph, getFontMetrics, getFontMetricsInto, getFontUnitScale, getGlyphExtents, getGlyphExtentsBatch, getGlyphExtentsInto, getGlyphIndexForCodePoint, getGlyphName, getIndexRangeForCluster, getTextShaperSignals, itemizeText, measureText, releaseShapedRun, shapeTextRun, shapeTextRunCached, shapeTextRunInto, shapeTextRuns, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,yBAAyB,EACzB,YAAY,EACZ,uBAAuB,EACvB,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,yBAAyB,EACzB,YAAY,EACZ,uBAAuB,EACvB,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { acquireShapedRun, clearShapedRun, clearTextShaperCache, createShapedRun, createTextShaperCache, disposeTextShaperCache, disposeTextShaperSignals, enableTextShaperSignals, getCaretPositionsForRun, getClusterForIndex, getCodePointForGlyph, getFontMetrics, getFontMetricsInto, getFontUnitScale, getGlyphExtents, getGlyphExtentsBatch, getGlyphExtentsInto, getGlyphIndexForCodePoint, getGlyphName, getIndexRangeForCluster, getTextShaperSignals, itemizeText, measureText, releaseShapedRun, shapeTextRun, shapeTextRunCached, shapeTextRunInto, shapeTextRuns, } from './contract';
|
|
1
|
+
export { acquireShapedRun, clearShapedRun, clearTextShaperCache, createShapedRun, createTextShaperCache, disposeTextShaperCache, disposeTextShaperSignals, disableTextShaperGuards, enableTextShaperGuards, enableTextShaperSignals, getCaretPositionsForRun, getClusterForIndex, getCodePointForGlyph, getFontMetrics, getFontMetricsInto, getFontUnitScale, getGlyphExtents, getGlyphExtentsBatch, getGlyphExtentsInto, getGlyphIndexForCodePoint, getGlyphName, getIndexRangeForCluster, getTextShaperSignals, itemizeText, measureText, releaseShapedRun, shapeTextRun, shapeTextRunCached, shapeTextRunInto, shapeTextRuns, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,yBAAyB,EACzB,YAAY,EACZ,uBAAuB,EACvB,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,yBAAyB,EACzB,YAAY,EACZ,uBAAuB,EACvB,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC"}
|
package/dist/textShaperPool.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ShapedRun } from '@flighthq/types/contract';
|
|
2
2
|
export declare function acquireShapedRun(): ShapedRun;
|
|
3
3
|
export declare function releaseShapedRun(run: ShapedRun): void;
|
|
4
|
+
/** Installs the shaped-run release guard, or clears it with `null`. The seam keeps warning text and the
|
|
5
|
+
* `@flighthq/log` dependency in the separately importable guard module. Use `enableTextShaperGuards`
|
|
6
|
+
* rather than installing a callback directly in application code. */
|
|
7
|
+
export declare function setShapedRunReleaseGuard(guard: ((run: Readonly<ShapedRun>) => void) | null): void;
|
|
4
8
|
//# sourceMappingURL=textShaperPool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textShaperPool.d.ts","sourceRoot":"","sources":["../src/textShaperPool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAU1D,wBAAgB,gBAAgB,IAAI,SAAS,CAK5C;AASD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"textShaperPool.d.ts","sourceRoot":"","sources":["../src/textShaperPool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAU1D,wBAAgB,gBAAgB,IAAI,SAAS,CAK5C;AASD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CASrD;AAED;;sEAEsE;AACtE,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAEjG"}
|
package/dist/textShaperPool.js
CHANGED
|
@@ -20,13 +20,21 @@ export function acquireShapedRun() {
|
|
|
20
20
|
// into one buffer -- corruption that surfaces as wrong glyphs far from its cause. Ignoring keeps the
|
|
21
21
|
// pool invariant (an entry appears at most once) intact whatever the caller does.
|
|
22
22
|
export function releaseShapedRun(run) {
|
|
23
|
-
if (_pooled.has(run))
|
|
23
|
+
if (_pooled.has(run)) {
|
|
24
|
+
_releaseGuard?.(run);
|
|
24
25
|
return;
|
|
26
|
+
}
|
|
25
27
|
if (_pool.length < _POOL_MAX_SIZE) {
|
|
26
28
|
_pool.push(run);
|
|
27
29
|
_pooled.add(run);
|
|
28
30
|
}
|
|
29
31
|
}
|
|
32
|
+
/** Installs the shaped-run release guard, or clears it with `null`. The seam keeps warning text and the
|
|
33
|
+
* `@flighthq/log` dependency in the separately importable guard module. Use `enableTextShaperGuards`
|
|
34
|
+
* rather than installing a callback directly in application code. */
|
|
35
|
+
export function setShapedRunReleaseGuard(guard) {
|
|
36
|
+
_releaseGuard = guard;
|
|
37
|
+
}
|
|
30
38
|
// Maximum number of ShapedRuns to retain in the pool before discarding on release. Keeps
|
|
31
39
|
// memory bounded in cases where burst shaping produces many runs.
|
|
32
40
|
const _POOL_MAX_SIZE = 64;
|
|
@@ -34,4 +42,5 @@ const _pool = [];
|
|
|
34
42
|
// Membership mirror for `_pool`, so the double-release check is O(1) rather than scanning the pool on
|
|
35
43
|
// every release. Weak so a run dropped past capacity stays collectable.
|
|
36
44
|
const _pooled = new WeakSet();
|
|
45
|
+
let _releaseGuard = null;
|
|
37
46
|
//# sourceMappingURL=textShaperPool.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textShaperPool.js","sourceRoot":"","sources":["../src/textShaperPool.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,mFAAmF;AACnF,oFAAoF;AACpF,0FAA0F;AAC1F,EAAE;AACF,wFAAwF;AACxF,uBAAuB;AACvB,MAAM,UAAU,gBAAgB;IAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;IAC3B,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,eAAe,EAAE,CAAC;IACnD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,sFAAsF;AACtF,oGAAoG;AACpG,EAAE;AACF,qGAAqG;AACrG,uGAAuG;AACvG,qGAAqG;AACrG,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,GAAc;IAC7C,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"textShaperPool.js","sourceRoot":"","sources":["../src/textShaperPool.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,mFAAmF;AACnF,oFAAoF;AACpF,0FAA0F;AAC1F,EAAE;AACF,wFAAwF;AACxF,uBAAuB;AACvB,MAAM,UAAU,gBAAgB;IAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;IAC3B,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,eAAe,EAAE,CAAC;IACnD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,sFAAsF;AACtF,oGAAoG;AACpG,EAAE;AACF,qGAAqG;AACrG,uGAAuG;AACvG,qGAAqG;AACrG,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,GAAc;IAC7C,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;sEAEsE;AACtE,MAAM,UAAU,wBAAwB,CAAC,KAAkD;IACzF,aAAa,GAAG,KAAK,CAAC;AACxB,CAAC;AAED,yFAAyF;AACzF,kEAAkE;AAClE,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,MAAM,KAAK,GAAgB,EAAE,CAAC;AAC9B,sGAAsG;AACtG,wEAAwE;AACxE,MAAM,OAAO,GAAG,IAAI,OAAO,EAAa,CAAC;AACzC,IAAI,aAAa,GAAgD,IAAI,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/textshaper",
|
|
3
|
-
"version": "0.3.0-next.
|
|
3
|
+
"version": "0.3.0-next.1392.f0bd0cc",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/flighthq/flight.git",
|
|
@@ -36,8 +36,9 @@
|
|
|
36
36
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@flighthq/
|
|
40
|
-
"@flighthq/
|
|
39
|
+
"@flighthq/log": "0.3.0-next.1392.f0bd0cc",
|
|
40
|
+
"@flighthq/signals": "0.3.0-next.1392.f0bd0cc",
|
|
41
|
+
"@flighthq/types": "0.3.0-next.1392.f0bd0cc"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"typescript": "^5.3.0"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { addLogSink, createMemoryLogSink, getMemoryLogSinkEntries, removeLogSink } from '@flighthq/log/contract';
|
|
2
|
+
import type { LogEntry } from '@flighthq/types/contract';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { disableTextShaperGuards, enableTextShaperGuards } from './enableTextShaperGuards';
|
|
6
|
+
import { acquireShapedRun, releaseShapedRun } from './textShaperPool';
|
|
7
|
+
|
|
8
|
+
function captureLog(run: () => void): readonly LogEntry[] {
|
|
9
|
+
const sink = createMemoryLogSink(8);
|
|
10
|
+
addLogSink(sink.sink);
|
|
11
|
+
try {
|
|
12
|
+
run();
|
|
13
|
+
return getMemoryLogSinkEntries(sink);
|
|
14
|
+
} finally {
|
|
15
|
+
removeLogSink(sink.sink);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('disableTextShaperGuards', () => {
|
|
20
|
+
it('stops repeated releases from reporting', () => {
|
|
21
|
+
const entries = captureLog(() => {
|
|
22
|
+
enableTextShaperGuards();
|
|
23
|
+
disableTextShaperGuards();
|
|
24
|
+
const run = acquireShapedRun();
|
|
25
|
+
releaseShapedRun(run);
|
|
26
|
+
releaseShapedRun(run);
|
|
27
|
+
});
|
|
28
|
+
expect(entries).toEqual([]);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('enableTextShaperGuards', () => {
|
|
33
|
+
it('warns when one acquired run is released twice', () => {
|
|
34
|
+
const entries = captureLog(() => {
|
|
35
|
+
enableTextShaperGuards();
|
|
36
|
+
try {
|
|
37
|
+
const run = acquireShapedRun();
|
|
38
|
+
releaseShapedRun(run);
|
|
39
|
+
releaseShapedRun(run);
|
|
40
|
+
} finally {
|
|
41
|
+
disableTextShaperGuards();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
expect(entries).toHaveLength(1);
|
|
45
|
+
const data = entries[0].data;
|
|
46
|
+
expect(typeof data === 'string' ? data : String(data.message)).toContain('released twice');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('stays silent for correctly paired acquire and release calls', () => {
|
|
50
|
+
const entries = captureLog(() => {
|
|
51
|
+
enableTextShaperGuards();
|
|
52
|
+
try {
|
|
53
|
+
const run = acquireShapedRun();
|
|
54
|
+
releaseShapedRun(run);
|
|
55
|
+
releaseShapedRun(acquireShapedRun());
|
|
56
|
+
} finally {
|
|
57
|
+
disableTextShaperGuards();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
expect(entries).toEqual([]);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { acquireShapedRun, releaseShapedRun } from './textShaperPool';
|
|
1
|
+
import { acquireShapedRun, releaseShapedRun, setShapedRunReleaseGuard } from './textShaperPool';
|
|
2
2
|
|
|
3
3
|
describe('acquireShapedRun', () => {
|
|
4
4
|
it('returns a ShapedRun with the expected shape', () => {
|
|
@@ -72,3 +72,33 @@ describe('releaseShapedRun', () => {
|
|
|
72
72
|
expect(a).not.toBe(b);
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
|
+
|
|
76
|
+
describe('setShapedRunReleaseGuard', () => {
|
|
77
|
+
it('reports a repeated release after core detects it', () => {
|
|
78
|
+
const seen: unknown[] = [];
|
|
79
|
+
const run = acquireShapedRun();
|
|
80
|
+
setShapedRunReleaseGuard((released) => seen.push(released));
|
|
81
|
+
try {
|
|
82
|
+
releaseShapedRun(run);
|
|
83
|
+
releaseShapedRun(run);
|
|
84
|
+
expect(seen).toEqual([run]);
|
|
85
|
+
} finally {
|
|
86
|
+
setShapedRunReleaseGuard(null);
|
|
87
|
+
releaseShapedRun(acquireShapedRun());
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('stays silent for paired reuse and after being cleared', () => {
|
|
92
|
+
const seen: unknown[] = [];
|
|
93
|
+
const run = acquireShapedRun();
|
|
94
|
+
setShapedRunReleaseGuard((released) => seen.push(released));
|
|
95
|
+
releaseShapedRun(run);
|
|
96
|
+
const reacquired = acquireShapedRun();
|
|
97
|
+
releaseShapedRun(reacquired);
|
|
98
|
+
expect(seen).toEqual([]);
|
|
99
|
+
|
|
100
|
+
setShapedRunReleaseGuard(null);
|
|
101
|
+
releaseShapedRun(reacquired);
|
|
102
|
+
expect(seen).toEqual([]);
|
|
103
|
+
});
|
|
104
|
+
});
|