osra 0.6.6 → 0.6.8

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.
@@ -9,24 +9,26 @@ export type Messages = {
9
9
  };
10
10
  export declare const Messages: Messages;
11
11
  declare const IDENTITY_MARKER: unique symbol;
12
- type IdentityWrapper<T = unknown> = {
12
+ /** Phantom shape. A tracked value carries no marker of its own, the mark lives in a WeakMap, so
13
+ * this is what `isType` declares instead: matching it at the type level would widen `Capable` to
14
+ * every object, and nothing a user writes structurally matches this. */
15
+ type IdentityMarked = {
13
16
  readonly [IDENTITY_MARKER]: true;
14
- readonly value: T;
15
17
  };
16
18
  export type BoxedIdentity<T extends Capable = Capable> = BoxBaseType<typeof type> & {
17
19
  id: string;
18
20
  inner?: Capable;
19
21
  [UnderlyingType]: T;
20
22
  };
21
- /** Wrap a value so osra preserves reference identity across the RPC
22
- * boundary. Idempotent; primitives pass through unchanged. Lies at the
23
- * type level - runtime value is an IdentityWrapper<T> typed as T. */
23
+ /** Mark a value so osra preserves its reference identity across the boundary. The peer's revived
24
+ * value stands for this one, and handing it back - to you, or onward to a further context and back
25
+ * again - resolves to this very reference. The mark sticks to the value, so only the side that
26
+ * owns it has to opt in. Idempotent, and primitives pass through unchanged. */
24
27
  export declare const identity: <T>(value: T) => T;
25
- export declare const isType: (value: unknown) => value is IdentityWrapper;
26
- export declare const box: <T extends Capable, TContext extends RevivableContext>(wrapper: IdentityWrapper<T>, context: TContext) => BoxedIdentity<T>;
27
- /** Identity-box a referenceable value with a caller-supplied inner box,
28
- * bypassing the recursive-box step. Used by revivables (symbol with
29
- * description=undefined) where recursing back through their own box
28
+ export declare const isType: (value: unknown) => value is IdentityMarked;
29
+ export declare const box: <T extends Capable, TContext extends RevivableContext>(value: T, context: TContext) => BoxedIdentity<T>;
30
+ /** Identity-box a referenceable value with a caller-supplied inner box, bypassing the walker. Used
31
+ * by revivables (symbol with description=undefined) where recursing back through their own box
30
32
  * would loop into this module again. */
31
33
  export declare const boxByReference: <T extends WeakKey, TContext extends RevivableContext>(value: T, innerBox: Capable, context: TContext) => BoxedIdentity;
32
34
  export declare const revive: <T extends BoxedIdentity, TContext extends RevivableContext>(value: T, context: TContext) => T[UnderlyingType];
@@ -70,5 +70,18 @@ export declare const defaultRevivableModules: readonly [typeof transfer, typeof
70
70
  }];
71
71
  export type DefaultRevivableModules = typeof defaultRevivableModules;
72
72
  export type DefaultRevivableModule = DefaultRevivableModules[number];
73
+ /** Hold a side effect until the whole box walk this value belongs to has finished, and undo it if the
74
+ * walk throws. A module that records state the PEER will be told about ("it knows this id now") must
75
+ * not keep that record when a later sibling breaks the message it was recorded for: nothing ships,
76
+ * and every later send would then reference something the peer never received. Outside a walk the
77
+ * effect is already settled, so `commit` runs immediately. */
78
+ export declare const onBoxWalkSettled: (commit: () => void, rollback: () => void) => void;
73
79
  export declare const recursiveBox: <T extends Capable, TModules extends readonly RevivableModule[]>(value: T, context: RevivableContext<TModules>) => DeepReplaceWithBox<T, TModules[number]>;
80
+ /** Box a value your own module claimed in place: every other module gets its turn and children are
81
+ * walked as usual, but `claimedBy` is skipped, and the cycle guard the caller's `recursiveBox`
82
+ * frame already holds for this value is not re-entered.
83
+ * A module needs this when its `isType` matches a bare value rather than a wrapper around one, the
84
+ * way `identity()` marks a reference in place: `recursiveBox` on that same value would come
85
+ * straight back to the module, and the guard would report it as a cycle. */
86
+ export declare const boxClaimedValue: <T extends Capable, TModules extends readonly RevivableModule[]>(value: T, context: RevivableContext<TModules>, claimedBy: string) => DeepReplaceWithBox<T, TModules[number]>;
74
87
  export declare const recursiveRevive: <T extends Capable, TModules extends readonly RevivableModule[]>(value: T, context: RevivableContext<TModules>) => DeepReplaceWithRevive<T, TModules[number]>;
@@ -1,6 +1,5 @@
1
- import type { Browser } from 'webextension-polyfill';
2
1
  import type { Message } from '../types.js';
3
- import type { WebExtOnConnect, WebExtOnMessage, WebExtPort, WebExtRuntime, WebExtSender } from './type-guards.js';
2
+ import type { WebExtGlobal, WebExtOnConnect, WebExtOnMessage, WebExtPort, WebExtRuntime, WebExtSender } from './webext-types.js';
4
3
  /** What the local side knows about the realm on the other end of a connection.
5
4
  *
6
5
  * `origin` and `source` are only OBSERVABLE on window transports. A MessagePort message carries
@@ -61,8 +60,8 @@ export type PlatformTransport = EmitPlatformTransport | ReceivePlatformTransport
61
60
  export type EmitTransport = EmitPlatformTransport | CustomEmitTransport;
62
61
  export type ReceiveTransport = ReceivePlatformTransport | CustomReceiveTransport;
63
62
  export type Transport = PlatformTransport | CustomTransport;
64
- export declare const getWebExtensionGlobal: () => Browser | undefined;
65
- export declare const getWebExtensionRuntime: () => import("webextension-polyfill").Runtime.Static | undefined;
63
+ export declare const getWebExtensionGlobal: () => WebExtGlobal | undefined;
64
+ export declare const getWebExtensionRuntime: () => WebExtRuntime | undefined;
66
65
  export declare const checkOsraMessageKey: (message: any, key: string) => message is Message;
67
66
  export declare const registerOsraMessageListener: ({ listener, transport, remoteName, key, origin, unregisterSignal }: {
68
67
  listener: (message: Message, messageContext: MessageContext) => void;
@@ -1,6 +1,6 @@
1
- import type { Runtime } from 'webextension-polyfill';
2
1
  import type { Message } from '../types.js';
3
2
  import type { CustomEmitTransport, CustomReceiveTransport, CustomTransport, EmitJsonPlatformTransport, EmitTransport, JsonPlatformTransport, ReceiveJsonPlatformTransport, ReceiveTransport, Transport } from './transport.js';
3
+ import type { WebExtOnConnect, WebExtOnMessage, WebExtPort, WebExtRuntime } from './webext-types.js';
4
4
  declare const typedArrayConstructorsByName: {
5
5
  readonly Int8Array: Int8ArrayConstructor;
6
6
  readonly Uint8Array: Uint8ArrayConstructor;
@@ -41,14 +41,10 @@ export declare const isSharedArrayBuffer: (value: unknown) => boolean;
41
41
  * the unrelated clonable fallback module. Use isSharedArrayBuffer. */
42
42
  export declare const isClonable: typeof isSharedArrayBuffer;
43
43
  export declare const isTransferable: (value: unknown) => value is Transferable;
44
- export type WebExtRuntime = Runtime.Static;
44
+ export type { WebExtRuntime, WebExtPort, WebExtSender, WebExtOnConnect, WebExtOnMessage, WebExtEvent, WebExtGlobal } from './webext-types.js';
45
45
  export declare const isWebExtensionRuntime: (value: unknown) => value is WebExtRuntime;
46
- export type WebExtPort = ReturnType<WebExtRuntime['connect']> | Runtime.Port;
47
46
  export declare const isWebExtensionPort: (value: unknown, connectPort?: boolean) => value is WebExtPort;
48
- export type WebExtSender = NonNullable<WebExtPort['sender']>;
49
- export type WebExtOnConnect = WebExtRuntime['onConnect'];
50
47
  export declare const isWebExtensionOnConnect: (value: unknown) => value is WebExtOnConnect;
51
- export type WebExtOnMessage = WebExtRuntime['onMessage'];
52
48
  export declare const isWebExtensionOnMessage: (value: unknown) => value is WebExtOnMessage;
53
49
  export declare const isWindow: (value: unknown) => value is Window;
54
50
  export declare const isEmitJsonOnlyTransport: (value: unknown) => value is EmitJsonPlatformTransport;
@@ -63,4 +59,3 @@ export declare const isCustomEmitTransport: (value: unknown) => value is CustomE
63
59
  export declare const isCustomReceiveTransport: (value: unknown) => value is CustomReceiveTransport;
64
60
  export declare const isCustomTransport: (value: unknown) => value is CustomTransport;
65
61
  export declare const isTransport: (value: unknown) => value is Transport;
66
- export {};
@@ -0,0 +1,65 @@
1
+ /** Structural stand-ins for the handful of WebExtension shapes osra touches.
2
+ *
3
+ * Vendored rather than imported from `webextension-polyfill`, because a bare `import type` inside a
4
+ * SHIPPED .d.ts makes osra's own declarations depend on a package the consumer may not have, and
5
+ * osra ships no dependencies. 0.6.6 and 0.6.7 did exactly that: `build/utils/transport.d.ts` and
6
+ * `build/utils/type-guards.d.ts` both imported `webextension-polyfill`, so a consumer without
7
+ * `@types/webextension-polyfill` got TS2307 under `skipLibCheck: false` and, far worse, a silent
8
+ * collapse with it on - `WebExtRuntime` became `any`, which made `Transport` `any`, which made
9
+ * `IsJsonOnlyTransport` true for every transport and degraded the whole `Capable` check.
10
+ * `npm run check-declaration-imports` is the guard that keeps it from coming back.
11
+ *
12
+ * Deliberately loose: only the members osra actually calls, typed permissively enough that both
13
+ * `webextension-polyfill`'s `Runtime.Static` and `@types/chrome`'s `typeof chrome.runtime` stay
14
+ * assignable, since either can be handed to `expose()` as a transport. No index signatures here,
15
+ * for the same reason: an interface has no implicit index signature, so adding one to these would
16
+ * make every real sender and port stop being assignable. `tests/extension` compiles against
17
+ * @types/chrome and the docs examples compile against webextension-polyfill, which is what keeps
18
+ * both ends honest. */
19
+ /** The three members osra uses on an extension event (`runtime.onMessage`, `port.onDisconnect`, ...). */
20
+ export type WebExtEvent<TListener extends (...args: any[]) => any = (...args: any[]) => any> = {
21
+ addListener(listener: TListener, ...rest: any[]): void;
22
+ removeListener(listener: TListener): void;
23
+ hasListener(listener: TListener): boolean;
24
+ };
25
+ /** Who a message came from, as the browser reported it. osra never builds one, it only hands the
26
+ * browser's own object through to your code, so the fields are all optional and the engine may
27
+ * carry more than these. */
28
+ export type WebExtSender = {
29
+ id?: string;
30
+ url?: string;
31
+ origin?: string;
32
+ frameId?: number;
33
+ documentId?: string;
34
+ tlsChannelId?: string;
35
+ tab?: {
36
+ id?: number;
37
+ url?: string;
38
+ title?: string;
39
+ windowId?: number;
40
+ index?: number;
41
+ active?: boolean;
42
+ };
43
+ };
44
+ export type WebExtPort = {
45
+ name: string;
46
+ sender?: WebExtSender;
47
+ disconnect(): void;
48
+ postMessage(message: any): void;
49
+ onMessage: WebExtEvent;
50
+ onDisconnect: WebExtEvent;
51
+ };
52
+ export type WebExtRuntime = {
53
+ id?: string;
54
+ connect(...args: any[]): WebExtPort;
55
+ sendMessage(...args: any[]): any;
56
+ onMessage: WebExtEvent;
57
+ onConnect: WebExtEvent;
58
+ onConnectExternal?: WebExtEvent;
59
+ };
60
+ export type WebExtOnConnect = WebExtRuntime['onConnect'];
61
+ export type WebExtOnMessage = WebExtRuntime['onMessage'];
62
+ /** The `browser` / `chrome` global, as much of it as osra looks at. */
63
+ export type WebExtGlobal = {
64
+ runtime?: WebExtRuntime;
65
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osra",
3
- "version": "0.6.6",
3
+ "version": "0.6.8",
4
4
  "description": "Easy communication between workers",
5
5
  "homepage": "https://github.com/Banou26/osra#readme",
6
6
  "bugs": {
@@ -36,7 +36,7 @@
36
36
  "scripts": {
37
37
  "build": "vp build && node ./node_modules/typescript7/bin/tsc --declaration --emitDeclarationOnly",
38
38
  "build-watch": "vp build --watch",
39
- "clean": "rimraf build",
39
+ "clean": "node -e \"require('node:fs').rmSync('build', { recursive: true, force: true })\"",
40
40
  "prepublishOnly": "npm run clean && npm run build",
41
41
  "build-test": "vp build --config vite.test.config.ts",
42
42
  "build-test-watch": "vp build --config vite.test.config.ts --watch",
@@ -46,20 +46,22 @@
46
46
  "test": "npm run build && npm run build-test && npx playwright test",
47
47
  "test-watch": "set PWTEST_WATCH=1 & npx playwright test",
48
48
  "test-watch-headful": "nodemon --watch build-test --exec \"npx playwright test --headed\"",
49
- "test-with-coverage": "npm run build-test && npx playwright test && npm run print-coverage && npm run clean-coverage",
49
+ "test-with-coverage": "VITE_COVERAGE=true npm run build-test && VITE_COVERAGE=true npx playwright test && npm run print-coverage && npm run clean-coverage",
50
50
  "print-coverage": "npx nyc report",
51
- "clean-coverage": "rimraf .nyc_output coverage",
51
+ "clean-coverage": "node -e \"for (const dir of ['.nyc_output', 'coverage']) require('node:fs').rmSync(dir, { recursive: true, force: true })\"",
52
52
  "type-check": "node ./node_modules/typescript7/bin/tsc --noEmit && node ./node_modules/typescript7/bin/tsc --noEmit -p tsconfig.tests.json",
53
53
  "start-server": "http-server -p 3000",
54
54
  "build-extension-test": "node build-extension-test.mjs",
55
55
  "test-extension": "npm run build-extension-test && npx playwright test -c playwright.extension.config.ts",
56
56
  "check-consumer-types": "npm run build && node ./node_modules/typescript7/bin/tsc --noEmit -p tsconfig.consumer.json",
57
+ "check-declaration-imports": "node scripts/check-declaration-imports.mjs",
57
58
  "docs-dev": "npm run dev -w docs",
58
59
  "docs-build": "npm run build -w docs",
59
60
  "docs-preview": "npm run preview -w docs",
60
61
  "docs-gen-reference": "npm run gen:reference -w docs",
61
62
  "docs-check-links": "npm run check-links -w docs",
62
- "docs-check-twoslash": "npm run check-twoslash -w docs"
63
+ "docs-check-twoslash": "npm run check-twoslash -w docs",
64
+ "docs-check-html": "npm run check-html -w docs"
63
65
  },
64
66
  "devDependencies": {
65
67
  "@istanbuljs/nyc-config-typescript": "^1.0.2",