@g9tm/lynxcontrols-native 0.0.0-stage → 1.4.2
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 +85 -2
- package/android/build.gradle.kts +45 -0
- package/android/consumer-rules.pro +3 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/com/g9lynx/g9native/G9Bridge.kt +67 -0
- package/android/src/main/java/com/g9lynx/g9native/G9HostModule.kt +172 -0
- package/android/src/main/java/com/g9lynx/g9native/G9SqliteModule.kt +49 -0
- package/android/src/main/java/com/g9lynx/g9native/generated/G9HostModuleSpec.java +48 -0
- package/android/src/main/java/com/g9lynx/g9native/generated/G9SqliteModuleSpec.java +41 -0
- package/android/src/main/java/com/g9lynx/g9native/host/core/G9PreferencesCore.kt +100 -0
- package/android/src/main/java/com/g9lynx/g9native/sqlite/androidx/AndroidxSqlEngine.kt +88 -0
- package/android/src/main/java/com/g9lynx/g9native/sqlite/core/G9SqlEngine.kt +61 -0
- package/android/src/main/java/com/g9lynx/g9native/sqlite/core/G9SqliteCore.kt +260 -0
- package/android/src/main/java/com/g9lynx/g9native/sqlite/core/G9SqliteService.kt +131 -0
- package/android/src/main/java/com/g9lynx/g9native/sqlite/core/G9SqliteTransport.kt +125 -0
- package/android/src/main/java/com/g9lynx/g9native/sqlite/core/G9SqliteValues.kt +71 -0
- package/android/src/main/java/com/g9lynx/g9native/video/G9VideoUI.kt +220 -0
- package/android/src/main/java/com/g9lynx/g9native/video/core/G9VideoCore.kt +67 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/driver.d.ts +20 -0
- package/dist/driver.d.ts.map +1 -0
- package/dist/driver.js +159 -0
- package/dist/driver.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/media.d.ts +60 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.jsx +119 -0
- package/dist/media.jsx.map +1 -0
- package/dist/protocol.d.ts +115 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +1 -0
- package/dist/services.d.ts +36 -0
- package/dist/services.d.ts.map +1 -0
- package/dist/services.js +111 -0
- package/dist/services.js.map +1 -0
- package/dist/transport.d.ts +10 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +77 -0
- package/dist/transport.js.map +1 -0
- package/generated/G9HostModule.ts +71 -0
- package/generated/G9SqliteModule.ts +68 -0
- package/ios/Tests/G9NativeTestSuite.h +25 -0
- package/ios/Tests/G9NativeTestSuite.m +560 -0
- package/ios/Tests/G9NativeTests.m +52 -0
- package/ios/g9lynx-native.podspec +29 -0
- package/ios/src/G9HostModule.h +14 -0
- package/ios/src/G9HostModule.m +121 -0
- package/ios/src/G9SqliteModule.h +14 -0
- package/ios/src/G9SqliteModule.m +167 -0
- package/ios/src/G9VideoUI.h +21 -0
- package/ios/src/G9VideoUI.m +250 -0
- package/ios/src/core/G9HostPaths.h +20 -0
- package/ios/src/core/G9HostPaths.m +23 -0
- package/ios/src/core/G9PreferencesCore.h +43 -0
- package/ios/src/core/G9PreferencesCore.m +114 -0
- package/ios/src/core/G9SqliteCore.h +62 -0
- package/ios/src/core/G9SqliteCore.m +497 -0
- package/ios/src/core/G9SqliteTransport.h +16 -0
- package/ios/src/core/G9SqliteTransport.m +85 -0
- package/ios/src/core/G9VideoCore.h +26 -0
- package/ios/src/core/G9VideoCore.m +37 -0
- package/ios/src/generated/G9HostModuleSpec.h +23 -0
- package/ios/src/generated/G9HostModuleSpec.m +2 -0
- package/ios/src/generated/G9SqliteModuleSpec.h +20 -0
- package/ios/src/generated/G9SqliteModuleSpec.m +2 -0
- package/lynx.lib.json +12 -0
- package/package.json +49 -4
- package/types/platform-native-module.d.ts +46 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver.js","sources":["../src/driver.ts"],"sourcesContent":["// The mobile G9SqliteDriver over `NativeModules.G9SqliteModule` (plan/specs/06 §4–§6): one module call per batch,\n// requests of one session queued here (so a batch aborted while queued never reaches native code), replies decoded\n// from the columnar (default) or rows transport.\nimport {\n type G9DriverCapabilities,\n type G9OpenOptions,\n type G9RunOptions,\n type G9SessionToken,\n type G9SqliteDriver,\n G9SqliteError,\n type G9SqliteErrorCode,\n type G9SqlResult,\n type G9TxToken,\n pragmaStatements,\n} from '@g9tm/lynxcontrols-sqlite';\nimport type {\n G9NativeEnvelope,\n G9NativeError,\n G9NativeParam,\n G9NativeRunValue,\n G9NativeTransport,\n G9SqliteNativeModule,\n} from './protocol';\nimport { decodeRunValue } from './transport';\n\ndeclare const NativeModules: { G9SqliteModule?: G9SqliteNativeModule } | undefined;\n\n/** `NativeModules.G9SqliteModule`, or `null` where the native library is not linked. */\nexport function nativeSqliteModule(): G9SqliteNativeModule | null {\n try {\n return (typeof NativeModules === 'undefined' ? undefined : NativeModules?.G9SqliteModule) ?? null;\n } catch {\n return null;\n }\n}\n\nfunction toError(e: G9NativeError): G9SqliteError {\n return new G9SqliteError(e.code as G9SqliteErrorCode, e.message, {\n ...(e.sqliteCode !== undefined ? { sqliteCode: e.sqliteCode } : {}),\n ...(e.sql !== undefined ? { sql: e.sql } : {}),\n ...(e.completedStatements !== undefined ? { completedStatements: e.completedStatements } : {}),\n });\n}\n\n/** Calls a module method whose last argument is the reply callback; resolves the envelope's value. */\nfunction call<T>(invoke: (reply: (envelope: G9NativeEnvelope<T>) => void) => void): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n try {\n invoke((envelope) => {\n if (envelope?.ok) resolve(envelope.value);\n else reject(toError(envelope?.error ?? { code: 'other', message: 'The native module sent no reply.' }));\n });\n } catch (error) {\n reject(new G9SqliteError('other', `The native module call failed: ${(error as Error)?.message ?? error}`));\n }\n });\n}\n\n/** A driver value as the bridge carries it (`undefined` tagged, so native code rejects it in statement order). */\nfunction param(value: unknown): G9NativeParam {\n return value === undefined ? { undefined: true } : (value as G9NativeParam);\n}\n\n/** Options of {@link createNativeSqliteDriver}. */\nexport interface G9NativeDriverOptions {\n /** The module (default: `NativeModules.G9SqliteModule`). Tests pass a fake. */\n readonly module?: G9SqliteNativeModule;\n /** Result transport (default `'columnar'`, specs/06 §6). */\n readonly transport?: G9NativeTransport;\n readonly name?: string;\n}\n\n/**\n * The Android/iOS {@link G9SqliteDriver}: androidx.sqlite `BundledSQLiteDriver` on Android, system `sqlite3` on iOS,\n * one native thread/queue per open database. Cancellation: a batch aborted while queued never starts; aborting a running\n * batch interrupts it on iOS (`sqlite3_interrupt`) and lets it finish on Android (`capabilities().interrupt`).\n * Throws `misuse` when the native library is not linked and no module is given.\n */\nexport function createNativeSqliteDriver(options: G9NativeDriverOptions = {}): G9SqliteDriver {\n const native = options.module ?? nativeSqliteModule();\n if (!native) {\n throw new G9SqliteError(\n 'misuse',\n 'NativeModules.G9SqliteModule is missing: add @g9tm/lynxcontrols-native to the app so Autolink links it.',\n );\n }\n const transport: G9NativeTransport = options.transport ?? 'columnar';\n const queues = new Map<string, Promise<unknown>>();\n const txSession = new Map<string, string>();\n function enqueue<T>(session: string, task: () => Promise<T>): Promise<T> {\n const previous = queues.get(session) ?? Promise.resolve();\n const next = previous.then(task, task);\n queues.set(\n session,\n next.then(\n () => undefined,\n () => undefined,\n ),\n );\n return next;\n }\n const unknownTx = () => Promise.reject(new G9SqliteError('misuse', 'The transaction is finished or unknown.'));\n return {\n name: options.name ?? 'native',\n async capabilities(): Promise<G9DriverCapabilities> {\n const value = await call<Record<string, unknown>>((reply) => native.capabilities(reply));\n return Object.freeze({\n sqliteVersion: String(value.sqliteVersion),\n maxVariables: Number(value.maxVariables),\n json1: value.json1 === true,\n returning: value.returning === true,\n upsert: value.upsert === true,\n interrupt: value.interrupt === true,\n });\n },\n async open(open: G9OpenOptions): Promise<G9SessionToken> {\n const token = await call<string>((reply) =>\n native.open(\n {\n path: open.path,\n ...(open.readOnly !== undefined ? { readOnly: open.readOnly } : {}),\n ...(open.create !== undefined ? { create: open.create } : {}),\n pragmas: pragmaStatements(open.pragmas ?? {}, open.durability),\n },\n reply,\n ),\n );\n return token as G9SessionToken;\n },\n run(session, statements, run: G9RunOptions = {}): Promise<G9SqlResult[]> {\n const cancelled = () => new G9SqliteError('cancelled', 'Cancelled before the batch started.');\n if (run.signal?.aborted) return Promise.reject(cancelled());\n return enqueue(session, async () => {\n if (run.signal?.aborted) throw cancelled();\n // Aborting while the batch runs interrupts it where the platform can (iOS); the batch then fails `interrupted`.\n const onAbort = () => native.interrupt?.(session);\n run.signal?.addEventListener('abort', onAbort);\n try {\n const value = await call<G9NativeRunValue>((reply) =>\n native.run(\n session,\n statements.map((s) => ({ sql: s.sql, params: s.params.map(param) })),\n {\n ...(run.tx !== undefined ? { tx: run.tx } : {}),\n ...(run.maxRows !== undefined ? { maxRows: run.maxRows } : {}),\n ...(run.atomic !== undefined ? { atomic: run.atomic } : {}),\n transport,\n },\n reply,\n ),\n );\n return decodeRunValue(value);\n } finally {\n run.signal?.removeEventListener('abort', onAbort);\n }\n });\n },\n begin(session, mode): Promise<G9TxToken> {\n return enqueue(session, async () => {\n const tx = await call<string>((reply) => native.begin(session, mode, reply));\n txSession.set(tx, session);\n return tx as G9TxToken;\n });\n },\n commit(tx): Promise<void> {\n if (typeof tx !== 'string') return unknownTx();\n return enqueue(txSession.get(tx) ?? `#${tx}`, async () => {\n await call<null>((reply) => native.commit(tx, reply));\n txSession.delete(tx);\n });\n },\n rollback(tx): Promise<void> {\n if (typeof tx !== 'string') return unknownTx();\n return enqueue(txSession.get(tx) ?? `#${tx}`, async () => {\n txSession.delete(tx);\n await call<null>((reply) => native.rollback(tx, reply));\n });\n },\n close(session): Promise<void> {\n if (typeof session !== 'string') {\n return Promise.reject(new G9SqliteError('misuse', 'The database session is closed or unknown.'));\n }\n return enqueue(session, async () => {\n await call<null>((reply) => native.close(session, reply));\n queues.delete(session);\n });\n },\n };\n}\n"],"names":["nativeSqliteModule","_ref","NativeModules","undefined","toError","e","G9SqliteError","call","invoke","Promise","resolve","reject","envelope","error","param","value","createNativeSqliteDriver","options","_options_module","_options_transport","_options_name","native","transport","queues","Map","txSession","enqueue","session","task","_queues_get","previous","next","unknownTx","reply","Object","String","Number","open","token","_open_pragmas","pragmaStatements","statements","run","_run_signal","cancelled","_run_signal1","onAbort","s","decodeRunValue","_run_signal2","mode","tx","_txSession_get"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createNativeSqliteDriver, type G9NativeDriverOptions, nativeSqliteModule } from './driver.js';
|
|
2
|
+
export type * from './protocol.js';
|
|
3
|
+
export { createNativeHostServices, type G9InsetsSubscriber, type G9NativeHostOptions, type G9NativeServices, lynxInsetsEvents, nativeHostModule, } from './services.js';
|
|
4
|
+
export { decodeRunValue, toArrayBuffer } from './transport.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,wBAAwB,EAAE,KAAK,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACpG,mBAAmB,YAAY,CAAC;AAChC,OAAO,EACL,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
package/dist/media.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type G9MediaProvider } from '@g9tm/lynxcontrols-foundation';
|
|
2
|
+
import type { G9HostNativeModule } from './protocol.js';
|
|
3
|
+
/** Props of the `<g9-video>` element (`G9VideoUI` on Android and iOS). */
|
|
4
|
+
export interface G9VideoElementProps {
|
|
5
|
+
/** `asset:///…` (Android), `file://…` (iOS), or `http(s)://…`. */
|
|
6
|
+
readonly src: string;
|
|
7
|
+
/** Default true: the element starts paused. */
|
|
8
|
+
readonly paused?: boolean;
|
|
9
|
+
/** Default true. */
|
|
10
|
+
readonly loop?: boolean;
|
|
11
|
+
/** Default true. */
|
|
12
|
+
readonly muted?: boolean;
|
|
13
|
+
/** One best-effort seek after the media opened. */
|
|
14
|
+
readonly 'start-seconds'?: number;
|
|
15
|
+
/** How early `nearend` fires before each loop's end. */
|
|
16
|
+
readonly 'near-end-seconds'?: number;
|
|
17
|
+
/** `cover` (default, fill and crop) or `contain`. */
|
|
18
|
+
readonly 'object-fit'?: 'cover' | 'contain';
|
|
19
|
+
readonly className?: string;
|
|
20
|
+
readonly style?: Record<string, string | number>;
|
|
21
|
+
/** The first frame is on screen. */
|
|
22
|
+
readonly bindready?: () => void;
|
|
23
|
+
readonly bindnearend?: (event: {
|
|
24
|
+
readonly detail: {
|
|
25
|
+
readonly seconds: number;
|
|
26
|
+
};
|
|
27
|
+
}) => void;
|
|
28
|
+
readonly bindlooped?: () => void;
|
|
29
|
+
readonly binderror?: (event: {
|
|
30
|
+
readonly detail: {
|
|
31
|
+
readonly code: string;
|
|
32
|
+
readonly message: string;
|
|
33
|
+
};
|
|
34
|
+
}) => void;
|
|
35
|
+
}
|
|
36
|
+
declare module '@lynx-js/types' {
|
|
37
|
+
interface IntrinsicElements {
|
|
38
|
+
'g9-video': G9VideoElementProps;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Options of {@link createNativeMediaProvider}. */
|
|
42
|
+
export interface G9NativeMediaOptions {
|
|
43
|
+
/** The host module (default `NativeModules.G9HostModule`). Tests pass a fake. */
|
|
44
|
+
readonly module?: G9HostNativeModule;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the host registered `<g9-video>` (Android: `G9VideoBehaviors.create()` among the LynxView behaviours; iOS:
|
|
47
|
+
* linked `g9lynx-native`, which self-registers it). Default true; false makes the carousel show its images with
|
|
48
|
+
* `'video-unsupported'`.
|
|
49
|
+
*/
|
|
50
|
+
readonly videoElement?: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* `services.media` for Android and iOS (MAUI `G9IntroMediaResolver` + `MediaElement`): `resolve` turns a bundled
|
|
54
|
+
* asset into the URI the platform loaders open (`G9HostModule.assetUri`; `not-found` when the app does not ship it)
|
|
55
|
+
* and passes `uri` sources through; `createPlayer` returns a player whose `view` is a `<g9-video>` element that plays
|
|
56
|
+
* muted and looping, reports `ready` / `nearEnd` / `looped` / `error`, and is released when disposed or unmounted.
|
|
57
|
+
* Throws outside a host that linked `@g9tm/lynxcontrols-native`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function createNativeMediaProvider(options?: G9NativeMediaOptions): G9MediaProvider;
|
|
60
|
+
//# sourceMappingURL=media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.tsx"],"names":[],"mappings":"AAGA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,kBAAkB,EAAoB,MAAM,YAAY,CAAC;AAQvE,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,oBAAoB;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,oBAAoB;IACpB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,wDAAwD;IACxD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,qDAAqD;IACrD,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IACjD,oCAAoC;IACpC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE;YAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1F,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,KAAK,IAAI,CAAC;CAChH;AAED,OAAO,QAAQ,gBAAgB,CAAC;IAC9B,UAAU,iBAAiB;QACzB,UAAU,EAAE,mBAAmB,CAAC;KACjC;CACF;AAED,oDAAoD;AACpD,MAAM,WAAW,oBAAoB;IACnC,iFAAiF;IACjF,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAqED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,oBAAyB,GAAG,eAAe,CAyB7F"}
|
package/dist/media.jsx
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { G9Error } from "@g9tm/lynxcontrols-foundation";
|
|
2
|
+
import { useSyncExternalStore } from "@lynx-js/react";
|
|
3
|
+
const hostModule = ()=>{
|
|
4
|
+
var _ref;
|
|
5
|
+
return null != (_ref = "u" < typeof NativeModules ? void 0 : null == NativeModules ? void 0 : NativeModules.G9HostModule) ? _ref : null;
|
|
6
|
+
};
|
|
7
|
+
function nativePlayer(media, options) {
|
|
8
|
+
let state = {
|
|
9
|
+
paused: true,
|
|
10
|
+
disposed: false
|
|
11
|
+
};
|
|
12
|
+
const views = new Set();
|
|
13
|
+
const listeners = new Set();
|
|
14
|
+
const setState = (next)=>{
|
|
15
|
+
if (state.disposed) return;
|
|
16
|
+
state = {
|
|
17
|
+
...state,
|
|
18
|
+
...next
|
|
19
|
+
};
|
|
20
|
+
for (const view of Array.from(views))view();
|
|
21
|
+
};
|
|
22
|
+
const emit = (event)=>{
|
|
23
|
+
if (state.disposed) return;
|
|
24
|
+
for (const listener of Array.from(listeners))listener(event);
|
|
25
|
+
};
|
|
26
|
+
const subscribeView = (onChange)=>{
|
|
27
|
+
views.add(onChange);
|
|
28
|
+
return ()=>{
|
|
29
|
+
views.delete(onChange);
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const getState = ()=>state;
|
|
33
|
+
function G9NativeVideo() {
|
|
34
|
+
var _options_loop, _options_muted;
|
|
35
|
+
const { paused, disposed } = useSyncExternalStore(subscribeView, getState);
|
|
36
|
+
if (disposed) return null;
|
|
37
|
+
return <g9-video src={media.uri} paused={paused} loop={null != (_options_loop = options.loop) ? _options_loop : true} muted={null != (_options_muted = options.muted) ? _options_muted : true} object-fit="cover" {...options.startSeconds ? {
|
|
38
|
+
'start-seconds': options.startSeconds
|
|
39
|
+
} : {}} {...options.nearEndSeconds ? {
|
|
40
|
+
'near-end-seconds': options.nearEndSeconds
|
|
41
|
+
} : {}} style={{
|
|
42
|
+
width: '100%',
|
|
43
|
+
height: '100%'
|
|
44
|
+
}} bindready={()=>emit({
|
|
45
|
+
type: 'ready'
|
|
46
|
+
})} bindnearend={(e)=>emit({
|
|
47
|
+
type: 'nearEnd',
|
|
48
|
+
seconds: e.detail.seconds
|
|
49
|
+
})} bindlooped={()=>emit({
|
|
50
|
+
type: 'looped'
|
|
51
|
+
})} binderror={(e)=>emit({
|
|
52
|
+
type: 'error',
|
|
53
|
+
error: new G9Error(`video-${e.detail.code}`, e.detail.message)
|
|
54
|
+
})}/>;
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
view: <G9NativeVideo/>,
|
|
58
|
+
play: ()=>setState({
|
|
59
|
+
paused: false
|
|
60
|
+
}),
|
|
61
|
+
pause: ()=>setState({
|
|
62
|
+
paused: true
|
|
63
|
+
}),
|
|
64
|
+
dispose () {
|
|
65
|
+
if (state.disposed) return;
|
|
66
|
+
state = {
|
|
67
|
+
...state,
|
|
68
|
+
disposed: true
|
|
69
|
+
};
|
|
70
|
+
for (const view of Array.from(views))view();
|
|
71
|
+
listeners.clear();
|
|
72
|
+
},
|
|
73
|
+
subscribe (listener) {
|
|
74
|
+
listeners.add(listener);
|
|
75
|
+
return ()=>{
|
|
76
|
+
listeners.delete(listener);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function createNativeMediaProvider(options = {}) {
|
|
82
|
+
var _options_module;
|
|
83
|
+
const native = null != (_options_module = options.module) ? _options_module : hostModule();
|
|
84
|
+
if (!native) throw new Error('NativeModules.G9HostModule is missing: add @g9tm/lynxcontrols-native to the app so Autolink links it.');
|
|
85
|
+
return {
|
|
86
|
+
async resolve (source, resolveOptions) {
|
|
87
|
+
var _resolveOptions_signal;
|
|
88
|
+
if (null == resolveOptions ? void 0 : null == (_resolveOptions_signal = resolveOptions.signal) ? void 0 : _resolveOptions_signal.aborted) throw new G9Error('aborted', 'The media request was cancelled.');
|
|
89
|
+
if ('uri' in source) return {
|
|
90
|
+
kind: source.kind,
|
|
91
|
+
uri: source.uri
|
|
92
|
+
};
|
|
93
|
+
const uri = await new Promise((resolve, reject)=>{
|
|
94
|
+
native.assetUri(source.asset, (envelope)=>{
|
|
95
|
+
if ((null == envelope ? void 0 : envelope.ok) && 'string' == typeof envelope.value) resolve(envelope.value);
|
|
96
|
+
else {
|
|
97
|
+
const e = envelope && !envelope.ok ? envelope.error : {
|
|
98
|
+
code: 'not-found',
|
|
99
|
+
message: 'No reply.'
|
|
100
|
+
};
|
|
101
|
+
reject(new G9Error(e.code, e.message));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
kind: source.kind,
|
|
107
|
+
uri
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
supportsVideo: ()=>{
|
|
111
|
+
var _options_videoElement;
|
|
112
|
+
return null != (_options_videoElement = options.videoElement) ? _options_videoElement : true;
|
|
113
|
+
},
|
|
114
|
+
createPlayer: (media, playerOptions = {})=>nativePlayer(media, playerOptions)
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export { createNativeMediaProvider };
|
|
118
|
+
|
|
119
|
+
//# sourceMappingURL=media.jsx.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.jsx","sources":["../src/media.tsx"],"sourcesContent":["// @g9tm/lynxcontrols-native/media, the mobile media provider (plan/specs/06 §7): bundled-asset resolution through `G9HostModule.assetUri` and video\n// players rendered as the `<g9-video>` element @g9tm/lynxcontrols-native registers (Android Media3, iOS AVFoundation). The\n// intro carousel takes it as `services.media`.\nimport {\n G9Error,\n type G9MediaPlayer,\n type G9MediaPlayerEvent,\n type G9MediaPlayerOptions,\n type G9MediaProvider,\n type G9ResolvedMedia,\n} from '@g9tm/lynxcontrols-foundation';\nimport { type ReactNode, useSyncExternalStore } from '@lynx-js/react';\nimport type { G9HostNativeModule, G9NativeEnvelope } from './protocol';\n\n// Read directly (not imported from ./services): this entry is built on its own as .jsx and has no runtime imports of\n// the package's other files.\ndeclare const NativeModules: { G9HostModule?: G9HostNativeModule } | undefined;\nconst hostModule = (): G9HostNativeModule | null =>\n (typeof NativeModules === 'undefined' ? undefined : NativeModules?.G9HostModule) ?? null;\n\n/** Props of the `<g9-video>` element (`G9VideoUI` on Android and iOS). */\nexport interface G9VideoElementProps {\n /** `asset:///…` (Android), `file://…` (iOS), or `http(s)://…`. */\n readonly src: string;\n /** Default true: the element starts paused. */\n readonly paused?: boolean;\n /** Default true. */\n readonly loop?: boolean;\n /** Default true. */\n readonly muted?: boolean;\n /** One best-effort seek after the media opened. */\n readonly 'start-seconds'?: number;\n /** How early `nearend` fires before each loop's end. */\n readonly 'near-end-seconds'?: number;\n /** `cover` (default, fill and crop) or `contain`. */\n readonly 'object-fit'?: 'cover' | 'contain';\n readonly className?: string;\n readonly style?: Record<string, string | number>;\n /** The first frame is on screen. */\n readonly bindready?: () => void;\n readonly bindnearend?: (event: { readonly detail: { readonly seconds: number } }) => void;\n readonly bindlooped?: () => void;\n readonly binderror?: (event: { readonly detail: { readonly code: string; readonly message: string } }) => void;\n}\n\ndeclare module '@lynx-js/types' {\n interface IntrinsicElements {\n 'g9-video': G9VideoElementProps;\n }\n}\n\n/** Options of {@link createNativeMediaProvider}. */\nexport interface G9NativeMediaOptions {\n /** The host module (default `NativeModules.G9HostModule`). Tests pass a fake. */\n readonly module?: G9HostNativeModule;\n /**\n * Whether the host registered `<g9-video>` (Android: `G9VideoBehaviors.create()` among the LynxView behaviours; iOS:\n * linked `g9lynx-native`, which self-registers it). Default true; false makes the carousel show its images with\n * `'video-unsupported'`.\n */\n readonly videoElement?: boolean;\n}\n\ninterface PlayerState {\n readonly paused: boolean;\n readonly disposed: boolean;\n}\n\nfunction nativePlayer(media: G9ResolvedMedia, options: G9MediaPlayerOptions): G9MediaPlayer {\n let state: PlayerState = { paused: true, disposed: false };\n const views = new Set<() => void>();\n const listeners = new Set<(event: G9MediaPlayerEvent) => void>();\n const setState = (next: Partial<PlayerState>) => {\n if (state.disposed) return;\n state = { ...state, ...next };\n for (const view of Array.from(views)) view();\n };\n const emit = (event: G9MediaPlayerEvent) => {\n if (state.disposed) return;\n for (const listener of Array.from(listeners)) listener(event);\n };\n const subscribeView = (onChange: () => void) => {\n views.add(onChange);\n return () => {\n views.delete(onChange);\n };\n };\n const getState = () => state;\n\n function G9NativeVideo(): ReactNode {\n const { paused, disposed } = useSyncExternalStore(subscribeView, getState);\n if (disposed) return null;\n return (\n <g9-video\n src={media.uri}\n paused={paused}\n loop={options.loop ?? true}\n muted={options.muted ?? true}\n object-fit=\"cover\"\n {...(options.startSeconds ? { 'start-seconds': options.startSeconds } : {})}\n {...(options.nearEndSeconds ? { 'near-end-seconds': options.nearEndSeconds } : {})}\n style={{ width: '100%', height: '100%' }}\n bindready={() => emit({ type: 'ready' })}\n bindnearend={(e) => emit({ type: 'nearEnd', seconds: e.detail.seconds })}\n bindlooped={() => emit({ type: 'looped' })}\n binderror={(e) => emit({ type: 'error', error: new G9Error(`video-${e.detail.code}`, e.detail.message) })}\n />\n );\n }\n\n return {\n view: <G9NativeVideo />,\n play: () => setState({ paused: false }),\n pause: () => setState({ paused: true }),\n dispose() {\n if (state.disposed) return;\n // The element unmounts with the state change; the native element releases its player when destroyed.\n state = { ...state, disposed: true };\n for (const view of Array.from(views)) view();\n listeners.clear();\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\n/**\n * `services.media` for Android and iOS (MAUI `G9IntroMediaResolver` + `MediaElement`): `resolve` turns a bundled\n * asset into the URI the platform loaders open (`G9HostModule.assetUri`; `not-found` when the app does not ship it)\n * and passes `uri` sources through; `createPlayer` returns a player whose `view` is a `<g9-video>` element that plays\n * muted and looping, reports `ready` / `nearEnd` / `looped` / `error`, and is released when disposed or unmounted.\n * Throws outside a host that linked `@g9tm/lynxcontrols-native`.\n */\nexport function createNativeMediaProvider(options: G9NativeMediaOptions = {}): G9MediaProvider {\n const native = options.module ?? hostModule();\n if (!native) {\n throw new Error(\n 'NativeModules.G9HostModule is missing: add @g9tm/lynxcontrols-native to the app so Autolink links it.',\n );\n }\n return {\n async resolve(source, resolveOptions) {\n if (resolveOptions?.signal?.aborted) throw new G9Error('aborted', 'The media request was cancelled.');\n if ('uri' in source) return { kind: source.kind, uri: source.uri };\n const uri = await new Promise<string>((resolve, reject) => {\n native.assetUri(source.asset, (envelope: G9NativeEnvelope<string>) => {\n if (envelope?.ok && typeof envelope.value === 'string') resolve(envelope.value);\n else {\n const e = envelope && !envelope.ok ? envelope.error : { code: 'not-found', message: 'No reply.' };\n reject(new G9Error(e.code, e.message));\n }\n });\n });\n return { kind: source.kind, uri };\n },\n supportsVideo: () => options.videoElement ?? true,\n createPlayer: (media, playerOptions = {}) => nativePlayer(media, playerOptions),\n };\n}\n"],"names":["hostModule","_ref","NativeModules","undefined","nativePlayer","media","options","state","views","Set","listeners","setState","next","view","Array","emit","event","listener","subscribeView","onChange","getState","G9NativeVideo","_options_loop","_options_muted","paused","disposed","useSyncExternalStore","g9-video","e","G9Error","createNativeMediaProvider","_options_module","native","Error","source","resolveOptions","_resolveOptions_signal","uri","Promise","resolve","reject","envelope","_options_videoElement","playerOptions"],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** A parameter on the wire: numbers as JS numbers, int64 as `{ int64 }`, BLOBs as ArrayBuffer, `undefined` tagged. */
|
|
2
|
+
export type G9NativeParam = null | number | string | ArrayBuffer | {
|
|
3
|
+
readonly int64: string;
|
|
4
|
+
} | {
|
|
5
|
+
readonly undefined: true;
|
|
6
|
+
};
|
|
7
|
+
/** A `G9SqliteError` as plain data. */
|
|
8
|
+
export interface G9NativeError {
|
|
9
|
+
readonly code: string;
|
|
10
|
+
readonly message: string;
|
|
11
|
+
readonly sqliteCode?: number;
|
|
12
|
+
readonly sql?: string;
|
|
13
|
+
readonly completedStatements?: number;
|
|
14
|
+
}
|
|
15
|
+
/** Every module reply: errors are data so their fields survive the bridge. */
|
|
16
|
+
export type G9NativeEnvelope<T> = {
|
|
17
|
+
readonly ok: true;
|
|
18
|
+
readonly value: T;
|
|
19
|
+
} | {
|
|
20
|
+
readonly ok: false;
|
|
21
|
+
readonly error: G9NativeError;
|
|
22
|
+
};
|
|
23
|
+
/** How results cross (specs/06 §6): `columnar` (C, default) or `rows` (A). */
|
|
24
|
+
export type G9NativeTransport = 'columnar' | 'rows';
|
|
25
|
+
/** Transport C: one result as columns + cell tags + payload buffers (see `G9SqliteTransport.kt`). */
|
|
26
|
+
export interface G9ColumnarResult {
|
|
27
|
+
readonly columns: string[];
|
|
28
|
+
readonly rowCount: number;
|
|
29
|
+
readonly changes: number;
|
|
30
|
+
readonly lastInsertRowId: number | {
|
|
31
|
+
readonly int64: string;
|
|
32
|
+
};
|
|
33
|
+
/** One tag byte per cell in row order: 0 null, 1 number, 2 int64 text, 3 text, 4 blob. */
|
|
34
|
+
readonly types: ArrayBuffer;
|
|
35
|
+
/** Little-endian float64: each number cell, and each blob's byte length. */
|
|
36
|
+
readonly numbers: ArrayBuffer;
|
|
37
|
+
/** Text cells and int64 decimals, in cell order. */
|
|
38
|
+
readonly strings: string[];
|
|
39
|
+
/** Blob bytes back to back. */
|
|
40
|
+
readonly blobs: ArrayBuffer;
|
|
41
|
+
}
|
|
42
|
+
/** Transport A: rows of tagged cells. */
|
|
43
|
+
export interface G9RowsResult {
|
|
44
|
+
readonly columns: string[];
|
|
45
|
+
readonly rows: Array<Array<null | number | string | ArrayBuffer | {
|
|
46
|
+
readonly int64: string;
|
|
47
|
+
}>>;
|
|
48
|
+
readonly changes: number;
|
|
49
|
+
readonly lastInsertRowId: number | {
|
|
50
|
+
readonly int64: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** A successful `run` reply of the native module: results in the transport the session was opened with. */
|
|
54
|
+
export type G9NativeRunValue = {
|
|
55
|
+
readonly transport: 'columnar';
|
|
56
|
+
readonly results: G9ColumnarResult[];
|
|
57
|
+
} | {
|
|
58
|
+
readonly transport: 'rows';
|
|
59
|
+
readonly results: G9RowsResult[];
|
|
60
|
+
};
|
|
61
|
+
type Reply<T> = (reply: G9NativeEnvelope<T>) => void;
|
|
62
|
+
/** `NativeModules.G9SqliteModule`. */
|
|
63
|
+
export interface G9SqliteNativeModule {
|
|
64
|
+
capabilities(callback: Reply<Record<string, unknown>>): void;
|
|
65
|
+
open(options: {
|
|
66
|
+
path: string;
|
|
67
|
+
readOnly?: boolean;
|
|
68
|
+
create?: boolean;
|
|
69
|
+
pragmas: string[];
|
|
70
|
+
}, callback: Reply<string>): void;
|
|
71
|
+
run(session: string, statements: Array<{
|
|
72
|
+
sql: string;
|
|
73
|
+
params: G9NativeParam[];
|
|
74
|
+
}>, options: {
|
|
75
|
+
tx?: string;
|
|
76
|
+
maxRows?: number;
|
|
77
|
+
atomic?: boolean;
|
|
78
|
+
transport: G9NativeTransport;
|
|
79
|
+
}, callback: Reply<G9NativeRunValue>): void;
|
|
80
|
+
begin(session: string, mode: string, callback: Reply<string>): void;
|
|
81
|
+
commit(tx: string, callback: Reply<null>): void;
|
|
82
|
+
rollback(tx: string, callback: Reply<null>): void;
|
|
83
|
+
close(session: string, callback: Reply<null>): void;
|
|
84
|
+
/** Interrupts the running statement (iOS); a no-op on Android. Absent on older builds of the module. */
|
|
85
|
+
interrupt?(session: string): void;
|
|
86
|
+
}
|
|
87
|
+
/** A stored preference as the host module reports it. */
|
|
88
|
+
export type G9NativePreference = {
|
|
89
|
+
readonly type: 'int';
|
|
90
|
+
readonly value: number;
|
|
91
|
+
} | {
|
|
92
|
+
readonly type: 'string';
|
|
93
|
+
readonly value: string;
|
|
94
|
+
};
|
|
95
|
+
/** `NativeModules.G9HostModule`. */
|
|
96
|
+
export interface G9HostNativeModule {
|
|
97
|
+
capabilities(callback: Reply<Record<string, unknown>>): void;
|
|
98
|
+
getPreference(key: string, callback: Reply<G9NativePreference | null>): void;
|
|
99
|
+
setPreference(key: string, kind: 'int' | 'string' | 'remove', value: string | null, callback: Reply<null>): void;
|
|
100
|
+
preferenceEntries(prefix: string, callback: Reply<Array<[string, number | string]>>): void;
|
|
101
|
+
safeInsets(callback: Reply<{
|
|
102
|
+
top: number;
|
|
103
|
+
end: number;
|
|
104
|
+
bottom: number;
|
|
105
|
+
start: number;
|
|
106
|
+
}>): void;
|
|
107
|
+
moveToBack(): void;
|
|
108
|
+
hideKeyboard(): void;
|
|
109
|
+
setSoftInputMode(mode: string): void;
|
|
110
|
+
hapticTick(): void;
|
|
111
|
+
appDataDirectory(callback: Reply<string>): void;
|
|
112
|
+
assetUri(path: string, callback: Reply<string>): void;
|
|
113
|
+
}
|
|
114
|
+
export {};
|
|
115
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type G9HostServices, type G9PreferenceStore, type G9SafeInsets } from '@g9tm/lynxcontrols-foundation';
|
|
2
|
+
import type { G9HostNativeModule } from './protocol.js';
|
|
3
|
+
/** `NativeModules.G9HostModule`, or `null` where the native library is not linked. */
|
|
4
|
+
export declare function nativeHostModule(): G9HostNativeModule | null;
|
|
5
|
+
/** Subscribes to the host's insets event; returns the unsubscribe function. */
|
|
6
|
+
export type G9InsetsSubscriber = (listener: (insets: G9SafeInsets) => void) => () => void;
|
|
7
|
+
/** The default insets source: the `g9:insets` global event a host sends on rotation / resize (Lynx background thread). */
|
|
8
|
+
export declare function lynxInsetsEvents(): G9InsetsSubscriber;
|
|
9
|
+
/** Options of {@link createNativeHostServices}. */
|
|
10
|
+
export interface G9NativeHostOptions {
|
|
11
|
+
/** The module (default: `NativeModules.G9HostModule`). Tests pass a fake. */
|
|
12
|
+
readonly module?: G9HostNativeModule;
|
|
13
|
+
/** Where inset changes come from (default: {@link lynxInsetsEvents}). */
|
|
14
|
+
readonly insetsEvents?: G9InsetsSubscriber;
|
|
15
|
+
}
|
|
16
|
+
/** What {@link createNativeHostServices} returns: the services for `G9Provider`, plus the optional haptic tick. */
|
|
17
|
+
export interface G9NativeServices {
|
|
18
|
+
readonly host: G9HostServices;
|
|
19
|
+
readonly preferences: G9PreferenceStore;
|
|
20
|
+
/** A light haptic tick (Android `HapticFeedbackConstants`, iOS `UIImpactFeedbackGenerator`). */
|
|
21
|
+
hapticTick(): void;
|
|
22
|
+
/**
|
|
23
|
+
* The app's private data directory, absolute (MAUI `FileSystem.AppDataDirectory`: Android `Context.filesDir`, iOS
|
|
24
|
+
* the user `Library` directory), so the database keeps the path its MAUI version used. Pass it to a database
|
|
25
|
+
* locator (`singleFileDatabaseLocator(dir + '/g9.db')` or `perUserDatabaseLocator({ root: dir, … })`).
|
|
26
|
+
* Rejects with `host-data-directory` when the host reports none.
|
|
27
|
+
*/
|
|
28
|
+
appDataDirectory(): Promise<string>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Android/iOS host services: `exit` moves the task to the back (Android; iOS has no programmatic exit, the module
|
|
32
|
+
* ignores it), `hideKeyboard`, `setSoftInputMode` (Android), safe-area insets, and preferences in `SharedPreferences`
|
|
33
|
+
* / `NSUserDefaults` (typed int/string, `null` string removes). Throws outside a host that linked `@g9tm/lynxcontrols-native`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createNativeHostServices(options?: G9NativeHostOptions): G9NativeServices;
|
|
36
|
+
//# sourceMappingURL=services.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,iBAAiB,EAEtB,KAAK,YAAY,EAClB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAwC,MAAM,YAAY,CAAC;AAY3F,sFAAsF;AACtF,wBAAgB,gBAAgB,IAAI,kBAAkB,GAAG,IAAI,CAM5D;AAiBD,+EAA+E;AAC/E,MAAM,MAAM,kBAAkB,GAAG,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;AAE1F,0HAA0H;AAC1H,wBAAgB,gBAAgB,IAAI,kBAAkB,CAQrD;AAED,mDAAmD;AACnD,MAAM,WAAW,mBAAmB;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IACrC,yEAAyE;IACzE,QAAQ,CAAC,YAAY,CAAC,EAAE,kBAAkB,CAAC;CAC5C;AAED,mHAAmH;AACnH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAC;IACxC,gGAAgG;IAChG,UAAU,IAAI,IAAI,CAAC;IACnB;;;;;OAKG;IACH,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC;AAID;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,mBAAwB,GAAG,gBAAgB,CA4D5F"}
|
package/dist/services.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { G9Error } from "@g9tm/lynxcontrols-foundation";
|
|
2
|
+
function nativeHostModule() {
|
|
3
|
+
try {
|
|
4
|
+
var _ref;
|
|
5
|
+
return null != (_ref = "u" < typeof NativeModules ? void 0 : null == NativeModules ? void 0 : NativeModules.G9HostModule) ? _ref : null;
|
|
6
|
+
} catch {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function call(invoke, missingCode = 'preference-store') {
|
|
11
|
+
return new Promise((resolve, reject)=>{
|
|
12
|
+
invoke((envelope)=>{
|
|
13
|
+
if (null == envelope ? void 0 : envelope.ok) resolve(envelope.value);
|
|
14
|
+
else {
|
|
15
|
+
var _ref;
|
|
16
|
+
const e = null != (_ref = null == envelope ? void 0 : envelope.error) ? _ref : {
|
|
17
|
+
code: missingCode,
|
|
18
|
+
message: 'The native module sent no reply.'
|
|
19
|
+
};
|
|
20
|
+
reject(new G9Error(e.code, e.message));
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function lynxInsetsEvents() {
|
|
26
|
+
return (listener)=>{
|
|
27
|
+
var _lynx_getJSModule;
|
|
28
|
+
const target = "u" < typeof lynx ? void 0 : null == lynx ? void 0 : null == (_lynx_getJSModule = lynx.getJSModule) ? void 0 : _lynx_getJSModule.call(lynx, 'GlobalEventEmitter');
|
|
29
|
+
if (!target) return ()=>void 0;
|
|
30
|
+
const handler = (insets)=>listener(insets);
|
|
31
|
+
target.addListener('g9:insets', handler);
|
|
32
|
+
return ()=>target.removeListener('g9:insets', handler);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const NO_INSETS = Object.freeze({
|
|
36
|
+
top: 0,
|
|
37
|
+
end: 0,
|
|
38
|
+
bottom: 0,
|
|
39
|
+
start: 0
|
|
40
|
+
});
|
|
41
|
+
function createNativeHostServices(options = {}) {
|
|
42
|
+
var _options_module, _options_insetsEvents;
|
|
43
|
+
const native = null != (_options_module = options.module) ? _options_module : nativeHostModule();
|
|
44
|
+
if (!native) throw new Error('NativeModules.G9HostModule is missing: add @g9tm/lynxcontrols-native to the app so Autolink links it.');
|
|
45
|
+
const subscribe = null != (_options_insetsEvents = options.insetsEvents) ? _options_insetsEvents : lynxInsetsEvents();
|
|
46
|
+
let insets = NO_INSETS;
|
|
47
|
+
const listeners = new Set();
|
|
48
|
+
const publish = (next)=>{
|
|
49
|
+
insets = Object.freeze({
|
|
50
|
+
top: next.top,
|
|
51
|
+
end: next.end,
|
|
52
|
+
bottom: next.bottom,
|
|
53
|
+
start: next.start
|
|
54
|
+
});
|
|
55
|
+
for (const listener of Array.from(listeners))listener(insets);
|
|
56
|
+
};
|
|
57
|
+
call((reply)=>native.safeInsets(reply)).then(publish, ()=>void 0);
|
|
58
|
+
subscribe(publish);
|
|
59
|
+
const host = {
|
|
60
|
+
exit: ()=>native.moveToBack(),
|
|
61
|
+
hideKeyboard: ()=>native.hideKeyboard(),
|
|
62
|
+
getSafeInsets: ()=>insets,
|
|
63
|
+
subscribeSafeInsets (listener) {
|
|
64
|
+
listeners.add(listener);
|
|
65
|
+
return ()=>{
|
|
66
|
+
listeners.delete(listener);
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
setSoftInputMode: (mode)=>native.setSoftInputMode(mode)
|
|
70
|
+
};
|
|
71
|
+
const preferences = {
|
|
72
|
+
async getInt (key, defaultValue) {
|
|
73
|
+
const stored = await call((reply)=>native.getPreference(key, reply));
|
|
74
|
+
return (null == stored ? void 0 : stored.type) === 'int' ? stored.value : defaultValue;
|
|
75
|
+
},
|
|
76
|
+
async setInt (key, value) {
|
|
77
|
+
await call((reply)=>native.setPreference(key, 'int', String(value), reply));
|
|
78
|
+
},
|
|
79
|
+
async getString (key, defaultValue) {
|
|
80
|
+
const stored = await call((reply)=>native.getPreference(key, reply));
|
|
81
|
+
return (null == stored ? void 0 : stored.type) === 'string' ? stored.value : defaultValue;
|
|
82
|
+
},
|
|
83
|
+
async setString (key, value) {
|
|
84
|
+
await call((reply)=>native.setPreference(key, 'string', value, reply));
|
|
85
|
+
},
|
|
86
|
+
async remove (key) {
|
|
87
|
+
await call((reply)=>native.setPreference(key, 'remove', null, reply));
|
|
88
|
+
},
|
|
89
|
+
async entries (prefix) {
|
|
90
|
+
const list = await call((reply)=>native.preferenceEntries(prefix, reply));
|
|
91
|
+
return list.map(([key, value])=>[
|
|
92
|
+
key,
|
|
93
|
+
value
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const appDataDirectory = async ()=>{
|
|
98
|
+
const dir = await call((reply)=>native.appDataDirectory(reply), 'host-data-directory');
|
|
99
|
+
if ('string' != typeof dir || '' === dir.trim()) throw new G9Error('host-data-directory', 'The host reported no app data directory.');
|
|
100
|
+
return dir;
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
host,
|
|
104
|
+
preferences,
|
|
105
|
+
hapticTick: ()=>native.hapticTick(),
|
|
106
|
+
appDataDirectory
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export { createNativeHostServices, lynxInsetsEvents, nativeHostModule };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=services.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"services.js","sources":["../src/services.ts"],"sourcesContent":["// The mobile host services over `NativeModules.G9HostModule` (plan/specs/06 §7): G9HostServices and an asynchronous\n// G9PreferenceStore. Safe-area insets are read once and then follow the host's `g9:insets` global event.\nimport {\n G9Error,\n type G9HostServices,\n type G9PreferenceStore,\n type G9PreferenceValue,\n type G9SafeInsets,\n} from '@g9tm/lynxcontrols-foundation';\nimport type { G9HostNativeModule, G9NativeEnvelope, G9NativePreference } from './protocol';\n\ndeclare const NativeModules: { G9HostModule?: G9HostNativeModule } | undefined;\ndeclare const lynx:\n | {\n getJSModule?(name: string): {\n addListener(name: string, listener: (...args: never[]) => void): void;\n removeListener(name: string, listener: (...args: never[]) => void): void;\n };\n }\n | undefined;\n\n/** `NativeModules.G9HostModule`, or `null` where the native library is not linked. */\nexport function nativeHostModule(): G9HostNativeModule | null {\n try {\n return (typeof NativeModules === 'undefined' ? undefined : NativeModules?.G9HostModule) ?? null;\n } catch {\n return null;\n }\n}\n\nfunction call<T>(\n invoke: (reply: (envelope: G9NativeEnvelope<T>) => void) => void,\n missingCode = 'preference-store',\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n invoke((envelope) => {\n if (envelope?.ok) resolve(envelope.value);\n else {\n const e = envelope?.error ?? { code: missingCode, message: 'The native module sent no reply.' };\n reject(new G9Error(e.code, e.message));\n }\n });\n });\n}\n\n/** Subscribes to the host's insets event; returns the unsubscribe function. */\nexport type G9InsetsSubscriber = (listener: (insets: G9SafeInsets) => void) => () => void;\n\n/** The default insets source: the `g9:insets` global event a host sends on rotation / resize (Lynx background thread). */\nexport function lynxInsetsEvents(): G9InsetsSubscriber {\n return (listener) => {\n const target = typeof lynx === 'undefined' ? undefined : lynx?.getJSModule?.('GlobalEventEmitter');\n if (!target) return () => undefined;\n const handler = ((insets: G9SafeInsets) => listener(insets)) as unknown as (...args: never[]) => void;\n target.addListener('g9:insets', handler);\n return () => target.removeListener('g9:insets', handler);\n };\n}\n\n/** Options of {@link createNativeHostServices}. */\nexport interface G9NativeHostOptions {\n /** The module (default: `NativeModules.G9HostModule`). Tests pass a fake. */\n readonly module?: G9HostNativeModule;\n /** Where inset changes come from (default: {@link lynxInsetsEvents}). */\n readonly insetsEvents?: G9InsetsSubscriber;\n}\n\n/** What {@link createNativeHostServices} returns: the services for `G9Provider`, plus the optional haptic tick. */\nexport interface G9NativeServices {\n readonly host: G9HostServices;\n readonly preferences: G9PreferenceStore;\n /** A light haptic tick (Android `HapticFeedbackConstants`, iOS `UIImpactFeedbackGenerator`). */\n hapticTick(): void;\n /**\n * The app's private data directory, absolute (MAUI `FileSystem.AppDataDirectory`: Android `Context.filesDir`, iOS\n * the user `Library` directory), so the database keeps the path its MAUI version used. Pass it to a database\n * locator (`singleFileDatabaseLocator(dir + '/g9.db')` or `perUserDatabaseLocator({ root: dir, … })`).\n * Rejects with `host-data-directory` when the host reports none.\n */\n appDataDirectory(): Promise<string>;\n}\n\nconst NO_INSETS: G9SafeInsets = Object.freeze({ top: 0, end: 0, bottom: 0, start: 0 });\n\n/**\n * Android/iOS host services: `exit` moves the task to the back (Android; iOS has no programmatic exit, the module\n * ignores it), `hideKeyboard`, `setSoftInputMode` (Android), safe-area insets, and preferences in `SharedPreferences`\n * / `NSUserDefaults` (typed int/string, `null` string removes). Throws outside a host that linked `@g9tm/lynxcontrols-native`.\n */\nexport function createNativeHostServices(options: G9NativeHostOptions = {}): G9NativeServices {\n const native = options.module ?? nativeHostModule();\n if (!native) {\n throw new Error(\n 'NativeModules.G9HostModule is missing: add @g9tm/lynxcontrols-native to the app so Autolink links it.',\n );\n }\n const subscribe = options.insetsEvents ?? lynxInsetsEvents();\n let insets: G9SafeInsets = NO_INSETS;\n const listeners = new Set<(insets: G9SafeInsets) => void>();\n const publish = (next: G9SafeInsets) => {\n insets = Object.freeze({ top: next.top, end: next.end, bottom: next.bottom, start: next.start });\n for (const listener of Array.from(listeners)) listener(insets);\n };\n call<G9SafeInsets>((reply) => native.safeInsets(reply)).then(publish, () => undefined);\n subscribe(publish);\n\n const host: G9HostServices = {\n exit: () => native.moveToBack(),\n hideKeyboard: () => native.hideKeyboard(),\n getSafeInsets: () => insets,\n subscribeSafeInsets(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n setSoftInputMode: (mode) => native.setSoftInputMode(mode),\n };\n const preferences: G9PreferenceStore = {\n async getInt(key, defaultValue) {\n const stored = await call<G9NativePreference | null>((reply) => native.getPreference(key, reply));\n return stored?.type === 'int' ? stored.value : defaultValue;\n },\n async setInt(key, value) {\n await call<null>((reply) => native.setPreference(key, 'int', String(value), reply));\n },\n async getString(key, defaultValue) {\n const stored = await call<G9NativePreference | null>((reply) => native.getPreference(key, reply));\n return stored?.type === 'string' ? stored.value : defaultValue;\n },\n async setString(key, value) {\n await call<null>((reply) => native.setPreference(key, 'string', value, reply));\n },\n async remove(key) {\n await call<null>((reply) => native.setPreference(key, 'remove', null, reply));\n },\n async entries(prefix): Promise<ReadonlyArray<readonly [string, G9PreferenceValue]>> {\n const list = await call<Array<[string, number | string]>>((reply) => native.preferenceEntries(prefix, reply));\n return list.map(([key, value]) => [key, value] as const);\n },\n };\n const appDataDirectory = async (): Promise<string> => {\n const dir = await call<string>((reply) => native.appDataDirectory(reply), 'host-data-directory');\n if (typeof dir !== 'string' || dir.trim() === '') {\n throw new G9Error('host-data-directory', 'The host reported no app data directory.');\n }\n return dir;\n };\n return { host, preferences, hapticTick: () => native.hapticTick(), appDataDirectory };\n}\n"],"names":["nativeHostModule","_ref","NativeModules","undefined","call","invoke","missingCode","Promise","resolve","reject","envelope","e","G9Error","lynxInsetsEvents","listener","lynx","target","handler","insets","NO_INSETS","Object","createNativeHostServices","options","_options_module","_options_insetsEvents","native","Error","subscribe","listeners","Set","publish","next","Array","reply","host","mode","preferences","key","defaultValue","stored","value","String","prefix","list","appDataDirectory","dir"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { G9SqlResult } from '@g9tm/lynxcontrols-sqlite';
|
|
2
|
+
import type { G9NativeRunValue } from './protocol.js';
|
|
3
|
+
/**
|
|
4
|
+
* Binary data from the bridge as this realm's ArrayBuffer: Lynx hands `byte[]` / `NSData` over as an ArrayBuffer, which
|
|
5
|
+
* may belong to another realm, or (some hosts) as a typed array.
|
|
6
|
+
*/
|
|
7
|
+
export declare function toArrayBuffer(value: unknown): ArrayBuffer | null;
|
|
8
|
+
/** The results of one `run` reply. */
|
|
9
|
+
export declare function decodeRunValue(value: G9NativeRunValue): G9SqlResult[];
|
|
10
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAoB,gBAAgB,EAAgB,MAAM,YAAY,CAAC;AAInF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAchE;AAuDD,sCAAsC;AACtC,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,WAAW,EAAE,CAIrE"}
|