@omujs/omu 0.9.31 → 0.9.33

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 (64) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/dts/api/asset/extension.d.ts +3 -3
  3. package/dist/dts/api/asset/index.d.ts +1 -1
  4. package/dist/dts/api/dashboard/extension.d.ts +17 -4
  5. package/dist/dts/api/dashboard/handler.d.ts +2 -1
  6. package/dist/dts/api/dashboard/index.d.ts +1 -1
  7. package/dist/dts/api/http/extension.d.ts +2 -135
  8. package/dist/dts/api/http/http.d.ts +36 -0
  9. package/dist/dts/api/http/websocket.d.ts +123 -0
  10. package/dist/dts/api/permission/extension.d.ts +1 -0
  11. package/dist/dts/api/registry/registry.d.ts +13 -0
  12. package/dist/dts/api/server/extension.d.ts +9 -1
  13. package/dist/dts/index.d.ts +1 -3
  14. package/dist/dts/version.d.ts +1 -1
  15. package/dist/esm/api/asset/extension.js +43 -18
  16. package/dist/esm/api/asset/index.js +42 -17
  17. package/dist/esm/api/dashboard/extension.js +23 -13
  18. package/dist/esm/api/dashboard/index.js +23 -13
  19. package/dist/esm/api/endpoint/extension.js +3 -3
  20. package/dist/esm/api/extension.js +3 -3
  21. package/dist/esm/api/http/extension.js +262 -277
  22. package/dist/esm/api/http/http.js +442 -0
  23. package/dist/esm/api/http/index.js +262 -165
  24. package/dist/esm/api/http/websocket.js +220 -0
  25. package/dist/esm/api/i18n/extension.js +47 -3
  26. package/dist/esm/api/i18n/index.js +47 -3
  27. package/dist/esm/api/index.js +3 -3
  28. package/dist/esm/api/logger/extension.js +3 -3
  29. package/dist/esm/api/logger/index.js +3 -3
  30. package/dist/esm/api/permission/extension.js +6 -3
  31. package/dist/esm/api/plugin/extension.js +3 -3
  32. package/dist/esm/api/plugin/index.js +3 -3
  33. package/dist/esm/api/registry/extension.js +47 -3
  34. package/dist/esm/api/registry/index.js +47 -3
  35. package/dist/esm/api/registry/registry.js +1 -1
  36. package/dist/esm/api/server/extension.js +51 -5
  37. package/dist/esm/api/server/index.js +51 -5
  38. package/dist/esm/api/session/extension.js +47 -3
  39. package/dist/esm/api/session/index.js +47 -3
  40. package/dist/esm/api/signal/extension.js +3 -3
  41. package/dist/esm/api/table/extension.js +3 -3
  42. package/dist/esm/api/table/index.js +3 -3
  43. package/dist/esm/index.js +369 -189
  44. package/dist/esm/network/index.js +3 -3
  45. package/dist/esm/network/network.js +3 -3
  46. package/dist/esm/omu.js +368 -186
  47. package/dist/esm/version.js +3 -3
  48. package/package.json +1 -1
  49. package/src/api/asset/extension.ts +46 -12
  50. package/src/api/asset/index.ts +1 -2
  51. package/src/api/dashboard/extension.ts +40 -14
  52. package/src/api/dashboard/handler.ts +2 -1
  53. package/src/api/dashboard/index.ts +2 -0
  54. package/src/api/extension.ts +1 -1
  55. package/src/api/http/extension.ts +13 -364
  56. package/src/api/http/http.ts +79 -0
  57. package/src/api/http/websocket.ts +283 -0
  58. package/src/api/permission/extension.ts +4 -0
  59. package/src/api/registry/extension.ts +46 -1
  60. package/src/api/registry/registry.ts +23 -0
  61. package/src/api/server/extension.ts +10 -0
  62. package/src/index.ts +3 -5
  63. package/src/version.ts +1 -1
  64. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,283 @@
1
+ import { textDecoder } from '../../const';
2
+ import { DataChunk, RequestHandle } from './http';
3
+
4
+ export type WSData = string | ArrayBufferLike | Blob | ArrayBufferView;
5
+
6
+ export const WSMsgType = {
7
+ CONTINUATION: 0x0,
8
+ TEXT: 0x1,
9
+ BINARY: 0x2,
10
+ PING: 0x9,
11
+ PONG: 0xA,
12
+ CLOSE: 0x8,
13
+ } as const;
14
+
15
+ export const WSCloseCode = {
16
+ OK: 1000,
17
+ GOING_AWAY: 1001,
18
+ PROTOCOL_ERROR: 1002,
19
+ UNSUPPORTED_DATA: 1003,
20
+ ABNORMAL_CLOSURE: 1006,
21
+ INVALID_TEXT: 1007,
22
+ POLICY_VIOLATION: 1008,
23
+ MESSAGE_TOO_BIG: 1009,
24
+ MANDATORY_EXTENSION: 1010,
25
+ INTERNAL_ERROR: 1011,
26
+ SERVICE_RESTART: 1012,
27
+ TRY_AGAIN_LATER: 1013,
28
+ BAD_GATEWAY: 1014,
29
+ } as const;
30
+
31
+ export type WebSocketOpen = RequestHandle & {
32
+ url: string;
33
+ protocol?: string | null;
34
+ };
35
+ export type WebSocketClose = RequestHandle & {
36
+ code?: number;
37
+ reason?: string | null;
38
+ };
39
+ export type WebSocketError = RequestHandle & {
40
+ type: 'ConnectionRefused';
41
+ reason?: string | null;
42
+ };
43
+
44
+ export type EventListenerMap<T> = {
45
+ [K in keyof T]: ((event: T[K]) => unknown)[]
46
+ };
47
+
48
+ export type WSMsg = {
49
+ type: 'open';
50
+ data: WebSocketOpen;
51
+ } | {
52
+ type: 'error';
53
+ data: WebSocketError;
54
+ } | {
55
+ type: 'text';
56
+ data: string;
57
+ } | {
58
+ type: 'binary';
59
+ data: Uint8Array;
60
+ } | {
61
+ type: 'close';
62
+ data: WebSocketClose;
63
+ };
64
+
65
+ export type WebSocketHandle = {
66
+ ws: OmuWS;
67
+ open: (response: WebSocketOpen) => void;
68
+ dispatch: (data: DataChunk<WSDataMeta>) => void;
69
+ close: (response: WebSocketClose) => void;
70
+ error: (response: WebSocketError) => void;
71
+ };
72
+
73
+ export type WSDataMeta = {
74
+ id: string;
75
+ type:
76
+ | typeof WSMsgType.TEXT
77
+ | typeof WSMsgType.BINARY
78
+ | typeof WSMsgType.PING
79
+ | typeof WSMsgType.PONG;
80
+ };
81
+
82
+ export type WSHandleState = {
83
+ type: 'created' | 'receiving';
84
+ handle: WebSocketHandle;
85
+ };
86
+
87
+ type WSOptions = {
88
+ addQueue: (data: WSData) => void;
89
+ dispose(options: {
90
+ code?: number;
91
+ reason?: string;
92
+ }): void;
93
+ };
94
+
95
+ export class OmuWS {
96
+ constructor(
97
+ protected readonly addQueue: (data: WSData) => void,
98
+ protected readonly dispose: (options: { code?: number; reason?: string }) => void,
99
+ protected readonly receiveQueue: WSMsg[] = [],
100
+ private receiveResolve: (() => void) | undefined = undefined,
101
+ private status: 'connecting' | 'open' | 'closed' = 'connecting',
102
+ ) { }
103
+
104
+ public toWebSocket(): OmuWebSocket {
105
+ return new OmuWebSocket(this);
106
+ }
107
+
108
+ public static create(options: WSOptions): WebSocketHandle {
109
+ const ws = new OmuWS(options.addQueue, options.dispose);
110
+ return {
111
+ ws,
112
+ open: (response) => {
113
+ ws.receiveQueue.push({
114
+ type: 'open',
115
+ data: response,
116
+ });
117
+ ws.receiveResolve?.();
118
+ },
119
+ dispatch: (data: DataChunk<WSDataMeta>): void => {
120
+ if (data.meta.type === WSMsgType.BINARY) {
121
+ ws.receiveQueue.push({
122
+ type: 'binary',
123
+ data: data.body,
124
+ });
125
+ } else {
126
+ ws.receiveQueue.push({
127
+ type: 'text',
128
+ data: textDecoder.decode(data.body),
129
+ });
130
+ }
131
+ ws.receiveResolve?.();
132
+ },
133
+ close: (response) => {
134
+ ws.receiveQueue.push({
135
+ type: 'close',
136
+ data: response,
137
+ });
138
+ ws.receiveResolve?.();
139
+ },
140
+ error: (response) => {
141
+ ws.receiveQueue.push({
142
+ type: 'error',
143
+ data: response,
144
+ });
145
+ ws.receiveResolve?.();
146
+ },
147
+ };
148
+ }
149
+
150
+ public send(data: WSData): void {
151
+ this.addQueue(data);
152
+ }
153
+
154
+ public close(code?: number, reason?: string): void {
155
+ this.dispose({ code, reason });
156
+ }
157
+
158
+ public async receive(): Promise<WSMsg> {
159
+ if (this.receiveResolve) {
160
+ throw new Error('Already receiving from another async call');
161
+ }
162
+ while (this.status !== 'closed') {
163
+ const data = this.receiveQueue.shift();
164
+ if (!data) {
165
+ await new Promise<void>((resolve) => this.receiveResolve = resolve);
166
+ continue;
167
+ }
168
+ if (data.type === 'close') {
169
+ this.status = 'closed';
170
+ }
171
+ if (data.type === 'open') {
172
+ this.status = 'open';
173
+ }
174
+ this.receiveResolve = undefined;
175
+ return data;
176
+ }
177
+ throw new Error('Receiving on already closed socket');
178
+ }
179
+
180
+ protocol: string = '';
181
+ url: string = '';
182
+ }
183
+
184
+ export class OmuWebSocket implements WebSocket {
185
+ public CONNECTING = 0 as const;
186
+ public OPEN = 1 as const;
187
+ public CLOSING = 2 as const;
188
+ public CLOSED = 3 as const;
189
+
190
+ binaryType: BinaryType = 'blob';
191
+ bufferedAmount: number = 0;
192
+ extensions: string = '';
193
+ onclose: ((this: WebSocket, ev: CloseEvent) => unknown) | null = null;
194
+ onerror: ((this: WebSocket, ev: Event) => unknown) | null = null;
195
+ onmessage: ((this: WebSocket, ev: MessageEvent) => unknown) | null = null;
196
+ onopen: ((this: WebSocket, ev: Event) => unknown) | null = null;
197
+ protocol: string = '';
198
+ readyState: 0 | 2 | 1 | 3 = 0;
199
+ url: string = '';
200
+
201
+ constructor(
202
+ protected readonly ws: OmuWS,
203
+ private readonly eventListeners: EventListenerMap<WebSocketEventMap> = {
204
+ close: [],
205
+ error: [],
206
+ message: [],
207
+ open: [],
208
+ },
209
+ ) {
210
+ this.receiveLoop();
211
+ }
212
+
213
+ private async receiveLoop() {
214
+ while (this.readyState === WebSocket.CLOSED) {
215
+ const { data, type } = await this.ws.receive();
216
+ switch (type) {
217
+ case 'close':
218
+ this.dispatchEvent(new CloseEvent('close', {
219
+ code: data.code,
220
+ reason: data.reason ?? undefined,
221
+ }));
222
+ break;
223
+ case 'open':
224
+ this.url = data.url;
225
+ this.protocol = data.protocol ?? '';
226
+ this.dispatchEvent(new Event('open', {}));
227
+ break;
228
+ case 'error':
229
+ this.dispatchEvent(new ErrorEvent('error', {
230
+ error: data.type,
231
+ message: data.reason ?? undefined,
232
+ }));
233
+ break;
234
+ case 'text':
235
+ case 'binary':
236
+ this.dispatchEvent(new MessageEvent('message', {
237
+ data,
238
+ }));
239
+ break;
240
+ }
241
+ }
242
+ }
243
+
244
+ dispatchEvent(event: Event): boolean {
245
+ const listeners = this.eventListeners[event.type];
246
+ for (const listener of listeners) {
247
+ listener(event);
248
+ }
249
+ if (event.type === 'open') {
250
+ this.onopen?.(event as MessageEvent);
251
+ }
252
+ if (event.type === 'error') {
253
+ this.onerror?.(event);
254
+ }
255
+ if (event.type === 'message') {
256
+ this.onmessage?.(event as MessageEvent);
257
+ }
258
+ if (event.type === 'close') {
259
+ this.onclose?.(event as CloseEvent);
260
+ }
261
+ return true;
262
+ }
263
+
264
+ send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void {
265
+ this.ws.send(data);
266
+ }
267
+
268
+ close(code?: number, reason?: string): void {
269
+ this.ws.close(code, reason);
270
+ }
271
+
272
+ addEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => unknown): void {
273
+ this.eventListeners[type].push(listener);
274
+ }
275
+
276
+ removeEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => unknown): void {
277
+ const listeners = this.eventListeners[type];
278
+ const index = listeners.indexOf(listener);
279
+ if (index !== -1) {
280
+ listeners.splice(index, 1);
281
+ }
282
+ }
283
+ }
@@ -100,4 +100,8 @@ export class PermissionExtension {
100
100
  public has(permissionIdentifier: Identifier): boolean {
101
101
  return this.permissions.has(permissionIdentifier);
102
102
  }
103
+
104
+ public required(permissionIdentifier: Identifier): boolean {
105
+ return this.requiredPermissions.has(permissionIdentifier);
106
+ }
103
107
  }
@@ -9,7 +9,7 @@ import { EndpointType } from '../endpoint/endpoint.js';
9
9
  import { type Extension, ExtensionType } from '../extension.js';
10
10
 
11
11
  import { RegisterPacket, RegistryPacket } from './packets.js';
12
- import { type Registry, RegistryType } from './registry.js';
12
+ import { type Registry, RegistryType, Writable } from './registry.js';
13
13
 
14
14
  export class RegistryExtension implements Extension {
15
15
  public readonly type: ExtensionType<RegistryExtension> = REGISTRY_EXTENSION_TYPE;
@@ -162,6 +162,51 @@ class RegistryImpl<T> implements Registry<T> {
162
162
  permissions: this.type.permissions,
163
163
  });
164
164
  }
165
+
166
+ public compatSvelte(): Writable<T> {
167
+ let ready = false;
168
+ let value: T = this.value;
169
+ const listeners = new Set<(value: T) => void>();
170
+ this.listen((newValue) => {
171
+ ready = true;
172
+ value = newValue;
173
+ listeners.forEach((run) => {
174
+ run(value);
175
+ });
176
+ });
177
+ return {
178
+ set: (value: T) => {
179
+ if (!ready) {
180
+ throw new Error(`Registry ${this.type.id.key()} is not ready`);
181
+ }
182
+ this.set(value);
183
+ },
184
+ subscribe: (run) => {
185
+ listeners.add(run);
186
+ run(value);
187
+ return () => {
188
+ listeners.delete(run);
189
+ };
190
+ },
191
+ update: (fn) => {
192
+ if (!ready) {
193
+ throw new Error(`Registry ${this.type.id.key()} is not ready`);
194
+ }
195
+ this.update(fn);
196
+ },
197
+ wait: () => {
198
+ return new Promise<T>((resolve) => {
199
+ if (ready) {
200
+ resolve(value);
201
+ } else {
202
+ listeners.add(() => {
203
+ resolve(value);
204
+ });
205
+ }
206
+ });
207
+ },
208
+ };
209
+ }
165
210
  }
166
211
 
167
212
  export const REGISTRY_EXTENSION_TYPE: ExtensionType<RegistryExtension> = new ExtensionType(
@@ -70,4 +70,27 @@ export interface Registry<T> {
70
70
  update(fn: (value: T) => PromiseLike<T> | T): Promise<T>;
71
71
  modify(fn: (value: T) => PromiseLike<T> | T): Promise<T>;
72
72
  listen(fn: (value: T) => void): Unlisten;
73
+
74
+ compatSvelte(): Writable<T>;
75
+ }
76
+
77
+ export type Subscriber<T> = (value: T) => void;
78
+
79
+ export type Unsubscriber = () => void;
80
+
81
+ export type Updater<T> = (value: T) => T;
82
+
83
+ export type StartStopNotifier<T> = (
84
+ set: (value: T) => void,
85
+ update: (fn: Updater<T>) => void
86
+ ) => void | (() => void);
87
+
88
+ export interface Readable<T> {
89
+ subscribe(this: void, run: Subscriber<T>): Unsubscriber;
90
+ }
91
+
92
+ export interface Writable<T> extends Readable<T> {
93
+ set(this: void, value: T): void;
94
+ update(this: void, updater: Updater<T>): void;
95
+ wait(): Promise<T>;
73
96
  }
@@ -1,5 +1,6 @@
1
1
  import { App, AppJson } from '../../app.js';
2
2
  import { Identifier, IdentifierMap } from '../../identifier';
3
+ import { LocalizedText } from '../../localization/localization.js';
3
4
  import { Omu } from '../../omu.js';
4
5
  import { EndpointType } from '../endpoint/endpoint.js';
5
6
  import { ExtensionType, type Extension } from '../extension.js';
@@ -13,6 +14,7 @@ export const SERVER_EXTENSION_TYPE: ExtensionType<ServerExtension> = new Extensi
13
14
 
14
15
  export interface AppIndexEntry {
15
16
  url: string;
17
+ meta?: AppIndexRegistryMeta;
16
18
  added_at: string;
17
19
  };
18
20
 
@@ -96,8 +98,14 @@ export class SessionObserver {
96
98
  }
97
99
  }
98
100
 
101
+ export type AppIndexRegistryMeta = {
102
+ name: LocalizedText;
103
+ note: LocalizedText;
104
+ };
105
+
99
106
  export interface AppIndexRegistryJSON {
100
107
  id: string;
108
+ meta?: AppIndexRegistryMeta;
101
109
  apps: Record<string, AppJson>;
102
110
  }
103
111
 
@@ -105,6 +113,7 @@ export class AppIndexRegistry {
105
113
  constructor(
106
114
  public id: Identifier,
107
115
  public apps: IdentifierMap<App>,
116
+ public meta: AppIndexRegistryMeta | undefined,
108
117
  ) { }
109
118
 
110
119
  public static fromJSON(json: AppIndexRegistryJSON): AppIndexRegistry {
@@ -121,6 +130,7 @@ export class AppIndexRegistry {
121
130
  return new AppIndexRegistry(
122
131
  id,
123
132
  apps,
133
+ json.meta,
124
134
  );
125
135
  }
126
136
  }
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@ export { Omu } from './omu.js';
6
6
  export { Serializer } from './serialize';
7
7
  export type { Serializable } from './serialize';
8
8
  export { BrowserTokenProvider, type SessionParam, type TokenProvider } from './token';
9
- import { ASSET_DELETE_PERMISSION_ID, ASSET_DOWNLOAD_PERMISSION_ID, ASSET_UPLOAD_PERMISSION_ID } from './api/asset';
9
+ import { ASSET_PERMISSION_ID } from './api/asset';
10
10
  import {
11
11
  DASHBOARD_APP_INSTALL_PERMISSION_ID,
12
12
  DAShBOARD_DRAG_DROP_PERMISSION_ID,
@@ -29,7 +29,7 @@ import { Identifier } from './identifier';
29
29
 
30
30
  export const OmuPermissions: {
31
31
  // Asset API
32
- readonly ASSET_DELETE_PERMISSION_ID: Identifier; readonly ASSET_DOWNLOAD_PERMISSION_ID: Identifier; readonly ASSET_UPLOAD_PERMISSION_ID: Identifier;
32
+ readonly ASSET_PERMISSION_ID: Identifier;
33
33
  // Dashboard API
34
34
  readonly DASHBOARD_APP_INSTALL_PERMISSION_ID: Identifier; readonly DAShBOARD_DRAG_DROP_PERMISSION_ID: Identifier; readonly DASHBOARD_OPEN_APP_PERMISSION_ID: Identifier; readonly DASHBOARD_SET_PERMISSION_ID: Identifier; readonly DASHBOARD_SPEECH_RECOGNITION_PERMISSION_ID: Identifier; readonly DASHBOARD_WEBVIEW_PERMISSION_ID: Identifier; readonly DASHOBARD_APP_EDIT_PERMISSION_ID: Identifier; readonly DASHOBARD_APP_READ_PERMISSION_ID: Identifier;
35
35
  // HTTP API
@@ -50,9 +50,7 @@ export const OmuPermissions: {
50
50
  readonly TABLE_PERMISSION_ID: Identifier;
51
51
  } = {
52
52
  // Asset API
53
- ASSET_DELETE_PERMISSION_ID,
54
- ASSET_DOWNLOAD_PERMISSION_ID,
55
- ASSET_UPLOAD_PERMISSION_ID,
53
+ ASSET_PERMISSION_ID: ASSET_PERMISSION_ID,
56
54
  // Dashboard API
57
55
  DASHBOARD_APP_INSTALL_PERMISSION_ID,
58
56
  DAShBOARD_DRAG_DROP_PERMISSION_ID,
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.9.26';
1
+ export const VERSION = '0.9.32';