aether-wv 0.0.0-stage → 1.0.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.
Files changed (106) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +313 -2
  3. package/bin/aetherc.js +4 -0
  4. package/dist/index.d.ts +5 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +5 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/packages/client/src/client.d.ts +26 -0
  9. package/dist/packages/client/src/client.d.ts.map +1 -0
  10. package/dist/packages/client/src/client.js +58 -0
  11. package/dist/packages/client/src/client.js.map +1 -0
  12. package/dist/packages/client/src/index.d.ts +8 -0
  13. package/dist/packages/client/src/index.d.ts.map +1 -0
  14. package/dist/packages/client/src/index.js +8 -0
  15. package/dist/packages/client/src/index.js.map +1 -0
  16. package/dist/packages/client/src/transports/android.d.ts +10 -0
  17. package/dist/packages/client/src/transports/android.d.ts.map +1 -0
  18. package/dist/packages/client/src/transports/android.js +38 -0
  19. package/dist/packages/client/src/transports/android.js.map +1 -0
  20. package/dist/packages/client/src/transports/electron.d.ts +39 -0
  21. package/dist/packages/client/src/transports/electron.d.ts.map +1 -0
  22. package/dist/packages/client/src/transports/electron.js +51 -0
  23. package/dist/packages/client/src/transports/electron.js.map +1 -0
  24. package/dist/packages/client/src/transports/ios.d.ts +9 -0
  25. package/dist/packages/client/src/transports/ios.d.ts.map +1 -0
  26. package/dist/packages/client/src/transports/ios.js +37 -0
  27. package/dist/packages/client/src/transports/ios.js.map +1 -0
  28. package/dist/packages/client/src/transports/memory.d.ts +10 -0
  29. package/dist/packages/client/src/transports/memory.d.ts.map +1 -0
  30. package/dist/packages/client/src/transports/memory.js +29 -0
  31. package/dist/packages/client/src/transports/memory.js.map +1 -0
  32. package/dist/packages/client/src/transports/transport.d.ts +3 -0
  33. package/dist/packages/client/src/transports/transport.d.ts.map +1 -0
  34. package/dist/packages/client/src/transports/transport.js +2 -0
  35. package/dist/packages/client/src/transports/transport.js.map +1 -0
  36. package/dist/packages/client/src/transports/webview2.d.ts +25 -0
  37. package/dist/packages/client/src/transports/webview2.d.ts.map +1 -0
  38. package/dist/packages/client/src/transports/webview2.js +45 -0
  39. package/dist/packages/client/src/transports/webview2.js.map +1 -0
  40. package/dist/packages/compiler/src/ast.d.ts +27 -0
  41. package/dist/packages/compiler/src/ast.d.ts.map +1 -0
  42. package/dist/packages/compiler/src/ast.js +2 -0
  43. package/dist/packages/compiler/src/ast.js.map +1 -0
  44. package/dist/packages/compiler/src/cli.d.ts +2 -0
  45. package/dist/packages/compiler/src/cli.d.ts.map +1 -0
  46. package/dist/packages/compiler/src/cli.js +68 -0
  47. package/dist/packages/compiler/src/cli.js.map +1 -0
  48. package/dist/packages/compiler/src/codegen-kotlin.d.ts +3 -0
  49. package/dist/packages/compiler/src/codegen-kotlin.d.ts.map +1 -0
  50. package/dist/packages/compiler/src/codegen-kotlin.js +119 -0
  51. package/dist/packages/compiler/src/codegen-kotlin.js.map +1 -0
  52. package/dist/packages/compiler/src/codegen-swift.d.ts +3 -0
  53. package/dist/packages/compiler/src/codegen-swift.d.ts.map +1 -0
  54. package/dist/packages/compiler/src/codegen-swift.js +129 -0
  55. package/dist/packages/compiler/src/codegen-swift.js.map +1 -0
  56. package/dist/packages/compiler/src/codegen-ts.d.ts +3 -0
  57. package/dist/packages/compiler/src/codegen-ts.d.ts.map +1 -0
  58. package/dist/packages/compiler/src/codegen-ts.js +121 -0
  59. package/dist/packages/compiler/src/codegen-ts.js.map +1 -0
  60. package/dist/packages/compiler/src/index.d.ts +7 -0
  61. package/dist/packages/compiler/src/index.d.ts.map +1 -0
  62. package/dist/packages/compiler/src/index.js +7 -0
  63. package/dist/packages/compiler/src/index.js.map +1 -0
  64. package/dist/packages/compiler/src/parser.d.ts +3 -0
  65. package/dist/packages/compiler/src/parser.d.ts.map +1 -0
  66. package/dist/packages/compiler/src/parser.js +80 -0
  67. package/dist/packages/compiler/src/parser.js.map +1 -0
  68. package/dist/packages/core/src/index.d.ts +7 -0
  69. package/dist/packages/core/src/index.d.ts.map +1 -0
  70. package/dist/packages/core/src/index.js +7 -0
  71. package/dist/packages/core/src/index.js.map +1 -0
  72. package/dist/packages/core/src/multiplexer/backpressure.d.ts +41 -0
  73. package/dist/packages/core/src/multiplexer/backpressure.d.ts.map +1 -0
  74. package/dist/packages/core/src/multiplexer/backpressure.js +80 -0
  75. package/dist/packages/core/src/multiplexer/backpressure.js.map +1 -0
  76. package/dist/packages/core/src/multiplexer/multiplexer.d.ts +36 -0
  77. package/dist/packages/core/src/multiplexer/multiplexer.d.ts.map +1 -0
  78. package/dist/packages/core/src/multiplexer/multiplexer.js +238 -0
  79. package/dist/packages/core/src/multiplexer/multiplexer.js.map +1 -0
  80. package/dist/packages/core/src/protocol/frames.d.ts +46 -0
  81. package/dist/packages/core/src/protocol/frames.d.ts.map +1 -0
  82. package/dist/packages/core/src/protocol/frames.js +89 -0
  83. package/dist/packages/core/src/protocol/frames.js.map +1 -0
  84. package/dist/packages/core/src/protocol/serializer.d.ts +38 -0
  85. package/dist/packages/core/src/protocol/serializer.d.ts.map +1 -0
  86. package/dist/packages/core/src/protocol/serializer.js +145 -0
  87. package/dist/packages/core/src/protocol/serializer.js.map +1 -0
  88. package/dist/packages/core/src/security/capability.d.ts +18 -0
  89. package/dist/packages/core/src/security/capability.d.ts.map +1 -0
  90. package/dist/packages/core/src/security/capability.js +56 -0
  91. package/dist/packages/core/src/security/capability.js.map +1 -0
  92. package/dist/packages/core/src/session/session.d.ts +29 -0
  93. package/dist/packages/core/src/session/session.d.ts.map +1 -0
  94. package/dist/packages/core/src/session/session.js +52 -0
  95. package/dist/packages/core/src/session/session.js.map +1 -0
  96. package/dist/packages/native-host/src/host.d.ts +30 -0
  97. package/dist/packages/native-host/src/host.d.ts.map +1 -0
  98. package/dist/packages/native-host/src/host.js +232 -0
  99. package/dist/packages/native-host/src/host.js.map +1 -0
  100. package/dist/packages/native-host/src/index.d.ts +2 -0
  101. package/dist/packages/native-host/src/index.d.ts.map +1 -0
  102. package/dist/packages/native-host/src/index.js +2 -0
  103. package/dist/packages/native-host/src/index.js.map +1 -0
  104. package/package.json +37 -4
  105. package/packages/platform-templates/android/AetherNativeDriver.kt +57 -0
  106. package/packages/platform-templates/ios/AetherNativeDriver.swift +78 -0
@@ -0,0 +1,56 @@
1
+ import { createHmac } from 'node:crypto';
2
+ export class CapabilityGatekeeper {
3
+ secretKey;
4
+ constructor(secretKey = 'aether-internal-secret') {
5
+ this.secretKey = secretKey;
6
+ }
7
+ /**
8
+ * Generates a signed capability token for a trusted mini-app origin
9
+ */
10
+ generateToken(manifest) {
11
+ const payload = JSON.stringify(manifest);
12
+ const signature = createHmac('sha256', this.secretKey).update(payload).digest('hex');
13
+ const b64Payload = Buffer.from(payload).toString('base64url');
14
+ return `${b64Payload}.${signature}`;
15
+ }
16
+ /**
17
+ * Verifies a capability token and checks if the service/method is authorized
18
+ */
19
+ authorize(token, currentOrigin, serviceName, methodName) {
20
+ if (!token)
21
+ return false;
22
+ const parts = token.split('.');
23
+ if (parts.length !== 2)
24
+ return false;
25
+ const [b64Payload, signature] = parts;
26
+ const payload = Buffer.from(b64Payload, 'base64url').toString('utf8');
27
+ const expectedSig = createHmac('sha256', this.secretKey).update(payload).digest('hex');
28
+ if (signature !== expectedSig) {
29
+ return false;
30
+ }
31
+ try {
32
+ const manifest = JSON.parse(payload);
33
+ if (manifest.expiresAt < Date.now()) {
34
+ return false;
35
+ }
36
+ if (manifest.origin !== '*' && currentOrigin !== '*' && manifest.origin !== currentOrigin) {
37
+ return false;
38
+ }
39
+ // Check wildcards and exact service matches
40
+ const target = `${serviceName}.${methodName}`;
41
+ return manifest.allowedServices.some((pattern) => {
42
+ if (pattern === '*')
43
+ return true;
44
+ if (pattern === serviceName || pattern === `${serviceName}.*`)
45
+ return true;
46
+ if (pattern === target)
47
+ return true;
48
+ return false;
49
+ });
50
+ }
51
+ catch {
52
+ return false;
53
+ }
54
+ }
55
+ }
56
+ //# sourceMappingURL=capability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.js","sourceRoot":"","sources":["../../../../../packages/core/src/security/capability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQzC,MAAM,OAAO,oBAAoB;IACF;IAA7B,YAA6B,YAAoB,wBAAwB;QAA5C,cAAS,GAAT,SAAS,CAAmC;IAAG,CAAC;IAE7E;;OAEG;IACH,aAAa,CAAC,QAA4B;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrF,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC9D,OAAO,GAAG,UAAU,IAAI,SAAS,EAAE,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,KAAa,EAAE,aAAqB,EAAE,WAAmB,EAAE,UAAkB;QACrF,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACzB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAErC,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC;QACtC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtE,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAEvF,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAuB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACzD,IAAI,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;gBACpC,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,aAAa,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;gBAC1F,OAAO,KAAK,CAAC;YACf,CAAC;YAED,4CAA4C;YAC5C,MAAM,MAAM,GAAG,GAAG,WAAW,IAAI,UAAU,EAAE,CAAC;YAC9C,OAAO,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC/C,IAAI,OAAO,KAAK,GAAG;oBAAE,OAAO,IAAI,CAAC;gBACjC,IAAI,OAAO,KAAK,WAAW,IAAI,OAAO,KAAK,GAAG,WAAW,IAAI;oBAAE,OAAO,IAAI,CAAC;gBAC3E,IAAI,OAAO,KAAK,MAAM;oBAAE,OAAO,IAAI,CAAC;gBACpC,OAAO,KAAK,CAAC;YACf,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,29 @@
1
+ import { AetherFrame } from '../protocol/frames.js';
2
+ /**
3
+ * Ring buffer of recent frames for session resumption following WebContent process OOM reload.
4
+ */
5
+ export declare class SessionRingBuffer {
6
+ private readonly capacity;
7
+ private buffer;
8
+ constructor(capacity?: number);
9
+ add(frame: AetherFrame): void;
10
+ /**
11
+ * Retrieves all frames with sequenceId > lastAckSeq
12
+ */
13
+ getFramesAfter(lastAckSeq: number): AetherFrame[];
14
+ clear(): void;
15
+ }
16
+ export interface SessionState {
17
+ sessionId: string;
18
+ createdAt: number;
19
+ lastActive: number;
20
+ lastSequenceId: number;
21
+ ringBuffer: SessionRingBuffer;
22
+ }
23
+ export declare class SessionRegistry {
24
+ private sessions;
25
+ getOrCreateSession(sessionId: string): SessionState;
26
+ getSession(sessionId: string): SessionState | undefined;
27
+ deleteSession(sessionId: string): void;
28
+ }
29
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../../../packages/core/src/session/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD;;GAEG;AACH,qBAAa,iBAAiB;IAGhB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAFrC,OAAO,CAAC,MAAM,CAAqB;gBAEN,QAAQ,GAAE,MAAY;IAEnD,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAO7B;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,EAAE;IAIjD,KAAK,IAAI,IAAI;CAGd;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,iBAAiB,CAAC;CAC/B;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAmC;IAEnD,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY;IAiBnD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIvD,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAGvC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Ring buffer of recent frames for session resumption following WebContent process OOM reload.
3
+ */
4
+ export class SessionRingBuffer {
5
+ capacity;
6
+ buffer = [];
7
+ constructor(capacity = 256) {
8
+ this.capacity = capacity;
9
+ }
10
+ add(frame) {
11
+ if (this.buffer.length >= this.capacity) {
12
+ this.buffer.shift();
13
+ }
14
+ this.buffer.push(frame);
15
+ }
16
+ /**
17
+ * Retrieves all frames with sequenceId > lastAckSeq
18
+ */
19
+ getFramesAfter(lastAckSeq) {
20
+ return this.buffer.filter((f) => f.sequenceId > lastAckSeq);
21
+ }
22
+ clear() {
23
+ this.buffer = [];
24
+ }
25
+ }
26
+ export class SessionRegistry {
27
+ sessions = new Map();
28
+ getOrCreateSession(sessionId) {
29
+ let session = this.sessions.get(sessionId);
30
+ if (!session) {
31
+ session = {
32
+ sessionId,
33
+ createdAt: Date.now(),
34
+ lastActive: Date.now(),
35
+ lastSequenceId: 0,
36
+ ringBuffer: new SessionRingBuffer(256),
37
+ };
38
+ this.sessions.set(sessionId, session);
39
+ }
40
+ else {
41
+ session.lastActive = Date.now();
42
+ }
43
+ return session;
44
+ }
45
+ getSession(sessionId) {
46
+ return this.sessions.get(sessionId);
47
+ }
48
+ deleteSession(sessionId) {
49
+ this.sessions.delete(sessionId);
50
+ }
51
+ }
52
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../../../packages/core/src/session/session.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,iBAAiB;IAGC;IAFrB,MAAM,GAAkB,EAAE,CAAC;IAEnC,YAA6B,WAAmB,GAAG;QAAtB,aAAQ,GAAR,QAAQ,CAAc;IAAG,CAAC;IAEvD,GAAG,CAAC,KAAkB;QACpB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,UAAkB;QAC/B,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,UAAU,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IACnB,CAAC;CACF;AAUD,MAAM,OAAO,eAAe;IAClB,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAC;IAEnD,kBAAkB,CAAC,SAAiB;QAClC,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG;gBACR,SAAS;gBACT,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;gBACrB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;gBACtB,cAAc,EAAE,CAAC;gBACjB,UAAU,EAAE,IAAI,iBAAiB,CAAC,GAAG,CAAC;aACvC,CAAC;YACF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAClC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,aAAa,CAAC,SAAiB;QAC7B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;CACF"}
@@ -0,0 +1,30 @@
1
+ import { Transport, CapabilityGatekeeper, SessionRegistry } from '../../core/src/index.js';
2
+ export interface StreamEmitter {
3
+ send(payload: Uint8Array): Promise<void>;
4
+ close(): void;
5
+ error(err: Error): void;
6
+ onCancel(cb: () => void): void;
7
+ }
8
+ export type RpcHandler = (payload: Uint8Array) => Promise<Uint8Array>;
9
+ export type StreamHandler = (payload: Uint8Array, emitter: StreamEmitter) => void;
10
+ export declare class AetherNativeHost {
11
+ private transport;
12
+ private rpcHandlers;
13
+ private streamHandlers;
14
+ private activeEmitters;
15
+ private streamControllers;
16
+ private sequenceId;
17
+ gatekeeper: CapabilityGatekeeper;
18
+ sessionRegistry: SessionRegistry;
19
+ constructor(transport: Transport, secretKey?: string);
20
+ registerRpc(service: string, method: string, handler: RpcHandler): void;
21
+ registerStream(service: string, method: string, handler: StreamHandler): void;
22
+ private handleRawBytes;
23
+ private sendFrame;
24
+ private sendError;
25
+ private handleFrame;
26
+ private handleRpcRequest;
27
+ private handleStreamInit;
28
+ private handleHandshake;
29
+ }
30
+ //# sourceMappingURL=host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../../../packages/native-host/src/host.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAST,oBAAoB,EACpB,eAAe,EAChB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,KAAK,IAAI,IAAI,CAAC;IACd,KAAK,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;AAElF,qBAAa,gBAAgB;IAWzB,OAAO,CAAC,SAAS;IAVnB,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,iBAAiB,CAA6C;IACtE,OAAO,CAAC,UAAU,CAAK;IAEhB,UAAU,EAAE,oBAAoB,CAAC;IACjC,eAAe,EAAE,eAAe,CAAC;gBAG9B,SAAS,EAAE,SAAS,EAC5B,SAAS,CAAC,EAAE,MAAM;IAQpB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAIvE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAI7E,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,WAAW;IA4CnB,OAAO,CAAC,gBAAgB;IAwCxB,OAAO,CAAC,gBAAgB;IA8ExB,OAAO,CAAC,eAAe;CAyCxB"}
@@ -0,0 +1,232 @@
1
+ import { FrameType, FrameFlags, encodeFrame, decodeFrame, BinaryReader, BinaryWriter, StreamCreditController, CapabilityGatekeeper, SessionRegistry, } from '../../core/src/index.js';
2
+ export class AetherNativeHost {
3
+ transport;
4
+ rpcHandlers = new Map();
5
+ streamHandlers = new Map();
6
+ activeEmitters = new Map();
7
+ streamControllers = new Map();
8
+ sequenceId = 1;
9
+ gatekeeper;
10
+ sessionRegistry;
11
+ constructor(transport, secretKey) {
12
+ this.transport = transport;
13
+ this.gatekeeper = new CapabilityGatekeeper(secretKey);
14
+ this.sessionRegistry = new SessionRegistry();
15
+ this.transport.onReceive((data) => this.handleRawBytes(data));
16
+ }
17
+ registerRpc(service, method, handler) {
18
+ this.rpcHandlers.set(`${service}.${method}`, handler);
19
+ }
20
+ registerStream(service, method, handler) {
21
+ this.streamHandlers.set(`${service}.${method}`, handler);
22
+ }
23
+ handleRawBytes(bytes) {
24
+ const frame = decodeFrame(bytes);
25
+ this.handleFrame(frame);
26
+ }
27
+ sendFrame(frame, sessionId) {
28
+ if (sessionId) {
29
+ const session = this.sessionRegistry.getOrCreateSession(sessionId);
30
+ session.ringBuffer.add(frame);
31
+ }
32
+ const bytes = encodeFrame(frame);
33
+ this.transport.send(bytes);
34
+ }
35
+ sendError(streamId, message, sessionId) {
36
+ const errorFrame = {
37
+ type: FrameType.ERROR,
38
+ flags: FrameFlags.NONE,
39
+ streamId,
40
+ sequenceId: this.sequenceId++,
41
+ payload: new TextEncoder().encode(message),
42
+ };
43
+ this.sendFrame(errorFrame, sessionId);
44
+ }
45
+ handleFrame(frame) {
46
+ switch (frame.type) {
47
+ case FrameType.RPC_REQ: {
48
+ this.handleRpcRequest(frame);
49
+ break;
50
+ }
51
+ case FrameType.STREAM_DATA: {
52
+ this.handleStreamInit(frame);
53
+ break;
54
+ }
55
+ case FrameType.FLOW_ACK: {
56
+ const credits = new DataView(frame.payload.buffer, frame.payload.byteOffset).getUint32(0, false);
57
+ const controller = this.streamControllers.get(frame.streamId);
58
+ if (controller) {
59
+ controller.grantCredits(credits);
60
+ }
61
+ break;
62
+ }
63
+ case FrameType.STREAM_CANCEL: {
64
+ const emitter = this.activeEmitters.get(frame.streamId);
65
+ if (emitter) {
66
+ // Trigger the handler's onCancel callback so it can stop producing data
67
+ emitter.__cancelCallback?.();
68
+ this.activeEmitters.delete(frame.streamId);
69
+ }
70
+ const controller = this.streamControllers.get(frame.streamId);
71
+ if (controller) {
72
+ controller.cancel();
73
+ this.streamControllers.delete(frame.streamId);
74
+ }
75
+ break;
76
+ }
77
+ case FrameType.HANDSHAKE: {
78
+ this.handleHandshake(frame);
79
+ break;
80
+ }
81
+ }
82
+ }
83
+ handleRpcRequest(frame) {
84
+ try {
85
+ const reader = new BinaryReader(frame.payload);
86
+ const sessionId = reader.readString();
87
+ const service = reader.readString();
88
+ const method = reader.readString();
89
+ const token = reader.readString();
90
+ const reqPayload = reader.readBytes();
91
+ // Check ACL permissions if token is provided or expected
92
+ if (token && !this.gatekeeper.authorize(token, '*', service, method)) {
93
+ this.sendError(frame.streamId, `[Security] Access denied for ${service}.${method}`, sessionId);
94
+ return;
95
+ }
96
+ const handler = this.rpcHandlers.get(`${service}.${method}`);
97
+ if (!handler) {
98
+ this.sendError(frame.streamId, `[RpcError] Method not found: ${service}.${method}`, sessionId);
99
+ return;
100
+ }
101
+ handler(reqPayload)
102
+ .then((resPayload) => {
103
+ const respFrame = {
104
+ type: FrameType.RPC_RESP,
105
+ flags: FrameFlags.NONE,
106
+ streamId: frame.streamId,
107
+ sequenceId: this.sequenceId++,
108
+ payload: resPayload,
109
+ };
110
+ this.sendFrame(respFrame, sessionId);
111
+ })
112
+ .catch((err) => {
113
+ this.sendError(frame.streamId, err.message || String(err), sessionId);
114
+ });
115
+ }
116
+ catch (err) {
117
+ this.sendError(frame.streamId, `[MalformedRequest] ${err.message}`);
118
+ }
119
+ }
120
+ handleStreamInit(frame) {
121
+ try {
122
+ const reader = new BinaryReader(frame.payload);
123
+ const sessionId = reader.readString();
124
+ const service = reader.readString();
125
+ const method = reader.readString();
126
+ const token = reader.readString();
127
+ const reqPayload = reader.readBytes();
128
+ if (token && !this.gatekeeper.authorize(token, '*', service, method)) {
129
+ this.sendError(frame.streamId, `[Security] Access denied for ${service}.${method}`, sessionId);
130
+ return;
131
+ }
132
+ const handler = this.streamHandlers.get(`${service}.${method}`);
133
+ if (!handler) {
134
+ this.sendError(frame.streamId, `[StreamError] Stream method not found: ${service}.${method}`, sessionId);
135
+ return;
136
+ }
137
+ const controller = new StreamCreditController({ initialCredits: 8, ackBatchSize: 4 });
138
+ this.streamControllers.set(frame.streamId, controller);
139
+ let cancelCallback = null;
140
+ let closed = false;
141
+ const emitter = {
142
+ send: async (data) => {
143
+ if (closed)
144
+ return;
145
+ // Wait for available credit from client
146
+ await controller.acquireCredit();
147
+ if (closed)
148
+ return;
149
+ const dataFrame = {
150
+ type: FrameType.STREAM_DATA,
151
+ flags: FrameFlags.NONE,
152
+ streamId: frame.streamId,
153
+ sequenceId: this.sequenceId++,
154
+ payload: data,
155
+ };
156
+ this.sendFrame(dataFrame, sessionId);
157
+ },
158
+ close: () => {
159
+ if (closed)
160
+ return;
161
+ closed = true;
162
+ this.activeEmitters.delete(frame.streamId);
163
+ this.streamControllers.delete(frame.streamId);
164
+ const closeFrame = {
165
+ type: FrameType.STREAM_CLOSE,
166
+ flags: FrameFlags.END_STREAM,
167
+ streamId: frame.streamId,
168
+ sequenceId: this.sequenceId++,
169
+ payload: new Uint8Array(0),
170
+ };
171
+ this.sendFrame(closeFrame, sessionId);
172
+ },
173
+ error: (err) => {
174
+ if (closed)
175
+ return;
176
+ closed = true;
177
+ this.activeEmitters.delete(frame.streamId);
178
+ this.streamControllers.delete(frame.streamId);
179
+ this.sendError(frame.streamId, err.message, sessionId);
180
+ },
181
+ onCancel: (cb) => {
182
+ cancelCallback = cb;
183
+ emitter.__cancelCallback = cb;
184
+ },
185
+ };
186
+ this.activeEmitters.set(frame.streamId, emitter);
187
+ handler(reqPayload, emitter);
188
+ }
189
+ catch (err) {
190
+ this.sendError(frame.streamId, `[MalformedStreamInit] ${err.message}`);
191
+ }
192
+ }
193
+ handleHandshake(frame) {
194
+ try {
195
+ const reader = new BinaryReader(frame.payload);
196
+ const sessionId = reader.readString();
197
+ const lastAckSeq = reader.readUint32();
198
+ const session = this.sessionRegistry.getSession(sessionId);
199
+ if (!session) {
200
+ return;
201
+ }
202
+ // Replay lost frames from ring buffer
203
+ const replayFrames = session.ringBuffer.getFramesAfter(lastAckSeq);
204
+ for (const replay of replayFrames) {
205
+ const replayCopy = {
206
+ ...replay,
207
+ flags: replay.flags | FrameFlags.RESUMED,
208
+ };
209
+ const bytes = encodeFrame(replayCopy);
210
+ this.transport.send(bytes);
211
+ }
212
+ // Send Handshake ACK
213
+ const ackPayload = new BinaryWriter()
214
+ .writeString(sessionId)
215
+ .writeUint32(replayFrames.length)
216
+ .finish();
217
+ const ackFrame = {
218
+ type: FrameType.HANDSHAKE_ACK,
219
+ flags: FrameFlags.NONE,
220
+ streamId: 0,
221
+ sequenceId: this.sequenceId++,
222
+ payload: ackPayload,
223
+ };
224
+ const bytes = encodeFrame(ackFrame);
225
+ this.transport.send(bytes);
226
+ }
227
+ catch {
228
+ // Ignored
229
+ }
230
+ }
231
+ }
232
+ //# sourceMappingURL=host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../../../../packages/native-host/src/host.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,SAAS,EACT,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,GAChB,MAAM,yBAAyB,CAAC;AAYjC,MAAM,OAAO,gBAAgB;IAWjB;IAVF,WAAW,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC5C,cAAc,GAAG,IAAI,GAAG,EAAyB,CAAC;IAClD,cAAc,GAAG,IAAI,GAAG,EAAyB,CAAC;IAClD,iBAAiB,GAAG,IAAI,GAAG,EAAkC,CAAC;IAC9D,UAAU,GAAG,CAAC,CAAC;IAEhB,UAAU,CAAuB;IACjC,eAAe,CAAkB;IAExC,YACU,SAAoB,EAC5B,SAAkB;QADV,cAAS,GAAT,SAAS,CAAW;QAG5B,IAAI,CAAC,UAAU,GAAG,IAAI,oBAAoB,CAAC,SAAS,CAAC,CAAC;QACtD,IAAI,CAAC,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAE7C,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,WAAW,CAAC,OAAe,EAAE,MAAc,EAAE,OAAmB;QAC9D,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,OAAO,IAAI,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC;IAED,cAAc,CAAC,OAAe,EAAE,MAAc,EAAE,OAAsB;QACpE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,OAAO,IAAI,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC;IAC3D,CAAC;IAEO,cAAc,CAAC,KAAiB;QACtC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAEO,SAAS,CAAC,KAAkB,EAAE,SAAkB;QACtD,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;YACnE,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAEO,SAAS,CAAC,QAAgB,EAAE,OAAe,EAAE,SAAkB;QACrE,MAAM,UAAU,GAAgB;YAC9B,IAAI,EAAE,SAAS,CAAC,KAAK;YACrB,KAAK,EAAE,UAAU,CAAC,IAAI;YACtB,QAAQ;YACR,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE;YAC7B,OAAO,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC;SAC3C,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IACxC,CAAC;IAEO,WAAW,CAAC,KAAkB;QACpC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;gBACvB,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;gBAC7B,MAAM;YACR,CAAC;YAED,KAAK,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;gBAC3B,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;gBAC7B,MAAM;YACR,CAAC;YAED,KAAK,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACxB,MAAM,OAAO,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBACjG,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC9D,IAAI,UAAU,EAAE,CAAC;oBACf,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;gBACnC,CAAC;gBACD,MAAM;YACR,CAAC;YAED,KAAK,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;gBAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACxD,IAAI,OAAO,EAAE,CAAC;oBACZ,wEAAwE;oBACvE,OAAe,CAAC,gBAAgB,EAAE,EAAE,CAAC;oBACtC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC7C,CAAC;gBACD,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC9D,IAAI,UAAU,EAAE,CAAC;oBACf,UAAU,CAAC,MAAM,EAAE,CAAC;oBACpB,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAChD,CAAC;gBACD,MAAM;YACR,CAAC;YAGD,KAAK,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC;gBACzB,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;gBAC5B,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,KAAkB;QACzC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/C,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YACtC,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YAClC,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;YAEtC,yDAAyD;YACzD,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;gBACrE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,gCAAgC,OAAO,IAAI,MAAM,EAAE,EAAE,SAAS,CAAC,CAAC;gBAC/F,OAAO;YACT,CAAC;YAED,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,OAAO,IAAI,MAAM,EAAE,CAAC,CAAC;YAC7D,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,gCAAgC,OAAO,IAAI,MAAM,EAAE,EAAE,SAAS,CAAC,CAAC;gBAC/F,OAAO;YACT,CAAC;YAED,OAAO,CAAC,UAAU,CAAC;iBAChB,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE;gBACnB,MAAM,SAAS,GAAgB;oBAC7B,IAAI,EAAE,SAAS,CAAC,QAAQ;oBACxB,KAAK,EAAE,UAAU,CAAC,IAAI;oBACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE;oBAC7B,OAAO,EAAE,UAAU;iBACpB,CAAC;gBACF,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACvC,CAAC,CAAC;iBACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACb,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;YACxE,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,sBAAsB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,KAAkB;QACzC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/C,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YACtC,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YAClC,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;YAEtC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;gBACrE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,gCAAgC,OAAO,IAAI,MAAM,EAAE,EAAE,SAAS,CAAC,CAAC;gBAC/F,OAAO;YACT,CAAC;YAED,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,OAAO,IAAI,MAAM,EAAE,CAAC,CAAC;YAChE,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,0CAA0C,OAAO,IAAI,MAAM,EAAE,EAAE,SAAS,CAAC,CAAC;gBACzG,OAAO;YACT,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,sBAAsB,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;YACtF,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YAEvD,IAAI,cAAc,GAAwB,IAAI,CAAC;YAC/C,IAAI,MAAM,GAAG,KAAK,CAAC;YAEnB,MAAM,OAAO,GAAsD;gBACjE,IAAI,EAAE,KAAK,EAAE,IAAgB,EAAE,EAAE;oBAC/B,IAAI,MAAM;wBAAE,OAAO;oBACnB,wCAAwC;oBACxC,MAAM,UAAU,CAAC,aAAa,EAAE,CAAC;oBACjC,IAAI,MAAM;wBAAE,OAAO;oBAEnB,MAAM,SAAS,GAAgB;wBAC7B,IAAI,EAAE,SAAS,CAAC,WAAW;wBAC3B,KAAK,EAAE,UAAU,CAAC,IAAI;wBACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;wBACxB,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE;wBAC7B,OAAO,EAAE,IAAI;qBACd,CAAC;oBACF,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;gBACvC,CAAC;gBACD,KAAK,EAAE,GAAG,EAAE;oBACV,IAAI,MAAM;wBAAE,OAAO;oBACnB,MAAM,GAAG,IAAI,CAAC;oBACd,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBAC3C,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBAE9C,MAAM,UAAU,GAAgB;wBAC9B,IAAI,EAAE,SAAS,CAAC,YAAY;wBAC5B,KAAK,EAAE,UAAU,CAAC,UAAU;wBAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ;wBACxB,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE;wBAC7B,OAAO,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC;qBAC3B,CAAC;oBACF,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;gBACxC,CAAC;gBACD,KAAK,EAAE,CAAC,GAAU,EAAE,EAAE;oBACpB,IAAI,MAAM;wBAAE,OAAO;oBACnB,MAAM,GAAG,IAAI,CAAC;oBACd,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBAC3C,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBAC9C,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;gBACzD,CAAC;gBACD,QAAQ,EAAE,CAAC,EAAc,EAAE,EAAE;oBAC3B,cAAc,GAAG,EAAE,CAAC;oBACpB,OAAO,CAAC,gBAAgB,GAAG,EAAE,CAAC;gBAChC,CAAC;aACF,CAAC;YAGF,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACjD,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,yBAAyB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,KAAkB;QACxC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/C,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YACtC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YAEvC,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YAC3D,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO;YACT,CAAC;YAED,sCAAsC;YACtC,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;YACnE,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;gBAClC,MAAM,UAAU,GAAgB;oBAC9B,GAAG,MAAM;oBACT,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,UAAU,CAAC,OAAO;iBACzC,CAAC;gBACF,MAAM,KAAK,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;gBACtC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC7B,CAAC;YAED,qBAAqB;YACrB,MAAM,UAAU,GAAG,IAAI,YAAY,EAAE;iBAClC,WAAW,CAAC,SAAS,CAAC;iBACtB,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC;iBAChC,MAAM,EAAE,CAAC;YAEZ,MAAM,QAAQ,GAAgB;gBAC5B,IAAI,EAAE,SAAS,CAAC,aAAa;gBAC7B,KAAK,EAAE,UAAU,CAAC,IAAI;gBACtB,QAAQ,EAAE,CAAC;gBACX,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE;gBAC7B,OAAO,EAAE,UAAU;aACpB,CAAC;YACF,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;YACpC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,UAAU;QACZ,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export * from './host.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/native-host/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './host.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/native-host/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "aether-wv",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Aether-WV: The Heterogeneous Local IPC Fabric for Modern WebViews",
5
+ "main": "dist/index.js",
6
+ "files": [
7
+ "dist/index.*",
8
+ "dist/packages",
9
+ "bin",
10
+ "packages/platform-templates"
11
+ ],
12
+ "type": "module",
13
+ "bin": {
14
+ "aetherc": "./bin/aetherc.js"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "test": "tsc && node --test dist/tests/*.test.js",
19
+ "test:src": "tsc && node --test dist/tests/*.test.js",
20
+ "demo": "tsc && node dist/examples/run-demo.js",
21
+ "benchmark": "tsc && node dist/tests/benchmark.test.js"
22
+ },
23
+ "keywords": [
24
+ "webview",
25
+ "ipc",
26
+ "bridge",
27
+ "binary-protocol",
28
+ "reactive-streams",
29
+ "backpressure",
30
+ "wkwebview",
31
+ "android-webview"
32
+ ],
33
+ "author": "Aether Architecture Team",
34
+ "license": "Apache-2.0",
35
+ "devDependencies": {
36
+ "@types/node": "^22.0.0",
37
+ "typescript": "^5.6.0"
38
+ }
39
+ }
@@ -0,0 +1,57 @@
1
+ //
2
+ // AetherNativeDriver.kt
3
+ // Aether-WV Android Chromium Driver
4
+ //
5
+ // Production WebViewCompat integration with binary WebMessagePort and origin validation
6
+ //
7
+
8
+ package io.aether.webview
9
+
10
+ import android.net.Uri
11
+ import android.webkit.WebView
12
+ import androidx.webkit.WebMessageCompat
13
+ import androidx.webkit.WebMessagePortCompat
14
+ import androidx.webkit.WebViewCompat
15
+ import androidx.webkit.WebViewFeature
16
+ import java.util.concurrent.Executors
17
+
18
+ class AetherAndroidDriver(
19
+ private val webView: WebView,
20
+ private val allowedOrigins: Set<String>,
21
+ private val onFrameReceived: (ByteArray) -> Unit
22
+ ) {
23
+ private val backgroundExecutor = Executors.newSingleThreadExecutor()
24
+ private var nativePort: WebMessagePortCompat? = null
25
+
26
+ init {
27
+ setupDriver()
28
+ }
29
+
30
+ private fun setupDriver() {
31
+ if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
32
+ throw UnsupportedOperationException("WEB_MESSAGE_LISTENER is required for Aether-WV")
33
+ }
34
+
35
+ // Establish channel messaging on document load
36
+ WebViewCompat.addWebMessageListener(
37
+ webView,
38
+ "aetherNativeChannel",
39
+ allowedOrigins
40
+ ) { _, message, sourceOrigin, isMainFrame, _ ->
41
+ // Extract raw bytes or channel port
42
+ val bytes = message.data?.toByteArray(Charsets.ISO_8859_1)
43
+ if (bytes != null) {
44
+ backgroundExecutor.execute {
45
+ onFrameReceived(bytes)
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ fun sendFrameToWeb(data: ByteArray) {
52
+ val message = WebMessageCompat(data.toString(Charsets.ISO_8859_1))
53
+ webView.post {
54
+ nativePort?.postMessage(message)
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,78 @@
1
+ //
2
+ // AetherNativeDriver.swift
3
+ // Aether-WV iOS WebKit Driver
4
+ //
5
+ // Production WebKit multi-process integration using WKContentWorld and WKScriptMessageHandlerWithReply
6
+ //
7
+
8
+ import Foundation
9
+ import WebKit
10
+
11
+ public final class AetherIosDriver: NSObject, WKScriptMessageHandlerWithReply {
12
+ private weak var webView: WKWebView?
13
+ private let contentWorldName = "AetherIsolatedWorld"
14
+ private let handlerName = "aether"
15
+ private var onFrameReceived: ((Data) -> Void)?
16
+
17
+ public init(webView: WKWebView, onFrameReceived: @escaping (Data) -> Void) {
18
+ self.webView = webView
19
+ self.onFrameReceived = onFrameReceived
20
+ super.init()
21
+ self.setupDriver()
22
+ }
23
+
24
+ private func setupDriver() {
25
+ guard let webView = self.webView else { return }
26
+
27
+ let world = WKContentWorld.world(name: contentWorldName)
28
+ let ucc = webView.configuration.userContentController
29
+
30
+ // Register script message handler in isolated content world to prevent page XSS access
31
+ ucc.addScriptMessageHandler(self, contentWorld: world, name: handlerName)
32
+
33
+ // Inject bootstrap hook to bridge message to page context
34
+ let bootstrapScript = """
35
+ (function() {
36
+ if (window.__aether_initialized) return;
37
+ window.__aether_initialized = true;
38
+ console.log('[Aether-WV] Isolated bridge attached');
39
+ })();
40
+ """
41
+ let userScript = WKUserScript(source: bootstrapScript, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: world)
42
+ ucc.addUserScript(userScript)
43
+ }
44
+
45
+ // MARK: - WKScriptMessageHandlerWithReply
46
+ public func userContentController(
47
+ _ userContentController: WKUserContentController,
48
+ didReceive message: WKScriptMessage,
49
+ replyHandler: @escaping (Any?, String?) -> Void
50
+ ) {
51
+ guard let dict = message.body as? [String: Any],
52
+ let b64 = dict["payload"] as? String,
53
+ let data = Data(base64Encoded: b64) else {
54
+ replyHandler(nil, "[AetherIosDriver] Malformed binary payload")
55
+ return
56
+ }
57
+
58
+ // Process incoming frame on background queue
59
+ DispatchQueue.global(qos: .userInitiated).async { [weak self] in
60
+ self?.onFrameReceived?(data)
61
+ replyHandler(["status": "ack"], nil)
62
+ }
63
+ }
64
+
65
+ // MARK: - Dispatch frame back to WebView
66
+ public func sendFrameToWeb(data: Data) {
67
+ let b64 = data.base64EncodedString()
68
+ let js = "window.__aether_native_dispatch && window.__aether_native_dispatch('\(b64)');"
69
+
70
+ DispatchQueue.main.async { [weak self] in
71
+ self?.webView?.evaluateJavaScript(js, in: nil, in: .page) { _, error in
72
+ if let error = error {
73
+ print("[AetherIosDriver] evaluateJavaScript error: \(error.localizedDescription)")
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }