@objectstack/service-realtime 7.2.1 → 7.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +385 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +391 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
6
9
|
var __export = (target, all) => {
|
|
7
10
|
for (var name in all)
|
|
8
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -17,6 +20,280 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
20
|
};
|
|
18
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
22
|
|
|
23
|
+
// src/translations/en.objects.generated.ts
|
|
24
|
+
var enObjects;
|
|
25
|
+
var init_en_objects_generated = __esm({
|
|
26
|
+
"src/translations/en.objects.generated.ts"() {
|
|
27
|
+
"use strict";
|
|
28
|
+
enObjects = {
|
|
29
|
+
sys_presence: {
|
|
30
|
+
label: "Presence",
|
|
31
|
+
pluralLabel: "Presences",
|
|
32
|
+
description: "Real-time user presence and activity tracking",
|
|
33
|
+
fields: {
|
|
34
|
+
id: {
|
|
35
|
+
label: "Presence ID"
|
|
36
|
+
},
|
|
37
|
+
created_at: {
|
|
38
|
+
label: "Created At"
|
|
39
|
+
},
|
|
40
|
+
updated_at: {
|
|
41
|
+
label: "Updated At"
|
|
42
|
+
},
|
|
43
|
+
user_id: {
|
|
44
|
+
label: "User"
|
|
45
|
+
},
|
|
46
|
+
session_id: {
|
|
47
|
+
label: "Session"
|
|
48
|
+
},
|
|
49
|
+
status: {
|
|
50
|
+
label: "Status",
|
|
51
|
+
options: {
|
|
52
|
+
online: "Online",
|
|
53
|
+
away: "Away",
|
|
54
|
+
busy: "Busy",
|
|
55
|
+
offline: "Offline"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
last_seen: {
|
|
59
|
+
label: "Last Seen"
|
|
60
|
+
},
|
|
61
|
+
current_location: {
|
|
62
|
+
label: "Current Location"
|
|
63
|
+
},
|
|
64
|
+
device: {
|
|
65
|
+
label: "Device",
|
|
66
|
+
options: {
|
|
67
|
+
desktop: "Desktop",
|
|
68
|
+
mobile: "Mobile",
|
|
69
|
+
tablet: "Tablet",
|
|
70
|
+
other: "Other"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
custom_status: {
|
|
74
|
+
label: "Custom Status"
|
|
75
|
+
},
|
|
76
|
+
metadata: {
|
|
77
|
+
label: "Metadata",
|
|
78
|
+
help: "Arbitrary JSON metadata associated with the presence state (matches PresenceStateSchema.metadata)."
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// src/translations/zh-CN.objects.generated.ts
|
|
87
|
+
var zhCNObjects;
|
|
88
|
+
var init_zh_CN_objects_generated = __esm({
|
|
89
|
+
"src/translations/zh-CN.objects.generated.ts"() {
|
|
90
|
+
"use strict";
|
|
91
|
+
zhCNObjects = {
|
|
92
|
+
sys_presence: {
|
|
93
|
+
label: "\u5728\u7EBF\u72B6\u6001",
|
|
94
|
+
pluralLabel: "\u5728\u7EBF\u72B6\u6001",
|
|
95
|
+
description: "\u5B9E\u65F6\u7528\u6237\u5728\u7EBF\u4E0E\u6D3B\u52A8\u8DDF\u8E2A",
|
|
96
|
+
fields: {
|
|
97
|
+
id: {
|
|
98
|
+
label: "\u5728\u7EBF\u72B6\u6001 ID"
|
|
99
|
+
},
|
|
100
|
+
created_at: {
|
|
101
|
+
label: "\u521B\u5EFA\u65F6\u95F4"
|
|
102
|
+
},
|
|
103
|
+
updated_at: {
|
|
104
|
+
label: "\u66F4\u65B0\u65F6\u95F4"
|
|
105
|
+
},
|
|
106
|
+
user_id: {
|
|
107
|
+
label: "\u7528\u6237"
|
|
108
|
+
},
|
|
109
|
+
session_id: {
|
|
110
|
+
label: "\u4F1A\u8BDD"
|
|
111
|
+
},
|
|
112
|
+
status: {
|
|
113
|
+
label: "\u72B6\u6001",
|
|
114
|
+
options: {
|
|
115
|
+
online: "\u5728\u7EBF",
|
|
116
|
+
away: "\u79BB\u5F00",
|
|
117
|
+
busy: "\u5FD9\u788C",
|
|
118
|
+
offline: "\u79BB\u7EBF"
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
last_seen: {
|
|
122
|
+
label: "\u6700\u8FD1\u5728\u7EBF\u65F6\u95F4"
|
|
123
|
+
},
|
|
124
|
+
current_location: {
|
|
125
|
+
label: "\u5F53\u524D\u4F4D\u7F6E"
|
|
126
|
+
},
|
|
127
|
+
device: {
|
|
128
|
+
label: "\u8BBE\u5907",
|
|
129
|
+
options: {
|
|
130
|
+
desktop: "\u684C\u9762\u7AEF",
|
|
131
|
+
mobile: "\u79FB\u52A8\u7AEF",
|
|
132
|
+
tablet: "\u5E73\u677F\u7AEF",
|
|
133
|
+
other: "\u5176\u4ED6"
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
custom_status: {
|
|
137
|
+
label: "\u81EA\u5B9A\u4E49\u72B6\u6001"
|
|
138
|
+
},
|
|
139
|
+
metadata: {
|
|
140
|
+
label: "\u5143\u6570\u636E",
|
|
141
|
+
help: "\u4E0E\u5728\u7EBF\u72B6\u6001\u5173\u8054\u7684\u4EFB\u610F JSON \u5143\u6570\u636E\uFF08\u5BF9\u5E94 PresenceStateSchema.metadata\uFF09\u3002"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// src/translations/ja-JP.objects.generated.ts
|
|
150
|
+
var jaJPObjects;
|
|
151
|
+
var init_ja_JP_objects_generated = __esm({
|
|
152
|
+
"src/translations/ja-JP.objects.generated.ts"() {
|
|
153
|
+
"use strict";
|
|
154
|
+
jaJPObjects = {
|
|
155
|
+
sys_presence: {
|
|
156
|
+
label: "\u5728\u5E2D\u72B6\u6CC1",
|
|
157
|
+
pluralLabel: "\u5728\u5E2D\u72B6\u6CC1",
|
|
158
|
+
description: "\u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u306E\u30E6\u30FC\u30B6\u30FC\u5728\u5E2D\u72B6\u6CC1\u3068\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\u8FFD\u8DE1",
|
|
159
|
+
fields: {
|
|
160
|
+
id: {
|
|
161
|
+
label: "\u5728\u5E2D ID"
|
|
162
|
+
},
|
|
163
|
+
created_at: {
|
|
164
|
+
label: "\u4F5C\u6210\u65E5\u6642"
|
|
165
|
+
},
|
|
166
|
+
updated_at: {
|
|
167
|
+
label: "\u66F4\u65B0\u65E5\u6642"
|
|
168
|
+
},
|
|
169
|
+
user_id: {
|
|
170
|
+
label: "\u30E6\u30FC\u30B6\u30FC"
|
|
171
|
+
},
|
|
172
|
+
session_id: {
|
|
173
|
+
label: "\u30BB\u30C3\u30B7\u30E7\u30F3"
|
|
174
|
+
},
|
|
175
|
+
status: {
|
|
176
|
+
label: "\u30B9\u30C6\u30FC\u30BF\u30B9",
|
|
177
|
+
options: {
|
|
178
|
+
online: "\u30AA\u30F3\u30E9\u30A4\u30F3",
|
|
179
|
+
away: "\u96E2\u5E2D\u4E2D",
|
|
180
|
+
busy: "\u53D6\u308A\u8FBC\u307F\u4E2D",
|
|
181
|
+
offline: "\u30AA\u30D5\u30E9\u30A4\u30F3"
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
last_seen: {
|
|
185
|
+
label: "\u6700\u7D42\u78BA\u8A8D\u65E5\u6642"
|
|
186
|
+
},
|
|
187
|
+
current_location: {
|
|
188
|
+
label: "\u73FE\u5728\u5730"
|
|
189
|
+
},
|
|
190
|
+
device: {
|
|
191
|
+
label: "\u30C7\u30D0\u30A4\u30B9",
|
|
192
|
+
options: {
|
|
193
|
+
desktop: "\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7",
|
|
194
|
+
mobile: "\u30E2\u30D0\u30A4\u30EB",
|
|
195
|
+
tablet: "\u30BF\u30D6\u30EC\u30C3\u30C8",
|
|
196
|
+
other: "\u305D\u306E\u4ED6"
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
custom_status: {
|
|
200
|
+
label: "\u30AB\u30B9\u30BF\u30E0\u30B9\u30C6\u30FC\u30BF\u30B9"
|
|
201
|
+
},
|
|
202
|
+
metadata: {
|
|
203
|
+
label: "\u30E1\u30BF\u30C7\u30FC\u30BF",
|
|
204
|
+
help: "\u5728\u5E2D\u72B6\u614B\u306B\u95A2\u9023\u4ED8\u3051\u3089\u308C\u305F\u4EFB\u610F\u306E JSON \u30E1\u30BF\u30C7\u30FC\u30BF\uFF08PresenceStateSchema.metadata \u306B\u5BFE\u5FDC\uFF09\u3002"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// src/translations/es-ES.objects.generated.ts
|
|
213
|
+
var esESObjects;
|
|
214
|
+
var init_es_ES_objects_generated = __esm({
|
|
215
|
+
"src/translations/es-ES.objects.generated.ts"() {
|
|
216
|
+
"use strict";
|
|
217
|
+
esESObjects = {
|
|
218
|
+
sys_presence: {
|
|
219
|
+
label: "Presencia",
|
|
220
|
+
pluralLabel: "Presencias",
|
|
221
|
+
description: "Seguimiento en tiempo real de presencia y actividad de usuarios",
|
|
222
|
+
fields: {
|
|
223
|
+
id: {
|
|
224
|
+
label: "ID de presencia"
|
|
225
|
+
},
|
|
226
|
+
created_at: {
|
|
227
|
+
label: "Creado el"
|
|
228
|
+
},
|
|
229
|
+
updated_at: {
|
|
230
|
+
label: "Actualizado el"
|
|
231
|
+
},
|
|
232
|
+
user_id: {
|
|
233
|
+
label: "Usuario"
|
|
234
|
+
},
|
|
235
|
+
session_id: {
|
|
236
|
+
label: "Sesi\xF3n"
|
|
237
|
+
},
|
|
238
|
+
status: {
|
|
239
|
+
label: "Estado",
|
|
240
|
+
options: {
|
|
241
|
+
online: "En l\xEDnea",
|
|
242
|
+
away: "Ausente",
|
|
243
|
+
busy: "Ocupado",
|
|
244
|
+
offline: "Desconectado"
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
last_seen: {
|
|
248
|
+
label: "Visto por \xFAltima vez"
|
|
249
|
+
},
|
|
250
|
+
current_location: {
|
|
251
|
+
label: "Ubicaci\xF3n actual"
|
|
252
|
+
},
|
|
253
|
+
device: {
|
|
254
|
+
label: "Dispositivo",
|
|
255
|
+
options: {
|
|
256
|
+
desktop: "Escritorio",
|
|
257
|
+
mobile: "M\xF3vil",
|
|
258
|
+
tablet: "Tableta",
|
|
259
|
+
other: "Otro"
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
custom_status: {
|
|
263
|
+
label: "Estado personalizado"
|
|
264
|
+
},
|
|
265
|
+
metadata: {
|
|
266
|
+
label: "Metadatos",
|
|
267
|
+
help: "Metadatos JSON arbitrarios asociados al estado de presencia (coincide con PresenceStateSchema.metadata)."
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// src/translations/index.ts
|
|
276
|
+
var translations_exports = {};
|
|
277
|
+
__export(translations_exports, {
|
|
278
|
+
RealtimeTranslations: () => RealtimeTranslations
|
|
279
|
+
});
|
|
280
|
+
var RealtimeTranslations;
|
|
281
|
+
var init_translations = __esm({
|
|
282
|
+
"src/translations/index.ts"() {
|
|
283
|
+
"use strict";
|
|
284
|
+
init_en_objects_generated();
|
|
285
|
+
init_zh_CN_objects_generated();
|
|
286
|
+
init_ja_JP_objects_generated();
|
|
287
|
+
init_es_ES_objects_generated();
|
|
288
|
+
RealtimeTranslations = {
|
|
289
|
+
en: { objects: enObjects },
|
|
290
|
+
"zh-CN": { objects: zhCNObjects },
|
|
291
|
+
"ja-JP": { objects: jaJPObjects },
|
|
292
|
+
"es-ES": { objects: esESObjects }
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
20
297
|
// src/index.ts
|
|
21
298
|
var index_exports = {};
|
|
22
299
|
__export(index_exports, {
|
|
@@ -25,8 +302,99 @@ __export(index_exports, {
|
|
|
25
302
|
});
|
|
26
303
|
module.exports = __toCommonJS(index_exports);
|
|
27
304
|
|
|
28
|
-
// src/
|
|
29
|
-
var
|
|
305
|
+
// src/objects/sys-presence.object.ts
|
|
306
|
+
var import_data = require("@objectstack/spec/data");
|
|
307
|
+
var SysPresence = import_data.ObjectSchema.create({
|
|
308
|
+
name: "sys_presence",
|
|
309
|
+
label: "Presence",
|
|
310
|
+
pluralLabel: "Presences",
|
|
311
|
+
icon: "wifi",
|
|
312
|
+
isSystem: true,
|
|
313
|
+
managedBy: "append-only",
|
|
314
|
+
description: "Real-time user presence and activity tracking",
|
|
315
|
+
titleFormat: "{user_id} ({status})",
|
|
316
|
+
compactLayout: ["user_id", "status", "last_seen"],
|
|
317
|
+
fields: {
|
|
318
|
+
id: import_data.Field.text({
|
|
319
|
+
label: "Presence ID",
|
|
320
|
+
required: true,
|
|
321
|
+
readonly: true
|
|
322
|
+
}),
|
|
323
|
+
created_at: import_data.Field.datetime({
|
|
324
|
+
label: "Created At",
|
|
325
|
+
defaultValue: "NOW()",
|
|
326
|
+
readonly: true
|
|
327
|
+
}),
|
|
328
|
+
updated_at: import_data.Field.datetime({
|
|
329
|
+
label: "Updated At",
|
|
330
|
+
defaultValue: "NOW()",
|
|
331
|
+
readonly: true
|
|
332
|
+
}),
|
|
333
|
+
user_id: import_data.Field.lookup("sys_user", {
|
|
334
|
+
label: "User",
|
|
335
|
+
required: true,
|
|
336
|
+
searchable: true
|
|
337
|
+
}),
|
|
338
|
+
session_id: import_data.Field.lookup("sys_session", {
|
|
339
|
+
label: "Session",
|
|
340
|
+
required: true
|
|
341
|
+
}),
|
|
342
|
+
status: import_data.Field.select({
|
|
343
|
+
label: "Status",
|
|
344
|
+
required: true,
|
|
345
|
+
defaultValue: "online",
|
|
346
|
+
options: [
|
|
347
|
+
{ value: "online", label: "Online" },
|
|
348
|
+
{ value: "away", label: "Away" },
|
|
349
|
+
{ value: "busy", label: "Busy" },
|
|
350
|
+
{ value: "offline", label: "Offline" }
|
|
351
|
+
]
|
|
352
|
+
}),
|
|
353
|
+
last_seen: import_data.Field.datetime({
|
|
354
|
+
label: "Last Seen",
|
|
355
|
+
required: true,
|
|
356
|
+
defaultValue: "NOW()"
|
|
357
|
+
}),
|
|
358
|
+
current_location: import_data.Field.text({
|
|
359
|
+
label: "Current Location",
|
|
360
|
+
required: false,
|
|
361
|
+
maxLength: 500
|
|
362
|
+
}),
|
|
363
|
+
device: import_data.Field.select({
|
|
364
|
+
label: "Device",
|
|
365
|
+
required: false,
|
|
366
|
+
options: [
|
|
367
|
+
{ value: "desktop", label: "Desktop" },
|
|
368
|
+
{ value: "mobile", label: "Mobile" },
|
|
369
|
+
{ value: "tablet", label: "Tablet" },
|
|
370
|
+
{ value: "other", label: "Other" }
|
|
371
|
+
]
|
|
372
|
+
}),
|
|
373
|
+
custom_status: import_data.Field.text({
|
|
374
|
+
label: "Custom Status",
|
|
375
|
+
required: false,
|
|
376
|
+
maxLength: 255
|
|
377
|
+
}),
|
|
378
|
+
metadata: import_data.Field.json({
|
|
379
|
+
label: "Metadata",
|
|
380
|
+
required: false,
|
|
381
|
+
description: "Arbitrary JSON metadata associated with the presence state (matches PresenceStateSchema.metadata)."
|
|
382
|
+
})
|
|
383
|
+
},
|
|
384
|
+
indexes: [
|
|
385
|
+
{ fields: ["user_id"], unique: false },
|
|
386
|
+
{ fields: ["session_id"], unique: true },
|
|
387
|
+
{ fields: ["status"], unique: false }
|
|
388
|
+
],
|
|
389
|
+
enable: {
|
|
390
|
+
trackHistory: false,
|
|
391
|
+
searchable: false,
|
|
392
|
+
apiEnabled: true,
|
|
393
|
+
apiMethods: ["get", "list", "create", "update", "delete"],
|
|
394
|
+
trash: false,
|
|
395
|
+
mru: false
|
|
396
|
+
}
|
|
397
|
+
});
|
|
30
398
|
|
|
31
399
|
// src/in-memory-realtime-adapter.ts
|
|
32
400
|
var InMemoryRealtimeAdapter = class {
|
|
@@ -122,8 +490,22 @@ var RealtimeServicePlugin = class {
|
|
|
122
490
|
type: "plugin",
|
|
123
491
|
scope: "system",
|
|
124
492
|
namespace: "sys",
|
|
125
|
-
objects: [
|
|
493
|
+
objects: [SysPresence]
|
|
126
494
|
});
|
|
495
|
+
if (typeof ctx.hook === "function") {
|
|
496
|
+
ctx.hook("kernel:ready", async () => {
|
|
497
|
+
try {
|
|
498
|
+
const i18n = ctx.getService("i18n");
|
|
499
|
+
if (i18n && typeof i18n.loadTranslations === "function") {
|
|
500
|
+
const { RealtimeTranslations: RealtimeTranslations2 } = await Promise.resolve().then(() => (init_translations(), translations_exports));
|
|
501
|
+
for (const [locale, data] of Object.entries(RealtimeTranslations2)) {
|
|
502
|
+
i18n.loadTranslations(locale, data);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
} catch {
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
}
|
|
127
509
|
ctx.logger.info("RealtimeServicePlugin: registered in-memory realtime adapter");
|
|
128
510
|
}
|
|
129
511
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/realtime-service-plugin.ts","../src/in-memory-realtime-adapter.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport { RealtimeServicePlugin } from './realtime-service-plugin.js';\nexport type { RealtimeServicePluginOptions } from './realtime-service-plugin.js';\nexport { InMemoryRealtimeAdapter } from './in-memory-realtime-adapter.js';\nexport type { InMemoryRealtimeAdapterOptions } from './in-memory-realtime-adapter.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { SysPresence } from '@objectstack/platform-objects/audit';\nimport { InMemoryRealtimeAdapter } from './in-memory-realtime-adapter.js';\nimport type { InMemoryRealtimeAdapterOptions } from './in-memory-realtime-adapter.js';\n\n/**\n * Configuration options for the RealtimeServicePlugin.\n */\nexport interface RealtimeServicePluginOptions {\n /** Realtime adapter type (default: 'memory') */\n adapter?: 'memory';\n /** Options for the in-memory adapter */\n memory?: InMemoryRealtimeAdapterOptions;\n}\n\n/**\n * RealtimeServicePlugin — Production IRealtimeService implementation.\n *\n * Registers a realtime pub/sub service with the kernel during the init phase.\n * Currently supports in-memory pub/sub for single-process environments.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { RealtimeServicePlugin } from '@objectstack/service-realtime';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new RealtimeServicePlugin());\n * await kernel.bootstrap();\n *\n * const realtime = kernel.getService('realtime');\n * await realtime.subscribe('records', (event) => {\n * console.log(event.type, event.payload);\n * });\n * ```\n */\nexport class RealtimeServicePlugin implements Plugin {\n name = 'com.objectstack.service.realtime';\n version = '1.0.0';\n type = 'standard';\n dependencies = ['com.objectstack.engine.objectql'];\n\n private readonly options: RealtimeServicePluginOptions;\n\n constructor(options: RealtimeServicePluginOptions = {}) {\n this.options = { adapter: 'memory', ...options };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n const realtime = new InMemoryRealtimeAdapter(this.options.memory);\n ctx.registerService('realtime', realtime);\n\n // Register realtime system objects via the manifest service.\n ctx.getService<{ register(m: any): void }>('manifest').register({\n id: 'com.objectstack.service.realtime',\n name: 'Realtime Service',\n version: '1.0.0',\n type: 'plugin',\n scope: 'system',\n namespace: 'sys',\n objects: [SysPresence],\n });\n\n ctx.logger.info('RealtimeServicePlugin: registered in-memory realtime adapter');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n IRealtimeService,\n RealtimeEventPayload,\n RealtimeEventHandler,\n RealtimeSubscriptionOptions,\n} from '@objectstack/spec/contracts';\n\n/**\n * Internal subscription entry.\n */\ninterface Subscription {\n id: string;\n channel: string;\n handler: RealtimeEventHandler;\n options?: RealtimeSubscriptionOptions;\n}\n\n/**\n * Configuration options for InMemoryRealtimeAdapter.\n */\nexport interface InMemoryRealtimeAdapterOptions {\n /** Maximum number of subscriptions allowed (0 = unlimited) */\n maxSubscriptions?: number;\n}\n\n/**\n * In-memory pub/sub adapter implementing IRealtimeService.\n *\n * Uses a Map-backed subscription store with channel-based routing.\n * Supports event type and object filtering via subscription options.\n *\n * Suitable for single-process environments, development, and testing.\n * For production multi-instance deployments, use a Redis-backed adapter.\n *\n * @example\n * ```ts\n * const realtime = new InMemoryRealtimeAdapter();\n *\n * const subId = await realtime.subscribe('records', (event) => {\n * console.log('Received:', event.type, event.payload);\n * }, { object: 'account', eventTypes: ['record.created'] });\n *\n * await realtime.publish({\n * type: 'record.created',\n * object: 'account',\n * payload: { id: 'acc-1', name: 'Acme' },\n * timestamp: new Date().toISOString(),\n * });\n *\n * await realtime.unsubscribe(subId);\n * ```\n */\nexport class InMemoryRealtimeAdapter implements IRealtimeService {\n private readonly subscriptions = new Map<string, Subscription>();\n private readonly channelIndex = new Map<string, Set<string>>();\n private counter = 0;\n private readonly maxSubscriptions: number;\n\n constructor(options: InMemoryRealtimeAdapterOptions = {}) {\n this.maxSubscriptions = options.maxSubscriptions ?? 0;\n }\n\n async publish(event: RealtimeEventPayload): Promise<void> {\n // Deliver to all channel subscriptions that match filters\n for (const sub of this.subscriptions.values()) {\n if (this.matchesSubscription(event, sub)) {\n try {\n await sub.handler(event);\n } catch {\n // Swallow handler errors to avoid breaking the publish loop\n }\n }\n }\n }\n\n async subscribe(\n channel: string,\n handler: RealtimeEventHandler,\n options?: RealtimeSubscriptionOptions,\n ): Promise<string> {\n if (this.maxSubscriptions > 0 && this.subscriptions.size >= this.maxSubscriptions) {\n throw new Error(\n `Maximum subscription limit reached (${this.maxSubscriptions}). ` +\n 'Unsubscribe from existing channels before adding new subscriptions.',\n );\n }\n\n const id = `sub-${++this.counter}`;\n const sub: Subscription = { id, channel, handler, options };\n this.subscriptions.set(id, sub);\n\n // Maintain channel index for efficient lookups\n if (!this.channelIndex.has(channel)) {\n this.channelIndex.set(channel, new Set());\n }\n this.channelIndex.get(channel)!.add(id);\n\n return id;\n }\n\n async unsubscribe(subscriptionId: string): Promise<void> {\n const sub = this.subscriptions.get(subscriptionId);\n if (!sub) return;\n\n this.subscriptions.delete(subscriptionId);\n\n // Clean up channel index\n const channelSubs = this.channelIndex.get(sub.channel);\n if (channelSubs) {\n channelSubs.delete(subscriptionId);\n if (channelSubs.size === 0) {\n this.channelIndex.delete(sub.channel);\n }\n }\n }\n\n /**\n * Get the number of active subscriptions.\n */\n getSubscriptionCount(): number {\n return this.subscriptions.size;\n }\n\n /**\n * Get all active channel names.\n */\n getChannels(): string[] {\n return Array.from(this.channelIndex.keys());\n }\n\n /**\n * Check if an event matches a subscription's filters.\n */\n private matchesSubscription(event: RealtimeEventPayload, sub: Subscription): boolean {\n const opts = sub.options;\n if (!opts) return true;\n\n // Filter by object name\n if (opts.object && event.object !== opts.object) {\n return false;\n }\n\n // Filter by event types\n if (opts.eventTypes && opts.eventTypes.length > 0) {\n if (!opts.eventTypes.includes(event.type)) {\n return false;\n }\n }\n\n return true;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/translations/en.objects.generated.ts","../src/translations/zh-CN.objects.generated.ts","../src/translations/ja-JP.objects.generated.ts","../src/translations/es-ES.objects.generated.ts","../src/translations/index.ts","../src/index.ts","../src/objects/sys-presence.object.ts","../src/in-memory-realtime-adapter.ts","../src/realtime-service-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'en'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const enObjects: NonNullable<TranslationData['objects']> = {\n sys_presence: {\n label: \"Presence\",\n pluralLabel: \"Presences\",\n description: \"Real-time user presence and activity tracking\",\n fields: {\n id: {\n label: \"Presence ID\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n },\n user_id: {\n label: \"User\"\n },\n session_id: {\n label: \"Session\"\n },\n status: {\n label: \"Status\",\n options: {\n online: \"Online\",\n away: \"Away\",\n busy: \"Busy\",\n offline: \"Offline\"\n }\n },\n last_seen: {\n label: \"Last Seen\"\n },\n current_location: {\n label: \"Current Location\"\n },\n device: {\n label: \"Device\",\n options: {\n desktop: \"Desktop\",\n mobile: \"Mobile\",\n tablet: \"Tablet\",\n other: \"Other\"\n }\n },\n custom_status: {\n label: \"Custom Status\"\n },\n metadata: {\n label: \"Metadata\",\n help: \"Arbitrary JSON metadata associated with the presence state (matches PresenceStateSchema.metadata).\"\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'zh-CN'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const zhCNObjects: NonNullable<TranslationData['objects']> = {\n sys_presence: {\n label: \"在线状态\",\n pluralLabel: \"在线状态\",\n description: \"实时用户在线与活动跟踪\",\n fields: {\n id: {\n label: \"在线状态 ID\"\n },\n created_at: {\n label: \"创建时间\"\n },\n updated_at: {\n label: \"更新时间\"\n },\n user_id: {\n label: \"用户\"\n },\n session_id: {\n label: \"会话\"\n },\n status: {\n label: \"状态\",\n options: {\n online: \"在线\",\n away: \"离开\",\n busy: \"忙碌\",\n offline: \"离线\"\n }\n },\n last_seen: {\n label: \"最近在线时间\"\n },\n current_location: {\n label: \"当前位置\"\n },\n device: {\n label: \"设备\",\n options: {\n desktop: \"桌面端\",\n mobile: \"移动端\",\n tablet: \"平板端\",\n other: \"其他\"\n }\n },\n custom_status: {\n label: \"自定义状态\"\n },\n metadata: {\n label: \"元数据\",\n help: \"与在线状态关联的任意 JSON 元数据(对应 PresenceStateSchema.metadata)。\"\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'ja-JP'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const jaJPObjects: NonNullable<TranslationData['objects']> = {\n sys_presence: {\n label: \"在席状況\",\n pluralLabel: \"在席状況\",\n description: \"リアルタイムのユーザー在席状況とアクティビティ追跡\",\n fields: {\n id: {\n label: \"在席 ID\"\n },\n created_at: {\n label: \"作成日時\"\n },\n updated_at: {\n label: \"更新日時\"\n },\n user_id: {\n label: \"ユーザー\"\n },\n session_id: {\n label: \"セッション\"\n },\n status: {\n label: \"ステータス\",\n options: {\n online: \"オンライン\",\n away: \"離席中\",\n busy: \"取り込み中\",\n offline: \"オフライン\"\n }\n },\n last_seen: {\n label: \"最終確認日時\"\n },\n current_location: {\n label: \"現在地\"\n },\n device: {\n label: \"デバイス\",\n options: {\n desktop: \"デスクトップ\",\n mobile: \"モバイル\",\n tablet: \"タブレット\",\n other: \"その他\"\n }\n },\n custom_status: {\n label: \"カスタムステータス\"\n },\n metadata: {\n label: \"メタデータ\",\n help: \"在席状態に関連付けられた任意の JSON メタデータ(PresenceStateSchema.metadata に対応)。\"\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'es-ES'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const esESObjects: NonNullable<TranslationData['objects']> = {\n sys_presence: {\n label: \"Presencia\",\n pluralLabel: \"Presencias\",\n description: \"Seguimiento en tiempo real de presencia y actividad de usuarios\",\n fields: {\n id: {\n label: \"ID de presencia\"\n },\n created_at: {\n label: \"Creado el\"\n },\n updated_at: {\n label: \"Actualizado el\"\n },\n user_id: {\n label: \"Usuario\"\n },\n session_id: {\n label: \"Sesión\"\n },\n status: {\n label: \"Estado\",\n options: {\n online: \"En línea\",\n away: \"Ausente\",\n busy: \"Ocupado\",\n offline: \"Desconectado\"\n }\n },\n last_seen: {\n label: \"Visto por última vez\"\n },\n current_location: {\n label: \"Ubicación actual\"\n },\n device: {\n label: \"Dispositivo\",\n options: {\n desktop: \"Escritorio\",\n mobile: \"Móvil\",\n tablet: \"Tableta\",\n other: \"Otro\"\n }\n },\n custom_status: {\n label: \"Estado personalizado\"\n },\n metadata: {\n label: \"Metadatos\",\n help: \"Metadatos JSON arbitrarios asociados al estado de presencia (coincide con PresenceStateSchema.metadata).\"\n }\n }\n }\n};\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * RealtimeTranslations — i18n bundle owned by this service plugin\n * (ADR-0029 D8).\n *\n * Object label/field/view/action translations for sys_presence, the object\n * this plugin owns. Loaded at runtime via the plugin's `kernel:ready` hook\n * (`i18n.loadTranslations`). Regenerate with `os i18n extract` against\n * `scripts/i18n-extract.config.ts`.\n */\n\nimport type { TranslationBundle } from '@objectstack/spec/system';\nimport { enObjects } from './en.objects.generated.js';\nimport { zhCNObjects } from './zh-CN.objects.generated.js';\nimport { jaJPObjects } from './ja-JP.objects.generated.js';\nimport { esESObjects } from './es-ES.objects.generated.js';\n\nexport const RealtimeTranslations: TranslationBundle = {\n en: { objects: enObjects },\n 'zh-CN': { objects: zhCNObjects },\n 'ja-JP': { objects: jaJPObjects },\n 'es-ES': { objects: esESObjects },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport { RealtimeServicePlugin } from './realtime-service-plugin.js';\nexport type { RealtimeServicePluginOptions } from './realtime-service-plugin.js';\nexport { InMemoryRealtimeAdapter } from './in-memory-realtime-adapter.js';\nexport type { InMemoryRealtimeAdapterOptions } from './in-memory-realtime-adapter.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_presence — System Presence Object\n *\n * Tracks real-time user presence and activity across the platform.\n * Fields align with the PresenceStateSchema protocol definition\n * from `@objectstack/spec/api` (websocket.zod.ts).\n *\n * Owned by `service-realtime` as the canonical Presence domain object.\n *\n * @namespace sys\n * @see PresenceStateSchema in packages/spec/src/api/websocket.zod.ts\n */\nexport const SysPresence = ObjectSchema.create({\n name: 'sys_presence',\n label: 'Presence',\n pluralLabel: 'Presences',\n icon: 'wifi',\n isSystem: true,\n managedBy: 'append-only',\n description: 'Real-time user presence and activity tracking',\n titleFormat: '{user_id} ({status})',\n compactLayout: ['user_id', 'status', 'last_seen'],\n\n fields: {\n id: Field.text({\n label: 'Presence ID',\n required: true,\n readonly: true,\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n defaultValue: 'NOW()',\n readonly: true,\n }),\n\n updated_at: Field.datetime({\n label: 'Updated At',\n defaultValue: 'NOW()',\n readonly: true,\n }),\n\n user_id: Field.lookup('sys_user', {\n label: 'User',\n required: true,\n searchable: true,\n }),\n\n session_id: Field.lookup('sys_session', {\n label: 'Session',\n required: true,\n }),\n\n status: Field.select({\n label: 'Status',\n required: true,\n defaultValue: 'online',\n options: [\n { value: 'online', label: 'Online' },\n { value: 'away', label: 'Away' },\n { value: 'busy', label: 'Busy' },\n { value: 'offline', label: 'Offline' },\n ],\n }),\n\n last_seen: Field.datetime({\n label: 'Last Seen',\n required: true,\n defaultValue: 'NOW()',\n }),\n\n current_location: Field.text({\n label: 'Current Location',\n required: false,\n maxLength: 500,\n }),\n\n device: Field.select({\n label: 'Device',\n required: false,\n options: [\n { value: 'desktop', label: 'Desktop' },\n { value: 'mobile', label: 'Mobile' },\n { value: 'tablet', label: 'Tablet' },\n { value: 'other', label: 'Other' },\n ],\n }),\n\n custom_status: Field.text({\n label: 'Custom Status',\n required: false,\n maxLength: 255,\n }),\n\n metadata: Field.json({\n label: 'Metadata',\n required: false,\n description: 'Arbitrary JSON metadata associated with the presence state (matches PresenceStateSchema.metadata).',\n }),\n },\n\n indexes: [\n { fields: ['user_id'], unique: false },\n { fields: ['session_id'], unique: true },\n { fields: ['status'], unique: false },\n ],\n\n enable: {\n trackHistory: false,\n searchable: false,\n apiEnabled: true,\n apiMethods: ['get', 'list', 'create', 'update', 'delete'],\n trash: false,\n mru: false,\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n IRealtimeService,\n RealtimeEventPayload,\n RealtimeEventHandler,\n RealtimeSubscriptionOptions,\n} from '@objectstack/spec/contracts';\n\n/**\n * Internal subscription entry.\n */\ninterface Subscription {\n id: string;\n channel: string;\n handler: RealtimeEventHandler;\n options?: RealtimeSubscriptionOptions;\n}\n\n/**\n * Configuration options for InMemoryRealtimeAdapter.\n */\nexport interface InMemoryRealtimeAdapterOptions {\n /** Maximum number of subscriptions allowed (0 = unlimited) */\n maxSubscriptions?: number;\n}\n\n/**\n * In-memory pub/sub adapter implementing IRealtimeService.\n *\n * Uses a Map-backed subscription store with channel-based routing.\n * Supports event type and object filtering via subscription options.\n *\n * Suitable for single-process environments, development, and testing.\n * For production multi-instance deployments, use a Redis-backed adapter.\n *\n * @example\n * ```ts\n * const realtime = new InMemoryRealtimeAdapter();\n *\n * const subId = await realtime.subscribe('records', (event) => {\n * console.log('Received:', event.type, event.payload);\n * }, { object: 'account', eventTypes: ['record.created'] });\n *\n * await realtime.publish({\n * type: 'record.created',\n * object: 'account',\n * payload: { id: 'acc-1', name: 'Acme' },\n * timestamp: new Date().toISOString(),\n * });\n *\n * await realtime.unsubscribe(subId);\n * ```\n */\nexport class InMemoryRealtimeAdapter implements IRealtimeService {\n private readonly subscriptions = new Map<string, Subscription>();\n private readonly channelIndex = new Map<string, Set<string>>();\n private counter = 0;\n private readonly maxSubscriptions: number;\n\n constructor(options: InMemoryRealtimeAdapterOptions = {}) {\n this.maxSubscriptions = options.maxSubscriptions ?? 0;\n }\n\n async publish(event: RealtimeEventPayload): Promise<void> {\n // Deliver to all channel subscriptions that match filters\n for (const sub of this.subscriptions.values()) {\n if (this.matchesSubscription(event, sub)) {\n try {\n await sub.handler(event);\n } catch {\n // Swallow handler errors to avoid breaking the publish loop\n }\n }\n }\n }\n\n async subscribe(\n channel: string,\n handler: RealtimeEventHandler,\n options?: RealtimeSubscriptionOptions,\n ): Promise<string> {\n if (this.maxSubscriptions > 0 && this.subscriptions.size >= this.maxSubscriptions) {\n throw new Error(\n `Maximum subscription limit reached (${this.maxSubscriptions}). ` +\n 'Unsubscribe from existing channels before adding new subscriptions.',\n );\n }\n\n const id = `sub-${++this.counter}`;\n const sub: Subscription = { id, channel, handler, options };\n this.subscriptions.set(id, sub);\n\n // Maintain channel index for efficient lookups\n if (!this.channelIndex.has(channel)) {\n this.channelIndex.set(channel, new Set());\n }\n this.channelIndex.get(channel)!.add(id);\n\n return id;\n }\n\n async unsubscribe(subscriptionId: string): Promise<void> {\n const sub = this.subscriptions.get(subscriptionId);\n if (!sub) return;\n\n this.subscriptions.delete(subscriptionId);\n\n // Clean up channel index\n const channelSubs = this.channelIndex.get(sub.channel);\n if (channelSubs) {\n channelSubs.delete(subscriptionId);\n if (channelSubs.size === 0) {\n this.channelIndex.delete(sub.channel);\n }\n }\n }\n\n /**\n * Get the number of active subscriptions.\n */\n getSubscriptionCount(): number {\n return this.subscriptions.size;\n }\n\n /**\n * Get all active channel names.\n */\n getChannels(): string[] {\n return Array.from(this.channelIndex.keys());\n }\n\n /**\n * Check if an event matches a subscription's filters.\n */\n private matchesSubscription(event: RealtimeEventPayload, sub: Subscription): boolean {\n const opts = sub.options;\n if (!opts) return true;\n\n // Filter by object name\n if (opts.object && event.object !== opts.object) {\n return false;\n }\n\n // Filter by event types\n if (opts.eventTypes && opts.eventTypes.length > 0) {\n if (!opts.eventTypes.includes(event.type)) {\n return false;\n }\n }\n\n return true;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { SysPresence } from './objects/index.js';\nimport { InMemoryRealtimeAdapter } from './in-memory-realtime-adapter.js';\nimport type { InMemoryRealtimeAdapterOptions } from './in-memory-realtime-adapter.js';\n\n/**\n * Configuration options for the RealtimeServicePlugin.\n */\nexport interface RealtimeServicePluginOptions {\n /** Realtime adapter type (default: 'memory') */\n adapter?: 'memory';\n /** Options for the in-memory adapter */\n memory?: InMemoryRealtimeAdapterOptions;\n}\n\n/**\n * RealtimeServicePlugin — Production IRealtimeService implementation.\n *\n * Registers a realtime pub/sub service with the kernel during the init phase.\n * Currently supports in-memory pub/sub for single-process environments.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { RealtimeServicePlugin } from '@objectstack/service-realtime';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new RealtimeServicePlugin());\n * await kernel.bootstrap();\n *\n * const realtime = kernel.getService('realtime');\n * await realtime.subscribe('records', (event) => {\n * console.log(event.type, event.payload);\n * });\n * ```\n */\nexport class RealtimeServicePlugin implements Plugin {\n name = 'com.objectstack.service.realtime';\n version = '1.0.0';\n type = 'standard';\n dependencies = ['com.objectstack.engine.objectql'];\n\n private readonly options: RealtimeServicePluginOptions;\n\n constructor(options: RealtimeServicePluginOptions = {}) {\n this.options = { adapter: 'memory', ...options };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n const realtime = new InMemoryRealtimeAdapter(this.options.memory);\n ctx.registerService('realtime', realtime);\n\n // Register realtime system objects via the manifest service.\n ctx.getService<{ register(m: any): void }>('manifest').register({\n id: 'com.objectstack.service.realtime',\n name: 'Realtime Service',\n version: '1.0.0',\n type: 'plugin',\n scope: 'system',\n namespace: 'sys',\n objects: [SysPresence],\n });\n\n // ADR-0029 D8 — contribute sys_presence translations on kernel:ready.\n if (typeof (ctx as any).hook === 'function') {\n (ctx as any).hook('kernel:ready', async () => {\n try {\n const i18n = ctx.getService<any>('i18n');\n if (i18n && typeof i18n.loadTranslations === 'function') {\n const { RealtimeTranslations } = await import('./translations/index.js');\n for (const [locale, data] of Object.entries(RealtimeTranslations)) {\n i18n.loadTranslations(locale, data as Record<string, unknown>);\n }\n }\n } catch { /* i18n optional */ }\n });\n }\n\n ctx.logger.info('RealtimeServicePlugin: registered in-memory realtime adapter');\n }\n}\n"],"mappings":"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","names":["RealtimeTranslations"]}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,380 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/translations/en.objects.generated.ts
|
|
12
|
+
var enObjects;
|
|
13
|
+
var init_en_objects_generated = __esm({
|
|
14
|
+
"src/translations/en.objects.generated.ts"() {
|
|
15
|
+
"use strict";
|
|
16
|
+
enObjects = {
|
|
17
|
+
sys_presence: {
|
|
18
|
+
label: "Presence",
|
|
19
|
+
pluralLabel: "Presences",
|
|
20
|
+
description: "Real-time user presence and activity tracking",
|
|
21
|
+
fields: {
|
|
22
|
+
id: {
|
|
23
|
+
label: "Presence ID"
|
|
24
|
+
},
|
|
25
|
+
created_at: {
|
|
26
|
+
label: "Created At"
|
|
27
|
+
},
|
|
28
|
+
updated_at: {
|
|
29
|
+
label: "Updated At"
|
|
30
|
+
},
|
|
31
|
+
user_id: {
|
|
32
|
+
label: "User"
|
|
33
|
+
},
|
|
34
|
+
session_id: {
|
|
35
|
+
label: "Session"
|
|
36
|
+
},
|
|
37
|
+
status: {
|
|
38
|
+
label: "Status",
|
|
39
|
+
options: {
|
|
40
|
+
online: "Online",
|
|
41
|
+
away: "Away",
|
|
42
|
+
busy: "Busy",
|
|
43
|
+
offline: "Offline"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
last_seen: {
|
|
47
|
+
label: "Last Seen"
|
|
48
|
+
},
|
|
49
|
+
current_location: {
|
|
50
|
+
label: "Current Location"
|
|
51
|
+
},
|
|
52
|
+
device: {
|
|
53
|
+
label: "Device",
|
|
54
|
+
options: {
|
|
55
|
+
desktop: "Desktop",
|
|
56
|
+
mobile: "Mobile",
|
|
57
|
+
tablet: "Tablet",
|
|
58
|
+
other: "Other"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
custom_status: {
|
|
62
|
+
label: "Custom Status"
|
|
63
|
+
},
|
|
64
|
+
metadata: {
|
|
65
|
+
label: "Metadata",
|
|
66
|
+
help: "Arbitrary JSON metadata associated with the presence state (matches PresenceStateSchema.metadata)."
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// src/translations/zh-CN.objects.generated.ts
|
|
75
|
+
var zhCNObjects;
|
|
76
|
+
var init_zh_CN_objects_generated = __esm({
|
|
77
|
+
"src/translations/zh-CN.objects.generated.ts"() {
|
|
78
|
+
"use strict";
|
|
79
|
+
zhCNObjects = {
|
|
80
|
+
sys_presence: {
|
|
81
|
+
label: "\u5728\u7EBF\u72B6\u6001",
|
|
82
|
+
pluralLabel: "\u5728\u7EBF\u72B6\u6001",
|
|
83
|
+
description: "\u5B9E\u65F6\u7528\u6237\u5728\u7EBF\u4E0E\u6D3B\u52A8\u8DDF\u8E2A",
|
|
84
|
+
fields: {
|
|
85
|
+
id: {
|
|
86
|
+
label: "\u5728\u7EBF\u72B6\u6001 ID"
|
|
87
|
+
},
|
|
88
|
+
created_at: {
|
|
89
|
+
label: "\u521B\u5EFA\u65F6\u95F4"
|
|
90
|
+
},
|
|
91
|
+
updated_at: {
|
|
92
|
+
label: "\u66F4\u65B0\u65F6\u95F4"
|
|
93
|
+
},
|
|
94
|
+
user_id: {
|
|
95
|
+
label: "\u7528\u6237"
|
|
96
|
+
},
|
|
97
|
+
session_id: {
|
|
98
|
+
label: "\u4F1A\u8BDD"
|
|
99
|
+
},
|
|
100
|
+
status: {
|
|
101
|
+
label: "\u72B6\u6001",
|
|
102
|
+
options: {
|
|
103
|
+
online: "\u5728\u7EBF",
|
|
104
|
+
away: "\u79BB\u5F00",
|
|
105
|
+
busy: "\u5FD9\u788C",
|
|
106
|
+
offline: "\u79BB\u7EBF"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
last_seen: {
|
|
110
|
+
label: "\u6700\u8FD1\u5728\u7EBF\u65F6\u95F4"
|
|
111
|
+
},
|
|
112
|
+
current_location: {
|
|
113
|
+
label: "\u5F53\u524D\u4F4D\u7F6E"
|
|
114
|
+
},
|
|
115
|
+
device: {
|
|
116
|
+
label: "\u8BBE\u5907",
|
|
117
|
+
options: {
|
|
118
|
+
desktop: "\u684C\u9762\u7AEF",
|
|
119
|
+
mobile: "\u79FB\u52A8\u7AEF",
|
|
120
|
+
tablet: "\u5E73\u677F\u7AEF",
|
|
121
|
+
other: "\u5176\u4ED6"
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
custom_status: {
|
|
125
|
+
label: "\u81EA\u5B9A\u4E49\u72B6\u6001"
|
|
126
|
+
},
|
|
127
|
+
metadata: {
|
|
128
|
+
label: "\u5143\u6570\u636E",
|
|
129
|
+
help: "\u4E0E\u5728\u7EBF\u72B6\u6001\u5173\u8054\u7684\u4EFB\u610F JSON \u5143\u6570\u636E\uFF08\u5BF9\u5E94 PresenceStateSchema.metadata\uFF09\u3002"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// src/translations/ja-JP.objects.generated.ts
|
|
138
|
+
var jaJPObjects;
|
|
139
|
+
var init_ja_JP_objects_generated = __esm({
|
|
140
|
+
"src/translations/ja-JP.objects.generated.ts"() {
|
|
141
|
+
"use strict";
|
|
142
|
+
jaJPObjects = {
|
|
143
|
+
sys_presence: {
|
|
144
|
+
label: "\u5728\u5E2D\u72B6\u6CC1",
|
|
145
|
+
pluralLabel: "\u5728\u5E2D\u72B6\u6CC1",
|
|
146
|
+
description: "\u30EA\u30A2\u30EB\u30BF\u30A4\u30E0\u306E\u30E6\u30FC\u30B6\u30FC\u5728\u5E2D\u72B6\u6CC1\u3068\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\u8FFD\u8DE1",
|
|
147
|
+
fields: {
|
|
148
|
+
id: {
|
|
149
|
+
label: "\u5728\u5E2D ID"
|
|
150
|
+
},
|
|
151
|
+
created_at: {
|
|
152
|
+
label: "\u4F5C\u6210\u65E5\u6642"
|
|
153
|
+
},
|
|
154
|
+
updated_at: {
|
|
155
|
+
label: "\u66F4\u65B0\u65E5\u6642"
|
|
156
|
+
},
|
|
157
|
+
user_id: {
|
|
158
|
+
label: "\u30E6\u30FC\u30B6\u30FC"
|
|
159
|
+
},
|
|
160
|
+
session_id: {
|
|
161
|
+
label: "\u30BB\u30C3\u30B7\u30E7\u30F3"
|
|
162
|
+
},
|
|
163
|
+
status: {
|
|
164
|
+
label: "\u30B9\u30C6\u30FC\u30BF\u30B9",
|
|
165
|
+
options: {
|
|
166
|
+
online: "\u30AA\u30F3\u30E9\u30A4\u30F3",
|
|
167
|
+
away: "\u96E2\u5E2D\u4E2D",
|
|
168
|
+
busy: "\u53D6\u308A\u8FBC\u307F\u4E2D",
|
|
169
|
+
offline: "\u30AA\u30D5\u30E9\u30A4\u30F3"
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
last_seen: {
|
|
173
|
+
label: "\u6700\u7D42\u78BA\u8A8D\u65E5\u6642"
|
|
174
|
+
},
|
|
175
|
+
current_location: {
|
|
176
|
+
label: "\u73FE\u5728\u5730"
|
|
177
|
+
},
|
|
178
|
+
device: {
|
|
179
|
+
label: "\u30C7\u30D0\u30A4\u30B9",
|
|
180
|
+
options: {
|
|
181
|
+
desktop: "\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7",
|
|
182
|
+
mobile: "\u30E2\u30D0\u30A4\u30EB",
|
|
183
|
+
tablet: "\u30BF\u30D6\u30EC\u30C3\u30C8",
|
|
184
|
+
other: "\u305D\u306E\u4ED6"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
custom_status: {
|
|
188
|
+
label: "\u30AB\u30B9\u30BF\u30E0\u30B9\u30C6\u30FC\u30BF\u30B9"
|
|
189
|
+
},
|
|
190
|
+
metadata: {
|
|
191
|
+
label: "\u30E1\u30BF\u30C7\u30FC\u30BF",
|
|
192
|
+
help: "\u5728\u5E2D\u72B6\u614B\u306B\u95A2\u9023\u4ED8\u3051\u3089\u308C\u305F\u4EFB\u610F\u306E JSON \u30E1\u30BF\u30C7\u30FC\u30BF\uFF08PresenceStateSchema.metadata \u306B\u5BFE\u5FDC\uFF09\u3002"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// src/translations/es-ES.objects.generated.ts
|
|
201
|
+
var esESObjects;
|
|
202
|
+
var init_es_ES_objects_generated = __esm({
|
|
203
|
+
"src/translations/es-ES.objects.generated.ts"() {
|
|
204
|
+
"use strict";
|
|
205
|
+
esESObjects = {
|
|
206
|
+
sys_presence: {
|
|
207
|
+
label: "Presencia",
|
|
208
|
+
pluralLabel: "Presencias",
|
|
209
|
+
description: "Seguimiento en tiempo real de presencia y actividad de usuarios",
|
|
210
|
+
fields: {
|
|
211
|
+
id: {
|
|
212
|
+
label: "ID de presencia"
|
|
213
|
+
},
|
|
214
|
+
created_at: {
|
|
215
|
+
label: "Creado el"
|
|
216
|
+
},
|
|
217
|
+
updated_at: {
|
|
218
|
+
label: "Actualizado el"
|
|
219
|
+
},
|
|
220
|
+
user_id: {
|
|
221
|
+
label: "Usuario"
|
|
222
|
+
},
|
|
223
|
+
session_id: {
|
|
224
|
+
label: "Sesi\xF3n"
|
|
225
|
+
},
|
|
226
|
+
status: {
|
|
227
|
+
label: "Estado",
|
|
228
|
+
options: {
|
|
229
|
+
online: "En l\xEDnea",
|
|
230
|
+
away: "Ausente",
|
|
231
|
+
busy: "Ocupado",
|
|
232
|
+
offline: "Desconectado"
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
last_seen: {
|
|
236
|
+
label: "Visto por \xFAltima vez"
|
|
237
|
+
},
|
|
238
|
+
current_location: {
|
|
239
|
+
label: "Ubicaci\xF3n actual"
|
|
240
|
+
},
|
|
241
|
+
device: {
|
|
242
|
+
label: "Dispositivo",
|
|
243
|
+
options: {
|
|
244
|
+
desktop: "Escritorio",
|
|
245
|
+
mobile: "M\xF3vil",
|
|
246
|
+
tablet: "Tableta",
|
|
247
|
+
other: "Otro"
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
custom_status: {
|
|
251
|
+
label: "Estado personalizado"
|
|
252
|
+
},
|
|
253
|
+
metadata: {
|
|
254
|
+
label: "Metadatos",
|
|
255
|
+
help: "Metadatos JSON arbitrarios asociados al estado de presencia (coincide con PresenceStateSchema.metadata)."
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// src/translations/index.ts
|
|
264
|
+
var translations_exports = {};
|
|
265
|
+
__export(translations_exports, {
|
|
266
|
+
RealtimeTranslations: () => RealtimeTranslations
|
|
267
|
+
});
|
|
268
|
+
var RealtimeTranslations;
|
|
269
|
+
var init_translations = __esm({
|
|
270
|
+
"src/translations/index.ts"() {
|
|
271
|
+
"use strict";
|
|
272
|
+
init_en_objects_generated();
|
|
273
|
+
init_zh_CN_objects_generated();
|
|
274
|
+
init_ja_JP_objects_generated();
|
|
275
|
+
init_es_ES_objects_generated();
|
|
276
|
+
RealtimeTranslations = {
|
|
277
|
+
en: { objects: enObjects },
|
|
278
|
+
"zh-CN": { objects: zhCNObjects },
|
|
279
|
+
"ja-JP": { objects: jaJPObjects },
|
|
280
|
+
"es-ES": { objects: esESObjects }
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// src/objects/sys-presence.object.ts
|
|
286
|
+
import { ObjectSchema, Field } from "@objectstack/spec/data";
|
|
287
|
+
var SysPresence = ObjectSchema.create({
|
|
288
|
+
name: "sys_presence",
|
|
289
|
+
label: "Presence",
|
|
290
|
+
pluralLabel: "Presences",
|
|
291
|
+
icon: "wifi",
|
|
292
|
+
isSystem: true,
|
|
293
|
+
managedBy: "append-only",
|
|
294
|
+
description: "Real-time user presence and activity tracking",
|
|
295
|
+
titleFormat: "{user_id} ({status})",
|
|
296
|
+
compactLayout: ["user_id", "status", "last_seen"],
|
|
297
|
+
fields: {
|
|
298
|
+
id: Field.text({
|
|
299
|
+
label: "Presence ID",
|
|
300
|
+
required: true,
|
|
301
|
+
readonly: true
|
|
302
|
+
}),
|
|
303
|
+
created_at: Field.datetime({
|
|
304
|
+
label: "Created At",
|
|
305
|
+
defaultValue: "NOW()",
|
|
306
|
+
readonly: true
|
|
307
|
+
}),
|
|
308
|
+
updated_at: Field.datetime({
|
|
309
|
+
label: "Updated At",
|
|
310
|
+
defaultValue: "NOW()",
|
|
311
|
+
readonly: true
|
|
312
|
+
}),
|
|
313
|
+
user_id: Field.lookup("sys_user", {
|
|
314
|
+
label: "User",
|
|
315
|
+
required: true,
|
|
316
|
+
searchable: true
|
|
317
|
+
}),
|
|
318
|
+
session_id: Field.lookup("sys_session", {
|
|
319
|
+
label: "Session",
|
|
320
|
+
required: true
|
|
321
|
+
}),
|
|
322
|
+
status: Field.select({
|
|
323
|
+
label: "Status",
|
|
324
|
+
required: true,
|
|
325
|
+
defaultValue: "online",
|
|
326
|
+
options: [
|
|
327
|
+
{ value: "online", label: "Online" },
|
|
328
|
+
{ value: "away", label: "Away" },
|
|
329
|
+
{ value: "busy", label: "Busy" },
|
|
330
|
+
{ value: "offline", label: "Offline" }
|
|
331
|
+
]
|
|
332
|
+
}),
|
|
333
|
+
last_seen: Field.datetime({
|
|
334
|
+
label: "Last Seen",
|
|
335
|
+
required: true,
|
|
336
|
+
defaultValue: "NOW()"
|
|
337
|
+
}),
|
|
338
|
+
current_location: Field.text({
|
|
339
|
+
label: "Current Location",
|
|
340
|
+
required: false,
|
|
341
|
+
maxLength: 500
|
|
342
|
+
}),
|
|
343
|
+
device: Field.select({
|
|
344
|
+
label: "Device",
|
|
345
|
+
required: false,
|
|
346
|
+
options: [
|
|
347
|
+
{ value: "desktop", label: "Desktop" },
|
|
348
|
+
{ value: "mobile", label: "Mobile" },
|
|
349
|
+
{ value: "tablet", label: "Tablet" },
|
|
350
|
+
{ value: "other", label: "Other" }
|
|
351
|
+
]
|
|
352
|
+
}),
|
|
353
|
+
custom_status: Field.text({
|
|
354
|
+
label: "Custom Status",
|
|
355
|
+
required: false,
|
|
356
|
+
maxLength: 255
|
|
357
|
+
}),
|
|
358
|
+
metadata: Field.json({
|
|
359
|
+
label: "Metadata",
|
|
360
|
+
required: false,
|
|
361
|
+
description: "Arbitrary JSON metadata associated with the presence state (matches PresenceStateSchema.metadata)."
|
|
362
|
+
})
|
|
363
|
+
},
|
|
364
|
+
indexes: [
|
|
365
|
+
{ fields: ["user_id"], unique: false },
|
|
366
|
+
{ fields: ["session_id"], unique: true },
|
|
367
|
+
{ fields: ["status"], unique: false }
|
|
368
|
+
],
|
|
369
|
+
enable: {
|
|
370
|
+
trackHistory: false,
|
|
371
|
+
searchable: false,
|
|
372
|
+
apiEnabled: true,
|
|
373
|
+
apiMethods: ["get", "list", "create", "update", "delete"],
|
|
374
|
+
trash: false,
|
|
375
|
+
mru: false
|
|
376
|
+
}
|
|
377
|
+
});
|
|
3
378
|
|
|
4
379
|
// src/in-memory-realtime-adapter.ts
|
|
5
380
|
var InMemoryRealtimeAdapter = class {
|
|
@@ -97,6 +472,20 @@ var RealtimeServicePlugin = class {
|
|
|
97
472
|
namespace: "sys",
|
|
98
473
|
objects: [SysPresence]
|
|
99
474
|
});
|
|
475
|
+
if (typeof ctx.hook === "function") {
|
|
476
|
+
ctx.hook("kernel:ready", async () => {
|
|
477
|
+
try {
|
|
478
|
+
const i18n = ctx.getService("i18n");
|
|
479
|
+
if (i18n && typeof i18n.loadTranslations === "function") {
|
|
480
|
+
const { RealtimeTranslations: RealtimeTranslations2 } = await Promise.resolve().then(() => (init_translations(), translations_exports));
|
|
481
|
+
for (const [locale, data] of Object.entries(RealtimeTranslations2)) {
|
|
482
|
+
i18n.loadTranslations(locale, data);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
}
|
|
100
489
|
ctx.logger.info("RealtimeServicePlugin: registered in-memory realtime adapter");
|
|
101
490
|
}
|
|
102
491
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/realtime-service-plugin.ts","../src/in-memory-realtime-adapter.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { SysPresence } from '@objectstack/platform-objects/audit';\nimport { InMemoryRealtimeAdapter } from './in-memory-realtime-adapter.js';\nimport type { InMemoryRealtimeAdapterOptions } from './in-memory-realtime-adapter.js';\n\n/**\n * Configuration options for the RealtimeServicePlugin.\n */\nexport interface RealtimeServicePluginOptions {\n /** Realtime adapter type (default: 'memory') */\n adapter?: 'memory';\n /** Options for the in-memory adapter */\n memory?: InMemoryRealtimeAdapterOptions;\n}\n\n/**\n * RealtimeServicePlugin — Production IRealtimeService implementation.\n *\n * Registers a realtime pub/sub service with the kernel during the init phase.\n * Currently supports in-memory pub/sub for single-process environments.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { RealtimeServicePlugin } from '@objectstack/service-realtime';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new RealtimeServicePlugin());\n * await kernel.bootstrap();\n *\n * const realtime = kernel.getService('realtime');\n * await realtime.subscribe('records', (event) => {\n * console.log(event.type, event.payload);\n * });\n * ```\n */\nexport class RealtimeServicePlugin implements Plugin {\n name = 'com.objectstack.service.realtime';\n version = '1.0.0';\n type = 'standard';\n dependencies = ['com.objectstack.engine.objectql'];\n\n private readonly options: RealtimeServicePluginOptions;\n\n constructor(options: RealtimeServicePluginOptions = {}) {\n this.options = { adapter: 'memory', ...options };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n const realtime = new InMemoryRealtimeAdapter(this.options.memory);\n ctx.registerService('realtime', realtime);\n\n // Register realtime system objects via the manifest service.\n ctx.getService<{ register(m: any): void }>('manifest').register({\n id: 'com.objectstack.service.realtime',\n name: 'Realtime Service',\n version: '1.0.0',\n type: 'plugin',\n scope: 'system',\n namespace: 'sys',\n objects: [SysPresence],\n });\n\n ctx.logger.info('RealtimeServicePlugin: registered in-memory realtime adapter');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n IRealtimeService,\n RealtimeEventPayload,\n RealtimeEventHandler,\n RealtimeSubscriptionOptions,\n} from '@objectstack/spec/contracts';\n\n/**\n * Internal subscription entry.\n */\ninterface Subscription {\n id: string;\n channel: string;\n handler: RealtimeEventHandler;\n options?: RealtimeSubscriptionOptions;\n}\n\n/**\n * Configuration options for InMemoryRealtimeAdapter.\n */\nexport interface InMemoryRealtimeAdapterOptions {\n /** Maximum number of subscriptions allowed (0 = unlimited) */\n maxSubscriptions?: number;\n}\n\n/**\n * In-memory pub/sub adapter implementing IRealtimeService.\n *\n * Uses a Map-backed subscription store with channel-based routing.\n * Supports event type and object filtering via subscription options.\n *\n * Suitable for single-process environments, development, and testing.\n * For production multi-instance deployments, use a Redis-backed adapter.\n *\n * @example\n * ```ts\n * const realtime = new InMemoryRealtimeAdapter();\n *\n * const subId = await realtime.subscribe('records', (event) => {\n * console.log('Received:', event.type, event.payload);\n * }, { object: 'account', eventTypes: ['record.created'] });\n *\n * await realtime.publish({\n * type: 'record.created',\n * object: 'account',\n * payload: { id: 'acc-1', name: 'Acme' },\n * timestamp: new Date().toISOString(),\n * });\n *\n * await realtime.unsubscribe(subId);\n * ```\n */\nexport class InMemoryRealtimeAdapter implements IRealtimeService {\n private readonly subscriptions = new Map<string, Subscription>();\n private readonly channelIndex = new Map<string, Set<string>>();\n private counter = 0;\n private readonly maxSubscriptions: number;\n\n constructor(options: InMemoryRealtimeAdapterOptions = {}) {\n this.maxSubscriptions = options.maxSubscriptions ?? 0;\n }\n\n async publish(event: RealtimeEventPayload): Promise<void> {\n // Deliver to all channel subscriptions that match filters\n for (const sub of this.subscriptions.values()) {\n if (this.matchesSubscription(event, sub)) {\n try {\n await sub.handler(event);\n } catch {\n // Swallow handler errors to avoid breaking the publish loop\n }\n }\n }\n }\n\n async subscribe(\n channel: string,\n handler: RealtimeEventHandler,\n options?: RealtimeSubscriptionOptions,\n ): Promise<string> {\n if (this.maxSubscriptions > 0 && this.subscriptions.size >= this.maxSubscriptions) {\n throw new Error(\n `Maximum subscription limit reached (${this.maxSubscriptions}). ` +\n 'Unsubscribe from existing channels before adding new subscriptions.',\n );\n }\n\n const id = `sub-${++this.counter}`;\n const sub: Subscription = { id, channel, handler, options };\n this.subscriptions.set(id, sub);\n\n // Maintain channel index for efficient lookups\n if (!this.channelIndex.has(channel)) {\n this.channelIndex.set(channel, new Set());\n }\n this.channelIndex.get(channel)!.add(id);\n\n return id;\n }\n\n async unsubscribe(subscriptionId: string): Promise<void> {\n const sub = this.subscriptions.get(subscriptionId);\n if (!sub) return;\n\n this.subscriptions.delete(subscriptionId);\n\n // Clean up channel index\n const channelSubs = this.channelIndex.get(sub.channel);\n if (channelSubs) {\n channelSubs.delete(subscriptionId);\n if (channelSubs.size === 0) {\n this.channelIndex.delete(sub.channel);\n }\n }\n }\n\n /**\n * Get the number of active subscriptions.\n */\n getSubscriptionCount(): number {\n return this.subscriptions.size;\n }\n\n /**\n * Get all active channel names.\n */\n getChannels(): string[] {\n return Array.from(this.channelIndex.keys());\n }\n\n /**\n * Check if an event matches a subscription's filters.\n */\n private matchesSubscription(event: RealtimeEventPayload, sub: Subscription): boolean {\n const opts = sub.options;\n if (!opts) return true;\n\n // Filter by object name\n if (opts.object && event.object !== opts.object) {\n return false;\n }\n\n // Filter by event types\n if (opts.eventTypes && opts.eventTypes.length > 0) {\n if (!opts.eventTypes.includes(event.type)) {\n return false;\n }\n }\n\n return true;\n }\n}\n"],"mappings":";AAGA,SAAS,mBAAmB;;;ACmDrB,IAAM,0BAAN,MAA0D;AAAA,EAM/D,YAAY,UAA0C,CAAC,GAAG;AAL1D,SAAiB,gBAAgB,oBAAI,IAA0B;AAC/D,SAAiB,eAAe,oBAAI,IAAyB;AAC7D,SAAQ,UAAU;AAIhB,SAAK,mBAAmB,QAAQ,oBAAoB;AAAA,EACtD;AAAA,EAEA,MAAM,QAAQ,OAA4C;AAExD,eAAW,OAAO,KAAK,cAAc,OAAO,GAAG;AAC7C,UAAI,KAAK,oBAAoB,OAAO,GAAG,GAAG;AACxC,YAAI;AACF,gBAAM,IAAI,QAAQ,KAAK;AAAA,QACzB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,UACJ,SACA,SACA,SACiB;AACjB,QAAI,KAAK,mBAAmB,KAAK,KAAK,cAAc,QAAQ,KAAK,kBAAkB;AACjF,YAAM,IAAI;AAAA,QACR,uCAAuC,KAAK,gBAAgB;AAAA,MAE9D;AAAA,IACF;AAEA,UAAM,KAAK,OAAO,EAAE,KAAK,OAAO;AAChC,UAAM,MAAoB,EAAE,IAAI,SAAS,SAAS,QAAQ;AAC1D,SAAK,cAAc,IAAI,IAAI,GAAG;AAG9B,QAAI,CAAC,KAAK,aAAa,IAAI,OAAO,GAAG;AACnC,WAAK,aAAa,IAAI,SAAS,oBAAI,IAAI,CAAC;AAAA,IAC1C;AACA,SAAK,aAAa,IAAI,OAAO,EAAG,IAAI,EAAE;AAEtC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,gBAAuC;AACvD,UAAM,MAAM,KAAK,cAAc,IAAI,cAAc;AACjD,QAAI,CAAC,IAAK;AAEV,SAAK,cAAc,OAAO,cAAc;AAGxC,UAAM,cAAc,KAAK,aAAa,IAAI,IAAI,OAAO;AACrD,QAAI,aAAa;AACf,kBAAY,OAAO,cAAc;AACjC,UAAI,YAAY,SAAS,GAAG;AAC1B,aAAK,aAAa,OAAO,IAAI,OAAO;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,uBAA+B;AAC7B,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,cAAwB;AACtB,WAAO,MAAM,KAAK,KAAK,aAAa,KAAK,CAAC;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoB,OAA6B,KAA4B;AACnF,UAAM,OAAO,IAAI;AACjB,QAAI,CAAC,KAAM,QAAO;AAGlB,QAAI,KAAK,UAAU,MAAM,WAAW,KAAK,QAAQ;AAC/C,aAAO;AAAA,IACT;AAGA,QAAI,KAAK,cAAc,KAAK,WAAW,SAAS,GAAG;AACjD,UAAI,CAAC,KAAK,WAAW,SAAS,MAAM,IAAI,GAAG;AACzC,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;ADnHO,IAAM,wBAAN,MAA8C;AAAA,EAQnD,YAAY,UAAwC,CAAC,GAAG;AAPxD,gBAAO;AACP,mBAAU;AACV,gBAAO;AACP,wBAAe,CAAC,iCAAiC;AAK/C,SAAK,UAAU,EAAE,SAAS,UAAU,GAAG,QAAQ;AAAA,EACjD;AAAA,EAEA,MAAM,KAAK,KAAmC;AAC5C,UAAM,WAAW,IAAI,wBAAwB,KAAK,QAAQ,MAAM;AAChE,QAAI,gBAAgB,YAAY,QAAQ;AAGxC,QAAI,WAAuC,UAAU,EAAE,SAAS;AAAA,MAC9D,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,OAAO;AAAA,MACP,WAAW;AAAA,MACX,SAAS,CAAC,WAAW;AAAA,IACvB,CAAC;AAED,QAAI,OAAO,KAAK,8DAA8D;AAAA,EAChF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/translations/en.objects.generated.ts","../src/translations/zh-CN.objects.generated.ts","../src/translations/ja-JP.objects.generated.ts","../src/translations/es-ES.objects.generated.ts","../src/translations/index.ts","../src/objects/sys-presence.object.ts","../src/in-memory-realtime-adapter.ts","../src/realtime-service-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'en'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const enObjects: NonNullable<TranslationData['objects']> = {\n sys_presence: {\n label: \"Presence\",\n pluralLabel: \"Presences\",\n description: \"Real-time user presence and activity tracking\",\n fields: {\n id: {\n label: \"Presence ID\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n },\n user_id: {\n label: \"User\"\n },\n session_id: {\n label: \"Session\"\n },\n status: {\n label: \"Status\",\n options: {\n online: \"Online\",\n away: \"Away\",\n busy: \"Busy\",\n offline: \"Offline\"\n }\n },\n last_seen: {\n label: \"Last Seen\"\n },\n current_location: {\n label: \"Current Location\"\n },\n device: {\n label: \"Device\",\n options: {\n desktop: \"Desktop\",\n mobile: \"Mobile\",\n tablet: \"Tablet\",\n other: \"Other\"\n }\n },\n custom_status: {\n label: \"Custom Status\"\n },\n metadata: {\n label: \"Metadata\",\n help: \"Arbitrary JSON metadata associated with the presence state (matches PresenceStateSchema.metadata).\"\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'zh-CN'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const zhCNObjects: NonNullable<TranslationData['objects']> = {\n sys_presence: {\n label: \"在线状态\",\n pluralLabel: \"在线状态\",\n description: \"实时用户在线与活动跟踪\",\n fields: {\n id: {\n label: \"在线状态 ID\"\n },\n created_at: {\n label: \"创建时间\"\n },\n updated_at: {\n label: \"更新时间\"\n },\n user_id: {\n label: \"用户\"\n },\n session_id: {\n label: \"会话\"\n },\n status: {\n label: \"状态\",\n options: {\n online: \"在线\",\n away: \"离开\",\n busy: \"忙碌\",\n offline: \"离线\"\n }\n },\n last_seen: {\n label: \"最近在线时间\"\n },\n current_location: {\n label: \"当前位置\"\n },\n device: {\n label: \"设备\",\n options: {\n desktop: \"桌面端\",\n mobile: \"移动端\",\n tablet: \"平板端\",\n other: \"其他\"\n }\n },\n custom_status: {\n label: \"自定义状态\"\n },\n metadata: {\n label: \"元数据\",\n help: \"与在线状态关联的任意 JSON 元数据(对应 PresenceStateSchema.metadata)。\"\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'ja-JP'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const jaJPObjects: NonNullable<TranslationData['objects']> = {\n sys_presence: {\n label: \"在席状況\",\n pluralLabel: \"在席状況\",\n description: \"リアルタイムのユーザー在席状況とアクティビティ追跡\",\n fields: {\n id: {\n label: \"在席 ID\"\n },\n created_at: {\n label: \"作成日時\"\n },\n updated_at: {\n label: \"更新日時\"\n },\n user_id: {\n label: \"ユーザー\"\n },\n session_id: {\n label: \"セッション\"\n },\n status: {\n label: \"ステータス\",\n options: {\n online: \"オンライン\",\n away: \"離席中\",\n busy: \"取り込み中\",\n offline: \"オフライン\"\n }\n },\n last_seen: {\n label: \"最終確認日時\"\n },\n current_location: {\n label: \"現在地\"\n },\n device: {\n label: \"デバイス\",\n options: {\n desktop: \"デスクトップ\",\n mobile: \"モバイル\",\n tablet: \"タブレット\",\n other: \"その他\"\n }\n },\n custom_status: {\n label: \"カスタムステータス\"\n },\n metadata: {\n label: \"メタデータ\",\n help: \"在席状態に関連付けられた任意の JSON メタデータ(PresenceStateSchema.metadata に対応)。\"\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'es-ES'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const esESObjects: NonNullable<TranslationData['objects']> = {\n sys_presence: {\n label: \"Presencia\",\n pluralLabel: \"Presencias\",\n description: \"Seguimiento en tiempo real de presencia y actividad de usuarios\",\n fields: {\n id: {\n label: \"ID de presencia\"\n },\n created_at: {\n label: \"Creado el\"\n },\n updated_at: {\n label: \"Actualizado el\"\n },\n user_id: {\n label: \"Usuario\"\n },\n session_id: {\n label: \"Sesión\"\n },\n status: {\n label: \"Estado\",\n options: {\n online: \"En línea\",\n away: \"Ausente\",\n busy: \"Ocupado\",\n offline: \"Desconectado\"\n }\n },\n last_seen: {\n label: \"Visto por última vez\"\n },\n current_location: {\n label: \"Ubicación actual\"\n },\n device: {\n label: \"Dispositivo\",\n options: {\n desktop: \"Escritorio\",\n mobile: \"Móvil\",\n tablet: \"Tableta\",\n other: \"Otro\"\n }\n },\n custom_status: {\n label: \"Estado personalizado\"\n },\n metadata: {\n label: \"Metadatos\",\n help: \"Metadatos JSON arbitrarios asociados al estado de presencia (coincide con PresenceStateSchema.metadata).\"\n }\n }\n }\n};\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * RealtimeTranslations — i18n bundle owned by this service plugin\n * (ADR-0029 D8).\n *\n * Object label/field/view/action translations for sys_presence, the object\n * this plugin owns. Loaded at runtime via the plugin's `kernel:ready` hook\n * (`i18n.loadTranslations`). Regenerate with `os i18n extract` against\n * `scripts/i18n-extract.config.ts`.\n */\n\nimport type { TranslationBundle } from '@objectstack/spec/system';\nimport { enObjects } from './en.objects.generated.js';\nimport { zhCNObjects } from './zh-CN.objects.generated.js';\nimport { jaJPObjects } from './ja-JP.objects.generated.js';\nimport { esESObjects } from './es-ES.objects.generated.js';\n\nexport const RealtimeTranslations: TranslationBundle = {\n en: { objects: enObjects },\n 'zh-CN': { objects: zhCNObjects },\n 'ja-JP': { objects: jaJPObjects },\n 'es-ES': { objects: esESObjects },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_presence — System Presence Object\n *\n * Tracks real-time user presence and activity across the platform.\n * Fields align with the PresenceStateSchema protocol definition\n * from `@objectstack/spec/api` (websocket.zod.ts).\n *\n * Owned by `service-realtime` as the canonical Presence domain object.\n *\n * @namespace sys\n * @see PresenceStateSchema in packages/spec/src/api/websocket.zod.ts\n */\nexport const SysPresence = ObjectSchema.create({\n name: 'sys_presence',\n label: 'Presence',\n pluralLabel: 'Presences',\n icon: 'wifi',\n isSystem: true,\n managedBy: 'append-only',\n description: 'Real-time user presence and activity tracking',\n titleFormat: '{user_id} ({status})',\n compactLayout: ['user_id', 'status', 'last_seen'],\n\n fields: {\n id: Field.text({\n label: 'Presence ID',\n required: true,\n readonly: true,\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n defaultValue: 'NOW()',\n readonly: true,\n }),\n\n updated_at: Field.datetime({\n label: 'Updated At',\n defaultValue: 'NOW()',\n readonly: true,\n }),\n\n user_id: Field.lookup('sys_user', {\n label: 'User',\n required: true,\n searchable: true,\n }),\n\n session_id: Field.lookup('sys_session', {\n label: 'Session',\n required: true,\n }),\n\n status: Field.select({\n label: 'Status',\n required: true,\n defaultValue: 'online',\n options: [\n { value: 'online', label: 'Online' },\n { value: 'away', label: 'Away' },\n { value: 'busy', label: 'Busy' },\n { value: 'offline', label: 'Offline' },\n ],\n }),\n\n last_seen: Field.datetime({\n label: 'Last Seen',\n required: true,\n defaultValue: 'NOW()',\n }),\n\n current_location: Field.text({\n label: 'Current Location',\n required: false,\n maxLength: 500,\n }),\n\n device: Field.select({\n label: 'Device',\n required: false,\n options: [\n { value: 'desktop', label: 'Desktop' },\n { value: 'mobile', label: 'Mobile' },\n { value: 'tablet', label: 'Tablet' },\n { value: 'other', label: 'Other' },\n ],\n }),\n\n custom_status: Field.text({\n label: 'Custom Status',\n required: false,\n maxLength: 255,\n }),\n\n metadata: Field.json({\n label: 'Metadata',\n required: false,\n description: 'Arbitrary JSON metadata associated with the presence state (matches PresenceStateSchema.metadata).',\n }),\n },\n\n indexes: [\n { fields: ['user_id'], unique: false },\n { fields: ['session_id'], unique: true },\n { fields: ['status'], unique: false },\n ],\n\n enable: {\n trackHistory: false,\n searchable: false,\n apiEnabled: true,\n apiMethods: ['get', 'list', 'create', 'update', 'delete'],\n trash: false,\n mru: false,\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n IRealtimeService,\n RealtimeEventPayload,\n RealtimeEventHandler,\n RealtimeSubscriptionOptions,\n} from '@objectstack/spec/contracts';\n\n/**\n * Internal subscription entry.\n */\ninterface Subscription {\n id: string;\n channel: string;\n handler: RealtimeEventHandler;\n options?: RealtimeSubscriptionOptions;\n}\n\n/**\n * Configuration options for InMemoryRealtimeAdapter.\n */\nexport interface InMemoryRealtimeAdapterOptions {\n /** Maximum number of subscriptions allowed (0 = unlimited) */\n maxSubscriptions?: number;\n}\n\n/**\n * In-memory pub/sub adapter implementing IRealtimeService.\n *\n * Uses a Map-backed subscription store with channel-based routing.\n * Supports event type and object filtering via subscription options.\n *\n * Suitable for single-process environments, development, and testing.\n * For production multi-instance deployments, use a Redis-backed adapter.\n *\n * @example\n * ```ts\n * const realtime = new InMemoryRealtimeAdapter();\n *\n * const subId = await realtime.subscribe('records', (event) => {\n * console.log('Received:', event.type, event.payload);\n * }, { object: 'account', eventTypes: ['record.created'] });\n *\n * await realtime.publish({\n * type: 'record.created',\n * object: 'account',\n * payload: { id: 'acc-1', name: 'Acme' },\n * timestamp: new Date().toISOString(),\n * });\n *\n * await realtime.unsubscribe(subId);\n * ```\n */\nexport class InMemoryRealtimeAdapter implements IRealtimeService {\n private readonly subscriptions = new Map<string, Subscription>();\n private readonly channelIndex = new Map<string, Set<string>>();\n private counter = 0;\n private readonly maxSubscriptions: number;\n\n constructor(options: InMemoryRealtimeAdapterOptions = {}) {\n this.maxSubscriptions = options.maxSubscriptions ?? 0;\n }\n\n async publish(event: RealtimeEventPayload): Promise<void> {\n // Deliver to all channel subscriptions that match filters\n for (const sub of this.subscriptions.values()) {\n if (this.matchesSubscription(event, sub)) {\n try {\n await sub.handler(event);\n } catch {\n // Swallow handler errors to avoid breaking the publish loop\n }\n }\n }\n }\n\n async subscribe(\n channel: string,\n handler: RealtimeEventHandler,\n options?: RealtimeSubscriptionOptions,\n ): Promise<string> {\n if (this.maxSubscriptions > 0 && this.subscriptions.size >= this.maxSubscriptions) {\n throw new Error(\n `Maximum subscription limit reached (${this.maxSubscriptions}). ` +\n 'Unsubscribe from existing channels before adding new subscriptions.',\n );\n }\n\n const id = `sub-${++this.counter}`;\n const sub: Subscription = { id, channel, handler, options };\n this.subscriptions.set(id, sub);\n\n // Maintain channel index for efficient lookups\n if (!this.channelIndex.has(channel)) {\n this.channelIndex.set(channel, new Set());\n }\n this.channelIndex.get(channel)!.add(id);\n\n return id;\n }\n\n async unsubscribe(subscriptionId: string): Promise<void> {\n const sub = this.subscriptions.get(subscriptionId);\n if (!sub) return;\n\n this.subscriptions.delete(subscriptionId);\n\n // Clean up channel index\n const channelSubs = this.channelIndex.get(sub.channel);\n if (channelSubs) {\n channelSubs.delete(subscriptionId);\n if (channelSubs.size === 0) {\n this.channelIndex.delete(sub.channel);\n }\n }\n }\n\n /**\n * Get the number of active subscriptions.\n */\n getSubscriptionCount(): number {\n return this.subscriptions.size;\n }\n\n /**\n * Get all active channel names.\n */\n getChannels(): string[] {\n return Array.from(this.channelIndex.keys());\n }\n\n /**\n * Check if an event matches a subscription's filters.\n */\n private matchesSubscription(event: RealtimeEventPayload, sub: Subscription): boolean {\n const opts = sub.options;\n if (!opts) return true;\n\n // Filter by object name\n if (opts.object && event.object !== opts.object) {\n return false;\n }\n\n // Filter by event types\n if (opts.eventTypes && opts.eventTypes.length > 0) {\n if (!opts.eventTypes.includes(event.type)) {\n return false;\n }\n }\n\n return true;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { SysPresence } from './objects/index.js';\nimport { InMemoryRealtimeAdapter } from './in-memory-realtime-adapter.js';\nimport type { InMemoryRealtimeAdapterOptions } from './in-memory-realtime-adapter.js';\n\n/**\n * Configuration options for the RealtimeServicePlugin.\n */\nexport interface RealtimeServicePluginOptions {\n /** Realtime adapter type (default: 'memory') */\n adapter?: 'memory';\n /** Options for the in-memory adapter */\n memory?: InMemoryRealtimeAdapterOptions;\n}\n\n/**\n * RealtimeServicePlugin — Production IRealtimeService implementation.\n *\n * Registers a realtime pub/sub service with the kernel during the init phase.\n * Currently supports in-memory pub/sub for single-process environments.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { RealtimeServicePlugin } from '@objectstack/service-realtime';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new RealtimeServicePlugin());\n * await kernel.bootstrap();\n *\n * const realtime = kernel.getService('realtime');\n * await realtime.subscribe('records', (event) => {\n * console.log(event.type, event.payload);\n * });\n * ```\n */\nexport class RealtimeServicePlugin implements Plugin {\n name = 'com.objectstack.service.realtime';\n version = '1.0.0';\n type = 'standard';\n dependencies = ['com.objectstack.engine.objectql'];\n\n private readonly options: RealtimeServicePluginOptions;\n\n constructor(options: RealtimeServicePluginOptions = {}) {\n this.options = { adapter: 'memory', ...options };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n const realtime = new InMemoryRealtimeAdapter(this.options.memory);\n ctx.registerService('realtime', realtime);\n\n // Register realtime system objects via the manifest service.\n ctx.getService<{ register(m: any): void }>('manifest').register({\n id: 'com.objectstack.service.realtime',\n name: 'Realtime Service',\n version: '1.0.0',\n type: 'plugin',\n scope: 'system',\n namespace: 'sys',\n objects: [SysPresence],\n });\n\n // ADR-0029 D8 — contribute sys_presence translations on kernel:ready.\n if (typeof (ctx as any).hook === 'function') {\n (ctx as any).hook('kernel:ready', async () => {\n try {\n const i18n = ctx.getService<any>('i18n');\n if (i18n && typeof i18n.loadTranslations === 'function') {\n const { RealtimeTranslations } = await import('./translations/index.js');\n for (const [locale, data] of Object.entries(RealtimeTranslations)) {\n i18n.loadTranslations(locale, data as Record<string, unknown>);\n }\n }\n } catch { /* i18n optional */ }\n });\n }\n\n ctx.logger.info('RealtimeServicePlugin: registered in-memory realtime adapter');\n }\n}\n"],"mappings":";;;;;;;;;;;AAAA,IAUa;AAVb;AAAA;AAAA;AAUO,IAAM,YAAqD;AAAA,MAChE,cAAc;AAAA,QACZ,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,SAAS;AAAA,YACP,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,QAAQ;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,kBAAkB;AAAA,YAChB,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,OAAO;AAAA,YACT;AAAA,UACF;AAAA,UACA,eAAe;AAAA,YACb,OAAO;AAAA,UACT;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;AChEA,IAUa;AAVb;AAAA;AAAA;AAUO,IAAM,cAAuD;AAAA,MAClE,cAAc;AAAA,QACZ,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,SAAS;AAAA,YACP,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,QAAQ;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,kBAAkB;AAAA,YAChB,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,OAAO;AAAA,YACT;AAAA,UACF;AAAA,UACA,eAAe;AAAA,YACb,OAAO;AAAA,UACT;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;AChEA,IAUa;AAVb;AAAA;AAAA;AAUO,IAAM,cAAuD;AAAA,MAClE,cAAc;AAAA,QACZ,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,SAAS;AAAA,YACP,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,QAAQ;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,kBAAkB;AAAA,YAChB,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,OAAO;AAAA,YACT;AAAA,UACF;AAAA,UACA,eAAe;AAAA,YACb,OAAO;AAAA,UACT;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;AChEA,IAUa;AAVb;AAAA;AAAA;AAUO,IAAM,cAAuD;AAAA,MAClE,cAAc;AAAA,QACZ,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,UACN,IAAI;AAAA,YACF,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,SAAS;AAAA,YACP,OAAO;AAAA,UACT;AAAA,UACA,YAAY;AAAA,YACV,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,QAAQ;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,UACF;AAAA,UACA,WAAW;AAAA,YACT,OAAO;AAAA,UACT;AAAA,UACA,kBAAkB;AAAA,YAChB,OAAO;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,OAAO;AAAA,YACT;AAAA,UACF;AAAA,UACA,eAAe;AAAA,YACb,OAAO;AAAA,UACT;AAAA,UACA,UAAU;AAAA,YACR,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;AChEA;AAAA;AAAA;AAAA;AAAA,IAkBa;AAlBb;AAAA;AAAA;AAaA;AACA;AACA;AACA;AAEO,IAAM,uBAA0C;AAAA,MACrD,IAAI,EAAE,SAAS,UAAU;AAAA,MACzB,SAAS,EAAE,SAAS,YAAY;AAAA,MAChC,SAAS,EAAE,SAAS,YAAY;AAAA,MAChC,SAAS,EAAE,SAAS,YAAY;AAAA,IAClC;AAAA;AAAA;;;ACrBA,SAAS,cAAc,aAAa;AAc7B,IAAM,cAAc,aAAa,OAAO;AAAA,EAC7C,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAM;AAAA,EACN,UAAU;AAAA,EACV,WAAW;AAAA,EACX,aAAa;AAAA,EACb,aAAa;AAAA,EACb,eAAe,CAAC,WAAW,UAAU,WAAW;AAAA,EAEhD,QAAQ;AAAA,IACN,IAAI,MAAM,KAAK;AAAA,MACb,OAAO;AAAA,MACP,UAAU;AAAA,MACV,UAAU;AAAA,IACZ,CAAC;AAAA,IAED,YAAY,MAAM,SAAS;AAAA,MACzB,OAAO;AAAA,MACP,cAAc;AAAA,MACd,UAAU;AAAA,IACZ,CAAC;AAAA,IAED,YAAY,MAAM,SAAS;AAAA,MACzB,OAAO;AAAA,MACP,cAAc;AAAA,MACd,UAAU;AAAA,IACZ,CAAC;AAAA,IAED,SAAS,MAAM,OAAO,YAAY;AAAA,MAChC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,YAAY;AAAA,IACd,CAAC;AAAA,IAED,YAAY,MAAM,OAAO,eAAe;AAAA,MACtC,OAAO;AAAA,MACP,UAAU;AAAA,IACZ,CAAC;AAAA,IAED,QAAQ,MAAM,OAAO;AAAA,MACnB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,cAAc;AAAA,MACd,SAAS;AAAA,QACP,EAAE,OAAO,UAAU,OAAO,SAAS;AAAA,QACnC,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,QAC/B,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,QAC/B,EAAE,OAAO,WAAW,OAAO,UAAU;AAAA,MACvC;AAAA,IACF,CAAC;AAAA,IAED,WAAW,MAAM,SAAS;AAAA,MACxB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,cAAc;AAAA,IAChB,CAAC;AAAA,IAED,kBAAkB,MAAM,KAAK;AAAA,MAC3B,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,IACb,CAAC;AAAA,IAED,QAAQ,MAAM,OAAO;AAAA,MACnB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,SAAS;AAAA,QACP,EAAE,OAAO,WAAW,OAAO,UAAU;AAAA,QACrC,EAAE,OAAO,UAAU,OAAO,SAAS;AAAA,QACnC,EAAE,OAAO,UAAU,OAAO,SAAS;AAAA,QACnC,EAAE,OAAO,SAAS,OAAO,QAAQ;AAAA,MACnC;AAAA,IACF,CAAC;AAAA,IAED,eAAe,MAAM,KAAK;AAAA,MACxB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,WAAW;AAAA,IACb,CAAC;AAAA,IAED,UAAU,MAAM,KAAK;AAAA,MACnB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAAA,EAEA,SAAS;AAAA,IACP,EAAE,QAAQ,CAAC,SAAS,GAAG,QAAQ,MAAM;AAAA,IACrC,EAAE,QAAQ,CAAC,YAAY,GAAG,QAAQ,KAAK;AAAA,IACvC,EAAE,QAAQ,CAAC,QAAQ,GAAG,QAAQ,MAAM;AAAA,EACtC;AAAA,EAEA,QAAQ;AAAA,IACN,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY,CAAC,OAAO,QAAQ,UAAU,UAAU,QAAQ;AAAA,IACxD,OAAO;AAAA,IACP,KAAK;AAAA,EACP;AACF,CAAC;;;ACjEM,IAAM,0BAAN,MAA0D;AAAA,EAM/D,YAAY,UAA0C,CAAC,GAAG;AAL1D,SAAiB,gBAAgB,oBAAI,IAA0B;AAC/D,SAAiB,eAAe,oBAAI,IAAyB;AAC7D,SAAQ,UAAU;AAIhB,SAAK,mBAAmB,QAAQ,oBAAoB;AAAA,EACtD;AAAA,EAEA,MAAM,QAAQ,OAA4C;AAExD,eAAW,OAAO,KAAK,cAAc,OAAO,GAAG;AAC7C,UAAI,KAAK,oBAAoB,OAAO,GAAG,GAAG;AACxC,YAAI;AACF,gBAAM,IAAI,QAAQ,KAAK;AAAA,QACzB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,UACJ,SACA,SACA,SACiB;AACjB,QAAI,KAAK,mBAAmB,KAAK,KAAK,cAAc,QAAQ,KAAK,kBAAkB;AACjF,YAAM,IAAI;AAAA,QACR,uCAAuC,KAAK,gBAAgB;AAAA,MAE9D;AAAA,IACF;AAEA,UAAM,KAAK,OAAO,EAAE,KAAK,OAAO;AAChC,UAAM,MAAoB,EAAE,IAAI,SAAS,SAAS,QAAQ;AAC1D,SAAK,cAAc,IAAI,IAAI,GAAG;AAG9B,QAAI,CAAC,KAAK,aAAa,IAAI,OAAO,GAAG;AACnC,WAAK,aAAa,IAAI,SAAS,oBAAI,IAAI,CAAC;AAAA,IAC1C;AACA,SAAK,aAAa,IAAI,OAAO,EAAG,IAAI,EAAE;AAEtC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,gBAAuC;AACvD,UAAM,MAAM,KAAK,cAAc,IAAI,cAAc;AACjD,QAAI,CAAC,IAAK;AAEV,SAAK,cAAc,OAAO,cAAc;AAGxC,UAAM,cAAc,KAAK,aAAa,IAAI,IAAI,OAAO;AACrD,QAAI,aAAa;AACf,kBAAY,OAAO,cAAc;AACjC,UAAI,YAAY,SAAS,GAAG;AAC1B,aAAK,aAAa,OAAO,IAAI,OAAO;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,uBAA+B;AAC7B,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,cAAwB;AACtB,WAAO,MAAM,KAAK,KAAK,aAAa,KAAK,CAAC;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoB,OAA6B,KAA4B;AACnF,UAAM,OAAO,IAAI;AACjB,QAAI,CAAC,KAAM,QAAO;AAGlB,QAAI,KAAK,UAAU,MAAM,WAAW,KAAK,QAAQ;AAC/C,aAAO;AAAA,IACT;AAGA,QAAI,KAAK,cAAc,KAAK,WAAW,SAAS,GAAG;AACjD,UAAI,CAAC,KAAK,WAAW,SAAS,MAAM,IAAI,GAAG;AACzC,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;ACnHO,IAAM,wBAAN,MAA8C;AAAA,EAQnD,YAAY,UAAwC,CAAC,GAAG;AAPxD,gBAAO;AACP,mBAAU;AACV,gBAAO;AACP,wBAAe,CAAC,iCAAiC;AAK/C,SAAK,UAAU,EAAE,SAAS,UAAU,GAAG,QAAQ;AAAA,EACjD;AAAA,EAEA,MAAM,KAAK,KAAmC;AAC5C,UAAM,WAAW,IAAI,wBAAwB,KAAK,QAAQ,MAAM;AAChE,QAAI,gBAAgB,YAAY,QAAQ;AAGxC,QAAI,WAAuC,UAAU,EAAE,SAAS;AAAA,MAC9D,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,OAAO;AAAA,MACP,WAAW;AAAA,MACX,SAAS,CAAC,WAAW;AAAA,IACvB,CAAC;AAGD,QAAI,OAAQ,IAAY,SAAS,YAAY;AAC3C,MAAC,IAAY,KAAK,gBAAgB,YAAY;AAC5C,YAAI;AACF,gBAAM,OAAO,IAAI,WAAgB,MAAM;AACvC,cAAI,QAAQ,OAAO,KAAK,qBAAqB,YAAY;AACvD,kBAAM,EAAE,sBAAAA,sBAAqB,IAAI,MAAM;AACvC,uBAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQA,qBAAoB,GAAG;AACjE,mBAAK,iBAAiB,QAAQ,IAA+B;AAAA,YAC/D;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAAsB;AAAA,MAChC,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,KAAK,8DAA8D;AAAA,EAChF;AACF;","names":["RealtimeTranslations"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/service-realtime",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.4.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Realtime Service for ObjectStack — implements IRealtimeService with WebSocket and in-memory pub/sub",
|
|
6
6
|
"type": "module",
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@objectstack/core": "7.
|
|
18
|
-
"@objectstack/platform-objects": "7.
|
|
19
|
-
"@objectstack/spec": "7.
|
|
17
|
+
"@objectstack/core": "7.4.0",
|
|
18
|
+
"@objectstack/platform-objects": "7.4.0",
|
|
19
|
+
"@objectstack/spec": "7.4.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "^25.9.1",
|