ugly-app 0.1.620 → 0.1.621

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 (66) hide show
  1. package/dist/cli/version.d.ts +1 -1
  2. package/dist/cli/version.js +1 -1
  3. package/dist/native/contract-meta.d.ts +7 -0
  4. package/dist/native/contract-meta.d.ts.map +1 -0
  5. package/dist/native/contract-meta.js +105 -0
  6. package/dist/native/contract-meta.js.map +1 -0
  7. package/dist/native/contract.d.ts +254 -24
  8. package/dist/native/contract.d.ts.map +1 -1
  9. package/dist/native/contract.js +4 -15
  10. package/dist/native/contract.js.map +1 -1
  11. package/dist/native/facades/net.d.ts +26 -0
  12. package/dist/native/facades/net.d.ts.map +1 -0
  13. package/dist/native/facades/net.js +22 -0
  14. package/dist/native/facades/net.js.map +1 -0
  15. package/dist/native/facades/process.d.ts +20 -0
  16. package/dist/native/facades/process.d.ts.map +1 -0
  17. package/dist/native/facades/process.js +15 -0
  18. package/dist/native/facades/process.js.map +1 -0
  19. package/dist/native/facades/studio.d.ts +47 -0
  20. package/dist/native/facades/studio.d.ts.map +1 -0
  21. package/dist/native/facades/studio.js +38 -0
  22. package/dist/native/facades/studio.js.map +1 -0
  23. package/dist/native/index.d.ts +10 -3
  24. package/dist/native/index.d.ts.map +1 -1
  25. package/dist/native/index.js +4 -2
  26. package/dist/native/index.js.map +1 -1
  27. package/dist/native/install.d.ts.map +1 -1
  28. package/dist/native/install.js +10 -8
  29. package/dist/native/install.js.map +1 -1
  30. package/dist/native/permissions.d.ts +12 -0
  31. package/dist/native/permissions.d.ts.map +1 -0
  32. package/dist/native/permissions.js +47 -0
  33. package/dist/native/permissions.js.map +1 -0
  34. package/dist/native/protocol.d.ts +5 -1
  35. package/dist/native/protocol.d.ts.map +1 -1
  36. package/dist/native/protocol.js +8 -0
  37. package/dist/native/protocol.js.map +1 -1
  38. package/dist/native/web-adapter.d.ts +5 -0
  39. package/dist/native/web-adapter.d.ts.map +1 -0
  40. package/dist/native/web-adapter.js +111 -0
  41. package/dist/native/web-adapter.js.map +1 -0
  42. package/dist/native/wrapper.d.ts +80 -6
  43. package/dist/native/wrapper.d.ts.map +1 -1
  44. package/dist/native/wrapper.js +149 -12
  45. package/dist/native/wrapper.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/cli/version.ts +1 -1
  48. package/src/native/contract-meta.test.ts +23 -0
  49. package/src/native/contract-meta.ts +114 -0
  50. package/src/native/contract.test.ts +13 -7
  51. package/src/native/contract.ts +130 -32
  52. package/src/native/facades/facades.test.ts +35 -0
  53. package/src/native/facades/net.ts +50 -0
  54. package/src/native/facades/process.ts +37 -0
  55. package/src/native/facades/studio.ts +73 -0
  56. package/src/native/index.ts +24 -3
  57. package/src/native/install.test.ts +8 -0
  58. package/src/native/install.ts +12 -8
  59. package/src/native/permissions.test.ts +42 -0
  60. package/src/native/permissions.ts +53 -0
  61. package/src/native/protocol.test.ts +15 -0
  62. package/src/native/protocol.ts +10 -1
  63. package/src/native/web-adapter.test.ts +27 -0
  64. package/src/native/web-adapter.ts +118 -0
  65. package/src/native/wrapper.test.ts +22 -0
  66. package/src/native/wrapper.ts +163 -13
@@ -1,24 +1,161 @@
1
+ import { NativeUnavailableError } from './protocol.js';
1
2
  import { installUglyNative } from './install.js';
3
+ import { createPermissions } from './permissions.js';
4
+ import { createProcessFacade } from './facades/process.js';
5
+ import { createNetFacade } from './facades/net.js';
6
+ import { createStudioFacade } from './facades/studio.js';
2
7
  const n = () => installUglyNative();
8
+ const inv = (c, p) => n().invoke(c, p);
9
+ export const permissions = createPermissions((c, p) => n().invoke(c, p), n().platform);
10
+ const desktopBridge = () => globalThis.UglyDesktop;
3
11
  export const native = {
12
+ permissions,
13
+ capture: {
14
+ screenshot: () => inv('capture.screenshot', undefined),
15
+ },
4
16
  gps: {
5
- start: (p) => n().invoke('gps.start', p),
6
- stop: () => n().invoke('gps.stop', undefined),
17
+ start: (p) => inv('gps.start', p),
18
+ stop: () => inv('gps.stop', undefined),
7
19
  onPosition: (cb) => n().subscribe('gps.position', cb),
8
20
  },
21
+ liveActivity: {
22
+ startWorkout: (p) => inv('liveActivity.startWorkout', p),
23
+ updateWorkout: (p) => inv('liveActivity.updateWorkout', p),
24
+ endWorkout: () => inv('liveActivity.endWorkout', undefined),
25
+ startCardio: (p) => inv('liveActivity.startCardio', p),
26
+ updateCardio: (p) => inv('liveActivity.updateCardio', p),
27
+ endCardio: () => inv('liveActivity.endCardio', undefined),
28
+ },
9
29
  alarm: {
10
- schedule: (p) => n().invoke('alarm.schedule', p),
11
- cancel: (p) => n().invoke('alarm.cancel', p),
12
- playAudio: (p) => n().invoke('alarm.playAudio', p),
30
+ schedule: (p) => inv('alarm.schedule', p),
31
+ cancel: (p) => inv('alarm.cancel', p),
32
+ playAudio: (p) => inv('alarm.playAudio', p),
13
33
  onFired: (cb) => n().subscribe('alarm.fired', cb),
14
34
  },
15
- liveActivity: {
16
- startWorkout: (p) => n().invoke('liveActivity.startWorkout', p),
17
- updateWorkout: (p) => n().invoke('liveActivity.updateWorkout', p),
18
- endWorkout: () => n().invoke('liveActivity.endWorkout', undefined),
19
- startCardio: (p) => n().invoke('liveActivity.startCardio', p),
20
- updateCardio: (p) => n().invoke('liveActivity.updateCardio', p),
21
- endCardio: () => n().invoke('liveActivity.endCardio', undefined),
35
+ tts: {
36
+ start: (p) => inv('tts.start', p),
37
+ pause: () => inv('tts.pause', undefined),
38
+ resume: () => inv('tts.resume', undefined),
39
+ stop: () => inv('tts.stop', undefined),
40
+ onState: (cb) => n().subscribe('tts.state', cb),
41
+ },
42
+ push: {
43
+ register: () => inv('push.register', undefined),
44
+ onToken: (cb) => n().subscribe('push.token', cb),
45
+ onNotificationClick: (cb) => n().subscribe('push.notificationClick', cb),
46
+ },
47
+ clipboard: {
48
+ write: (p) => inv('clipboard.write', p),
49
+ read: () => inv('clipboard.read', undefined),
50
+ },
51
+ system: {
52
+ openExternal: (p) => inv('system.openExternal', p),
53
+ share: (p) => inv('system.share', p),
54
+ },
55
+ badge: {
56
+ set: (p) => inv('badge.set', p),
57
+ },
58
+ ui: {
59
+ getSafeArea: () => inv('ui.getSafeArea', undefined),
60
+ onKeyboardHeight: (cb) => n().subscribe('keyboard.height', cb),
61
+ onSafeAreaInsets: (cb) => n().subscribe('safeArea.insets', cb),
62
+ },
63
+ fs: {
64
+ readFile: async (path) => {
65
+ await permissions.ensure('fs');
66
+ return (await inv('fs.readFile', { path })).content;
67
+ },
68
+ writeFile: async (path, content) => {
69
+ await permissions.ensure('fs');
70
+ return inv('fs.writeFile', { path, content });
71
+ },
72
+ mkdir: async (path, recursive = false) => {
73
+ await permissions.ensure('fs');
74
+ return inv('fs.mkdir', { path, recursive });
75
+ },
76
+ rm: async (path, opts = {}) => {
77
+ await permissions.ensure('fs');
78
+ return inv('fs.rm', { path, ...opts });
79
+ },
80
+ rename: async (from, to) => {
81
+ await permissions.ensure('fs');
82
+ return inv('fs.rename', { from, to });
83
+ },
84
+ readdir: async (path) => {
85
+ await permissions.ensure('fs');
86
+ return (await inv('fs.readdir', { path })).entries;
87
+ },
88
+ stat: async (path) => {
89
+ await permissions.ensure('fs');
90
+ return inv('fs.stat', { path });
91
+ },
92
+ exists: async (path) => {
93
+ await permissions.ensure('fs');
94
+ return (await inv('fs.exists', { path })).exists;
95
+ },
96
+ realpath: async (path) => {
97
+ await permissions.ensure('fs');
98
+ return (await inv('fs.realpath', { path })).path;
99
+ },
100
+ readFileBytes: async (path) => {
101
+ await permissions.ensure('fs');
102
+ const fb = desktopBridge()?.fsBytes;
103
+ if (!fb)
104
+ throw new NativeUnavailableError('fs.readFileBytes');
105
+ return fb.readFileBytes(path);
106
+ },
107
+ writeFileBytes: async (path, bytes) => {
108
+ await permissions.ensure('fs');
109
+ const fb = desktopBridge()?.fsBytes;
110
+ if (!fb)
111
+ throw new NativeUnavailableError('fs.writeFileBytes');
112
+ return fb.writeFileBytes(path, bytes);
113
+ },
114
+ },
115
+ process: createProcessFacade(() => desktopBridge()?.process, n().platform, () => permissions.ensure('process')),
116
+ net: createNetFacade(() => desktopBridge()?.net, n().platform, () => permissions.ensure('net')),
117
+ studio: createStudioFacade(() => desktopBridge()?.studio, n().platform),
118
+ secrets: {
119
+ get: async (key) => {
120
+ await permissions.ensure('secrets');
121
+ return (await inv('secrets.get', { key })).value;
122
+ },
123
+ set: async (key, value) => {
124
+ await permissions.ensure('secrets');
125
+ return inv('secrets.set', { key, value });
126
+ },
127
+ delete: async (key) => {
128
+ await permissions.ensure('secrets');
129
+ return inv('secrets.delete', { key });
130
+ },
131
+ list: async () => {
132
+ await permissions.ensure('secrets');
133
+ return (await inv('secrets.list', undefined)).keys;
134
+ },
135
+ },
136
+ serve: {
137
+ start: async (dir) => {
138
+ await permissions.ensure('serve');
139
+ return inv('serve.start', { dir });
140
+ },
141
+ stop: async (port) => {
142
+ await permissions.ensure('serve');
143
+ return inv('serve.stop', { port });
144
+ },
145
+ },
146
+ workers: {
147
+ register: async (path, boot = false) => {
148
+ await permissions.ensure('workers');
149
+ return (await inv('workers.register', { path, boot })).id;
150
+ },
151
+ unregister: async (id) => {
152
+ await permissions.ensure('workers');
153
+ return inv('workers.unregister', { id });
154
+ },
155
+ list: async () => {
156
+ await permissions.ensure('workers');
157
+ return (await inv('workers.list', undefined)).ids;
158
+ },
22
159
  },
23
160
  };
24
161
  export function nativePlatform() {
@@ -1 +1 @@
1
- {"version":3,"file":"wrapper.js","sourceRoot":"","sources":["../../src/native/wrapper.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,CAAC,GAAG,GAAG,EAAE,CAAC,iBAAiB,EAAE,CAAC;AAEpC,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,GAAG,EAAE;QACH,KAAK,EAAE,CAAC,CAAyB,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QAChE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC;QAC7C,UAAU,EAAE,CAAC,EAAuC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,cAAc,EAAE,EAAE,CAAC;KAC3F;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,CAAC,CAA8B,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;QAC7E,MAAM,EAAE,CAAC,CAA4B,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;QACvE,SAAS,EAAE,CAAC,CAA+B,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAC;QAChF,OAAO,EAAE,CAAC,EAAsC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,CAAC;KACtF;IACD,YAAY,EAAE;QACZ,YAAY,EAAE,CAAC,CAAyC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,2BAA2B,EAAE,CAAC,CAAC;QACvG,aAAa,EAAE,CAAC,CAA0C,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,4BAA4B,EAAE,CAAC,CAAC;QAC1G,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,yBAAyB,EAAE,SAAS,CAAC;QAClE,WAAW,EAAE,CAAC,CAAwC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,0BAA0B,EAAE,CAAC,CAAC;QACpG,YAAY,EAAE,CAAC,CAAyC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,2BAA2B,EAAE,CAAC,CAAC;QACvG,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,wBAAwB,EAAE,SAAS,CAAC;KACjE;CACF,CAAC;AAEF,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC;AACtB,CAAC;AACD,MAAM,UAAU,iBAAiB;IAC/B,OAAO,CAAC,EAAE,CAAC,QAAQ,KAAK,KAAK,CAAC;AAChC,CAAC"}
1
+ {"version":3,"file":"wrapper.js","sourceRoot":"","sources":["../../src/native/wrapper.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugly-app",
3
- "version": "0.1.620",
3
+ "version": "0.1.621",
4
4
  "type": "module",
5
5
  "comment:files": "Allowlist what ships to npm. dist = runtime; src = sourcemap targets (dist/*.js.map reference ../../src/); templates = CLI scaffold. Everything else at repo root (.pgdata local Postgres, coverage, assets/icons sources, test/, test-results/) is excluded by omission. The !negations strip the scaffold's installed deps + cruft (templates/node_modules is 200MB+ and must never ship). package.json/README/LICENSE ship automatically.",
6
6
  "files": [
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by prebuild — do not edit manually
2
- export const CLI_VERSION = "0.1.620";
2
+ export const CLI_VERSION = "0.1.621";
@@ -0,0 +1,23 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { CHANNEL_FALLBACK, NAMESPACES, fallbackFor } from './contract-meta';
3
+
4
+ describe('contract-meta', () => {
5
+ it('has a fallback entry for every channel and platform', () => {
6
+ const channels = Object.keys(CHANNEL_FALLBACK) as (keyof typeof CHANNEL_FALLBACK)[];
7
+ expect(channels.length).toBeGreaterThan(30);
8
+ for (const ch of channels) {
9
+ for (const p of ['ios', 'android', 'desktop', 'web'] as const) {
10
+ expect(['native', 'web', 'noop', 'throw']).toContain(CHANNEL_FALLBACK[ch][p]);
11
+ }
12
+ }
13
+ });
14
+ it('fs is throw on web, native on desktop; gps is web on web', () => {
15
+ expect(fallbackFor('fs.readFile', 'web')).toBe('throw');
16
+ expect(fallbackFor('fs.readFile', 'desktop')).toBe('native');
17
+ expect(fallbackFor('gps.start', 'web')).toBe('web');
18
+ });
19
+ it('NAMESPACES includes facade namespaces', () => {
20
+ expect(NAMESPACES).toContain('process');
21
+ expect(NAMESPACES).toContain('studio');
22
+ });
23
+ });
@@ -0,0 +1,114 @@
1
+ import type { Channel, Namespace } from './contract.js';
2
+ import { namespaceOf } from './contract.js';
3
+
4
+ export type Platform = 'ios' | 'android' | 'desktop' | 'web';
5
+ export type Fallback = 'native' | 'web' | 'noop' | 'throw';
6
+
7
+ export const NAMESPACES: Namespace[] = [
8
+ 'capture',
9
+ 'gps',
10
+ 'liveActivity',
11
+ 'alarm',
12
+ 'tts',
13
+ 'push',
14
+ 'clipboard',
15
+ 'system',
16
+ 'badge',
17
+ 'ui',
18
+ 'fs',
19
+ 'secrets',
20
+ 'serve',
21
+ 'workers',
22
+ 'net',
23
+ 'process',
24
+ 'permissions',
25
+ 'studio',
26
+ ];
27
+
28
+ // Per-namespace fallback profile when no explicit channel override applies.
29
+ // Tuple order: [ios, android, desktop, web].
30
+ const NS_DEFAULT: Record<Namespace, [Fallback, Fallback, Fallback, Fallback]> = {
31
+ capture: ['native', 'native', 'native', 'web'],
32
+ gps: ['native', 'native', 'native', 'web'],
33
+ liveActivity: ['native', 'native', 'native', 'web'],
34
+ alarm: ['native', 'native', 'native', 'web'],
35
+ tts: ['native', 'native', 'native', 'web'],
36
+ push: ['native', 'native', 'native', 'web'],
37
+ clipboard: ['native', 'native', 'native', 'web'],
38
+ system: ['native', 'native', 'native', 'web'],
39
+ badge: ['native', 'native', 'native', 'web'],
40
+ ui: ['native', 'native', 'noop', 'web'],
41
+ fs: ['native', 'native', 'native', 'throw'],
42
+ secrets: ['native', 'native', 'native', 'throw'],
43
+ serve: ['native', 'native', 'native', 'throw'],
44
+ workers: ['native', 'native', 'native', 'throw'],
45
+ net: ['native', 'native', 'native', 'throw'],
46
+ process: ['throw', 'throw', 'native', 'throw'],
47
+ permissions: ['native', 'native', 'native', 'native'],
48
+ studio: ['native', 'native', 'native', 'throw'],
49
+ };
50
+
51
+ const PLATFORM_INDEX: Record<Platform, 0 | 1 | 2 | 3> = { ios: 0, android: 1, desktop: 2, web: 3 };
52
+
53
+ // All request/response channels (mirrors contract.ts channels; the test guards count).
54
+ const CHANNELS: Channel[] = [
55
+ 'capture.screenshot',
56
+ 'gps.start',
57
+ 'gps.stop',
58
+ 'liveActivity.startWorkout',
59
+ 'liveActivity.updateWorkout',
60
+ 'liveActivity.endWorkout',
61
+ 'liveActivity.startCardio',
62
+ 'liveActivity.updateCardio',
63
+ 'liveActivity.endCardio',
64
+ 'alarm.schedule',
65
+ 'alarm.cancel',
66
+ 'alarm.playAudio',
67
+ 'tts.start',
68
+ 'tts.pause',
69
+ 'tts.resume',
70
+ 'tts.stop',
71
+ 'push.register',
72
+ 'clipboard.write',
73
+ 'clipboard.read',
74
+ 'system.openExternal',
75
+ 'system.share',
76
+ 'badge.set',
77
+ 'ui.getSafeArea',
78
+ 'fs.readFile',
79
+ 'fs.writeFile',
80
+ 'fs.mkdir',
81
+ 'fs.rm',
82
+ 'fs.rename',
83
+ 'fs.readdir',
84
+ 'fs.stat',
85
+ 'fs.exists',
86
+ 'fs.realpath',
87
+ 'secrets.get',
88
+ 'secrets.set',
89
+ 'secrets.delete',
90
+ 'secrets.list',
91
+ 'serve.start',
92
+ 'serve.stop',
93
+ 'workers.register',
94
+ 'workers.unregister',
95
+ 'workers.list',
96
+ ];
97
+
98
+ function buildChannelFallback(): Record<Channel, Record<Platform, Fallback>> {
99
+ const out = {} as Record<Channel, Record<Platform, Fallback>>;
100
+ for (const ch of CHANNELS) {
101
+ const prof = NS_DEFAULT[namespaceOf(ch)];
102
+ out[ch] = { ios: prof[0], android: prof[1], desktop: prof[2], web: prof[3] };
103
+ }
104
+ return out;
105
+ }
106
+
107
+ export const CHANNEL_FALLBACK: Record<Channel, Record<Platform, Fallback>> = buildChannelFallback();
108
+
109
+ export function fallbackFor(channel: Channel, platform: Platform): Fallback {
110
+ const row = CHANNEL_FALLBACK[channel];
111
+ if (row) return row[platform];
112
+ // Facade-namespace channel (e.g. studio.*, process.*, net.*) not in the table:
113
+ return NS_DEFAULT[namespaceOf(channel)][PLATFORM_INDEX[platform]];
114
+ }
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, expectTypeOf } from 'vitest';
2
- import type { PayloadOf, DataOf, Channel } from './contract';
3
- import { CHANNEL_WEB_FALLBACK } from './contract';
2
+ import type { PayloadOf, ResultOf, DataOf, Channel, Namespace } from './contract';
3
+ import { namespaceOf } from './contract';
4
4
 
5
5
  describe('native contract types', () => {
6
6
  it('types the alarm.schedule payload', () => {
@@ -9,12 +9,18 @@ describe('native contract types', () => {
9
9
  it('types the gps.position event data', () => {
10
10
  expectTypeOf<DataOf<'gps.position'>>().toMatchTypeOf<{ lat: number; lng: number }>();
11
11
  });
12
- it('includes liveActivity channels', () => {
12
+ it('contract types span all namespaces', () => {
13
+ expectTypeOf<PayloadOf<'fs.readFile'>>().toEqualTypeOf<{ path: string }>();
14
+ expectTypeOf<ResultOf<'fs.readFile'>>().toEqualTypeOf<{ content: string }>();
15
+ expectTypeOf<ResultOf<'capture.screenshot'>>().toEqualTypeOf<string>();
16
+ expectTypeOf<DataOf<'push.notificationClick'>>().toEqualTypeOf<{ action: string; data: Record<string, string> }>();
17
+ expectTypeOf<'fs'>().toMatchTypeOf<Namespace>();
18
+ expectTypeOf<'liveActivity'>().toMatchTypeOf<Namespace>();
13
19
  expectTypeOf<'liveActivity.startWorkout'>().toMatchTypeOf<Channel>();
14
20
  });
15
- it('runtime fallback table covers the fit channels', () => {
16
- expect(CHANNEL_WEB_FALLBACK['alarm.schedule']).toBe('noop');
17
- expect(CHANNEL_WEB_FALLBACK['liveActivity.startWorkout']).toBe('noop');
18
- expect(Object.keys(CHANNEL_WEB_FALLBACK).length).toBe(11);
21
+ it('namespaceOf extracts the top-level namespace', () => {
22
+ expect(namespaceOf('fs.readFile')).toBe('fs');
23
+ expect(namespaceOf('liveActivity.startWorkout')).toBe('liveActivity');
24
+ expect(namespaceOf('gps.start')).toBe('gps');
19
25
  });
20
26
  });
@@ -1,4 +1,6 @@
1
- // Live Activity state shapes (mirror ugly-fit FitLiveActivity.ts).
1
+ // Single source of truth for the UglyNative extension surface.
2
+
3
+ // ── Shared state shapes ──────────────────────────────────────────────────────
2
4
  export interface WorkoutLAState {
3
5
  workoutName?: string;
4
6
  exerciseName: string;
@@ -18,27 +20,114 @@ export interface CardioLAState {
18
20
  elapsedSeconds: number;
19
21
  isPaused: boolean;
20
22
  }
23
+ export interface HostDirent {
24
+ name: string;
25
+ isDirectory: boolean;
26
+ isFile: boolean;
27
+ }
28
+ export interface HostStat {
29
+ size: number;
30
+ isDirectory: boolean;
31
+ isFile: boolean;
32
+ mtimeMs: number;
33
+ }
34
+ export interface UpdateStatus {
35
+ state: 'idle' | 'checking' | 'up-to-date' | 'available' | 'downloading' | 'downloaded' | 'error';
36
+ version?: string;
37
+ progressPercent?: number;
38
+ error?: string;
39
+ }
40
+ export type GrantState = 'granted' | 'denied' | 'scoped' | 'full' | 'prompt';
41
+ export interface SpawnOpts {
42
+ cwd?: string;
43
+ env?: Record<string, string>;
44
+ replaceEnv?: boolean;
45
+ detached?: boolean;
46
+ treeKill?: boolean;
47
+ }
48
+ export interface SafeAreaInsets {
49
+ top: number;
50
+ right: number;
51
+ bottom: number;
52
+ left: number;
53
+ }
21
54
 
22
- // Per-channel: payload (web->native) + result (native->web reply).
23
- // `web` declares the no-shell fallback: 'noop' resolves to the result default,
24
- // 'throw' rejects with NativeUnavailableError.
55
+ // ── Request/response channels (via invoke) ───────────────────────────────────
25
56
  export interface NativeContract {
26
57
  channels: {
27
- 'gps.start': { payload: { activityType: 'run' | 'hike' | 'bike' }; result: void; web: 'noop' };
28
- 'gps.stop': { payload: void; result: void; web: 'noop' };
29
- 'alarm.schedule': { payload: { id: string; title?: string; body?: string; delaySeconds: number }; result: void; web: 'noop' };
30
- 'alarm.cancel': { payload: { id?: string }; result: void; web: 'noop' };
31
- 'alarm.playAudio': { payload: { id: string; base64: string; mimeType?: string }; result: void; web: 'noop' };
32
- 'liveActivity.startWorkout': { payload: WorkoutLAState; result: void; web: 'noop' };
33
- 'liveActivity.updateWorkout': { payload: Partial<WorkoutLAState>; result: void; web: 'noop' };
34
- 'liveActivity.endWorkout': { payload: void; result: void; web: 'noop' };
35
- 'liveActivity.startCardio': { payload: CardioLAState; result: void; web: 'noop' };
36
- 'liveActivity.updateCardio': { payload: Partial<CardioLAState>; result: void; web: 'noop' };
37
- 'liveActivity.endCardio': { payload: void; result: void; web: 'noop' };
58
+ // capture
59
+ 'capture.screenshot': { payload: void; result: string };
60
+ // gps
61
+ 'gps.start': { payload: { activityType: 'run' | 'hike' | 'bike' }; result: void };
62
+ 'gps.stop': { payload: void; result: void };
63
+ // liveActivity
64
+ 'liveActivity.startWorkout': { payload: WorkoutLAState; result: void };
65
+ 'liveActivity.updateWorkout': { payload: Partial<WorkoutLAState>; result: void };
66
+ 'liveActivity.endWorkout': { payload: void; result: void };
67
+ 'liveActivity.startCardio': { payload: CardioLAState; result: void };
68
+ 'liveActivity.updateCardio': { payload: Partial<CardioLAState>; result: void };
69
+ 'liveActivity.endCardio': { payload: void; result: void };
70
+ // alarm
71
+ 'alarm.schedule': { payload: { id: string; delaySeconds: number; title?: string; body?: string }; result: void };
72
+ 'alarm.cancel': { payload: { id?: string }; result: void };
73
+ 'alarm.playAudio': { payload: { id: string; base64: string; mimeType?: string }; result: void };
74
+ // tts
75
+ 'tts.start': { payload: { text: string; voice?: string }; result: void };
76
+ 'tts.pause': { payload: void; result: void };
77
+ 'tts.resume': { payload: void; result: void };
78
+ 'tts.stop': { payload: void; result: void };
79
+ // push
80
+ 'push.register': { payload: void; result: { token: string } };
81
+ // clipboard
82
+ 'clipboard.write': { payload: { text: string }; result: void };
83
+ 'clipboard.read': { payload: void; result: { text: string } };
84
+ // system
85
+ 'system.openExternal': { payload: { url: string }; result: void };
86
+ 'system.share': { payload: { title?: string; text?: string; url?: string }; result: void };
87
+ // badge
88
+ 'badge.set': { payload: { count: number }; result: void };
89
+ // ui
90
+ 'ui.getSafeArea': { payload: void; result: SafeAreaInsets };
91
+ // fs (text ops; byte ops are facade)
92
+ 'fs.readFile': { payload: { path: string }; result: { content: string } };
93
+ 'fs.writeFile': { payload: { path: string; content: string }; result: void };
94
+ 'fs.mkdir': { payload: { path: string; recursive?: boolean }; result: void };
95
+ 'fs.rm': { payload: { path: string; recursive?: boolean; force?: boolean }; result: void };
96
+ 'fs.rename': { payload: { from: string; to: string }; result: void };
97
+ 'fs.readdir': { payload: { path: string }; result: { entries: HostDirent[] } };
98
+ 'fs.stat': { payload: { path: string }; result: HostStat };
99
+ 'fs.exists': { payload: { path: string }; result: { exists: boolean } };
100
+ 'fs.realpath': { payload: { path: string }; result: { path: string } };
101
+ // secrets
102
+ 'secrets.get': { payload: { key: string }; result: { value: string | null } };
103
+ 'secrets.set': { payload: { key: string; value: string }; result: void };
104
+ 'secrets.delete': { payload: { key: string }; result: void };
105
+ 'secrets.list': { payload: void; result: { keys: string[] } };
106
+ // serve
107
+ 'serve.start': { payload: { dir?: string }; result: { port: number; url: string } };
108
+ 'serve.stop': { payload: { port: number }; result: void };
109
+ // workers
110
+ 'workers.register': { payload: { path: string; boot?: boolean }; result: { id: string } };
111
+ 'workers.unregister': { payload: { id: string }; result: void };
112
+ 'workers.list': { payload: void; result: { ids: string[] } };
38
113
  };
39
114
  events: {
40
- 'gps.position': { lat: number; lng: number; accuracy: number; timestamp: number; altitude?: number; speed?: number; course?: number };
115
+ 'gps.position': {
116
+ lat: number;
117
+ lng: number;
118
+ accuracy: number;
119
+ timestamp: number;
120
+ altitude?: number;
121
+ speed?: number;
122
+ course?: number;
123
+ };
41
124
  'alarm.fired': { id: string };
125
+ 'keyboard.height': { height: number };
126
+ 'safeArea.insets': SafeAreaInsets;
127
+ 'tts.state': { state: 'idle' | 'loading' | 'playing' | 'paused' | 'finished' };
128
+ 'push.token': { token: string };
129
+ 'push.notificationClick': { action: string; data: Record<string, string> };
130
+ 'studio.update.status': UpdateStatus;
42
131
  };
43
132
  }
44
133
 
@@ -46,21 +135,30 @@ export type Channel = keyof NativeContract['channels'];
46
135
  export type NativeEvent = keyof NativeContract['events'];
47
136
  export type PayloadOf<C extends Channel> = NativeContract['channels'][C]['payload'];
48
137
  export type ResultOf<C extends Channel> = NativeContract['channels'][C]['result'];
49
- export type WebFallbackOf<C extends Channel> = NativeContract['channels'][C]['web'];
50
138
  export type DataOf<E extends NativeEvent> = NativeContract['events'][E];
51
139
 
52
- // Runtime mirror of each channel's `web` field (TS types are erased, so the
53
- // fallback behaviour also needs a runtime table). Keep keys in sync with channels.
54
- export const CHANNEL_WEB_FALLBACK: Record<Channel, 'noop' | 'throw'> = {
55
- 'gps.start': 'noop',
56
- 'gps.stop': 'noop',
57
- 'alarm.schedule': 'noop',
58
- 'alarm.cancel': 'noop',
59
- 'alarm.playAudio': 'noop',
60
- 'liveActivity.startWorkout': 'noop',
61
- 'liveActivity.updateWorkout': 'noop',
62
- 'liveActivity.endWorkout': 'noop',
63
- 'liveActivity.startCardio': 'noop',
64
- 'liveActivity.updateCardio': 'noop',
65
- 'liveActivity.endCardio': 'noop',
66
- };
140
+ // Top-level namespaces (permission units). Includes streaming-facade namespaces
141
+ // (process, net, studio) that are NOT in `channels` but ARE permission/wrapper units.
142
+ export type Namespace =
143
+ | 'capture'
144
+ | 'gps'
145
+ | 'liveActivity'
146
+ | 'alarm'
147
+ | 'tts'
148
+ | 'push'
149
+ | 'clipboard'
150
+ | 'system'
151
+ | 'badge'
152
+ | 'ui'
153
+ | 'fs'
154
+ | 'secrets'
155
+ | 'serve'
156
+ | 'workers'
157
+ | 'net'
158
+ | 'process'
159
+ | 'permissions'
160
+ | 'studio';
161
+
162
+ export function namespaceOf(channel: Channel): Namespace {
163
+ return channel.split('.')[0] as Namespace;
164
+ }
@@ -0,0 +1,35 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createProcessFacade } from './process';
3
+ import { createNetFacade } from './net';
4
+ import { createStudioFacade } from './studio';
5
+ import { NativeUnavailableError } from '../protocol';
6
+
7
+ describe('process facade', () => {
8
+ it('throws NativeUnavailableError off-desktop', () => {
9
+ const proc = createProcessFacade(() => undefined, 'ios', async () => {});
10
+ expect(() => proc.spawn('ls', [])).toThrow(NativeUnavailableError);
11
+ });
12
+ it('delegates to the desktop bridge on desktop', () => {
13
+ const handle = {
14
+ onStdout() {}, onStderr() {}, onExit() {}, onError() {}, write() {}, closeStdin() {}, kill() {},
15
+ };
16
+ const bridge = { spawn: () => handle };
17
+ const proc = createProcessFacade(() => bridge, 'desktop', async () => {});
18
+ expect(proc.spawn('ls', ['-a'])).toBe(handle);
19
+ });
20
+ });
21
+
22
+ describe('net facade', () => {
23
+ it('throws on web', () => {
24
+ const net = createNetFacade(() => undefined, 'web', async () => {});
25
+ expect(() => net.connect('h', 1)).toThrow(NativeUnavailableError);
26
+ });
27
+ });
28
+
29
+ describe('studio facade', () => {
30
+ it('app.platform returns the platform; revealInFolder throws off-desktop', async () => {
31
+ const studio = createStudioFacade(() => undefined, 'web');
32
+ expect(studio.app.platform()).toBe('web');
33
+ await expect(studio.revealInFolder('/x')).rejects.toBeInstanceOf(NativeUnavailableError);
34
+ });
35
+ });