@objectstack/plugin-dev 2.0.6

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.js ADDED
@@ -0,0 +1,735 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ DevPlugin: () => DevPlugin
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/dev-plugin.ts
38
+ var CORE_SERVICE_NAMES = [
39
+ "metadata",
40
+ "data",
41
+ "auth",
42
+ "file-storage",
43
+ "search",
44
+ "cache",
45
+ "queue",
46
+ "automation",
47
+ "graphql",
48
+ "analytics",
49
+ "realtime",
50
+ "job",
51
+ "notification",
52
+ "ai",
53
+ "i18n",
54
+ "ui",
55
+ "workflow"
56
+ ];
57
+ var SECURITY_SERVICE_NAMES = [
58
+ "security.permissions",
59
+ "security.rls",
60
+ "security.fieldMasker"
61
+ ];
62
+ function createCacheStub() {
63
+ const store = /* @__PURE__ */ new Map();
64
+ let hits = 0;
65
+ let misses = 0;
66
+ return {
67
+ _dev: true,
68
+ _serviceName: "cache",
69
+ async get(key) {
70
+ const entry = store.get(key);
71
+ if (!entry || entry.expires && Date.now() > entry.expires) {
72
+ store.delete(key);
73
+ misses++;
74
+ return void 0;
75
+ }
76
+ hits++;
77
+ return entry.value;
78
+ },
79
+ async set(key, value, ttl) {
80
+ store.set(key, { value, expires: ttl ? Date.now() + ttl * 1e3 : void 0 });
81
+ },
82
+ async delete(key) {
83
+ return store.delete(key);
84
+ },
85
+ async has(key) {
86
+ return store.has(key);
87
+ },
88
+ async clear() {
89
+ store.clear();
90
+ },
91
+ async stats() {
92
+ return { hits, misses, keyCount: store.size };
93
+ }
94
+ };
95
+ }
96
+ function createQueueStub() {
97
+ const handlers = /* @__PURE__ */ new Map();
98
+ let msgId = 0;
99
+ return {
100
+ _dev: true,
101
+ _serviceName: "queue",
102
+ async publish(queue, data) {
103
+ const id = `dev-msg-${++msgId}`;
104
+ const fns = handlers.get(queue) ?? [];
105
+ for (const fn of fns) fn({ id, data, attempts: 1, timestamp: Date.now() });
106
+ return id;
107
+ },
108
+ async subscribe(queue, handler) {
109
+ handlers.set(queue, [...handlers.get(queue) ?? [], handler]);
110
+ },
111
+ async unsubscribe(queue) {
112
+ handlers.delete(queue);
113
+ },
114
+ async getQueueSize() {
115
+ return 0;
116
+ },
117
+ async purge(queue) {
118
+ handlers.delete(queue);
119
+ }
120
+ };
121
+ }
122
+ function createJobStub() {
123
+ const jobs = /* @__PURE__ */ new Map();
124
+ return {
125
+ _dev: true,
126
+ _serviceName: "job",
127
+ async schedule(name, schedule, handler) {
128
+ jobs.set(name, { schedule, handler });
129
+ },
130
+ async cancel(name) {
131
+ jobs.delete(name);
132
+ },
133
+ async trigger(name, data) {
134
+ const job = jobs.get(name);
135
+ if (job?.handler) await job.handler({ jobId: name, data });
136
+ },
137
+ async getExecutions() {
138
+ return [];
139
+ },
140
+ async listJobs() {
141
+ return [...jobs.keys()];
142
+ }
143
+ };
144
+ }
145
+ function createStorageStub() {
146
+ const files = /* @__PURE__ */ new Map();
147
+ return {
148
+ _dev: true,
149
+ _serviceName: "file-storage",
150
+ async upload(key, data, options) {
151
+ files.set(key, { data: Buffer.from(data), meta: { contentType: options?.contentType, metadata: options?.metadata } });
152
+ },
153
+ async download(key) {
154
+ return files.get(key)?.data ?? Buffer.alloc(0);
155
+ },
156
+ async delete(key) {
157
+ files.delete(key);
158
+ },
159
+ async exists(key) {
160
+ return files.has(key);
161
+ },
162
+ async getInfo(key) {
163
+ const f = files.get(key);
164
+ return { key, size: f?.data?.length ?? 0, contentType: f?.meta?.contentType, lastModified: /* @__PURE__ */ new Date(), metadata: f?.meta?.metadata };
165
+ },
166
+ async list(prefix) {
167
+ return [...files.entries()].filter(([k]) => k.startsWith(prefix)).map(([key, f]) => ({ key, size: f.data.length, contentType: f.meta?.contentType, lastModified: /* @__PURE__ */ new Date() }));
168
+ }
169
+ };
170
+ }
171
+ function createSearchStub() {
172
+ const indexes = /* @__PURE__ */ new Map();
173
+ return {
174
+ _dev: true,
175
+ _serviceName: "search",
176
+ async index(object, id, document) {
177
+ if (!indexes.has(object)) indexes.set(object, /* @__PURE__ */ new Map());
178
+ indexes.get(object).set(id, document);
179
+ },
180
+ async remove(object, id) {
181
+ indexes.get(object)?.delete(id);
182
+ },
183
+ async search(object, query) {
184
+ const docs = indexes.get(object) ?? /* @__PURE__ */ new Map();
185
+ const q = query.toLowerCase();
186
+ const hits = [...docs.entries()].filter(([, doc]) => JSON.stringify(doc).toLowerCase().includes(q)).map(([id, doc]) => ({ id, score: 1, document: doc }));
187
+ return { hits, totalHits: hits.length, processingTimeMs: 0 };
188
+ },
189
+ async bulkIndex(object, documents) {
190
+ if (!indexes.has(object)) indexes.set(object, /* @__PURE__ */ new Map());
191
+ for (const d of documents) {
192
+ indexes.get(object).set(d.id, d.document);
193
+ }
194
+ },
195
+ async deleteIndex(object) {
196
+ indexes.delete(object);
197
+ }
198
+ };
199
+ }
200
+ function createAutomationStub() {
201
+ const flows = /* @__PURE__ */ new Map();
202
+ return {
203
+ _dev: true,
204
+ _serviceName: "automation",
205
+ async execute(_flowName) {
206
+ return { success: true, output: void 0, durationMs: 0 };
207
+ },
208
+ async listFlows() {
209
+ return [...flows.keys()];
210
+ },
211
+ registerFlow(name, definition) {
212
+ flows.set(name, definition);
213
+ },
214
+ unregisterFlow(name) {
215
+ flows.delete(name);
216
+ }
217
+ };
218
+ }
219
+ function createGraphQLStub() {
220
+ return {
221
+ _dev: true,
222
+ _serviceName: "graphql",
223
+ async execute() {
224
+ return { data: null, errors: [{ message: "GraphQL not available in dev stub mode" }] };
225
+ },
226
+ getSchema() {
227
+ return "type Query { _dev: Boolean }";
228
+ }
229
+ };
230
+ }
231
+ function createAnalyticsStub() {
232
+ return {
233
+ _dev: true,
234
+ _serviceName: "analytics",
235
+ async query() {
236
+ return { rows: [], fields: [] };
237
+ },
238
+ async getMeta() {
239
+ return [];
240
+ },
241
+ async generateSql() {
242
+ return { sql: "", params: [] };
243
+ }
244
+ };
245
+ }
246
+ function createRealtimeStub() {
247
+ const subs = /* @__PURE__ */ new Map();
248
+ let subId = 0;
249
+ return {
250
+ _dev: true,
251
+ _serviceName: "realtime",
252
+ async publish(event) {
253
+ for (const fn of subs.values()) fn(event);
254
+ },
255
+ async subscribe(_channel, handler) {
256
+ const id = `dev-sub-${++subId}`;
257
+ subs.set(id, handler);
258
+ return id;
259
+ },
260
+ async unsubscribe(subscriptionId) {
261
+ subs.delete(subscriptionId);
262
+ }
263
+ };
264
+ }
265
+ function createNotificationStub() {
266
+ const sent = [];
267
+ return {
268
+ _dev: true,
269
+ _serviceName: "notification",
270
+ async send(message) {
271
+ sent.push(message);
272
+ return { success: true, messageId: `dev-notif-${sent.length}` };
273
+ },
274
+ async sendBatch(messages) {
275
+ return messages.map((m) => {
276
+ sent.push(m);
277
+ return { success: true, messageId: `dev-notif-${sent.length}` };
278
+ });
279
+ },
280
+ getChannels() {
281
+ return ["email", "in-app"];
282
+ }
283
+ };
284
+ }
285
+ function createAIStub() {
286
+ return {
287
+ _dev: true,
288
+ _serviceName: "ai",
289
+ async chat() {
290
+ return { content: "[dev-stub] AI not available in development mode", model: "dev-stub" };
291
+ },
292
+ async complete() {
293
+ return { content: "[dev-stub] AI not available in development mode", model: "dev-stub" };
294
+ },
295
+ async embed() {
296
+ return [[0]];
297
+ },
298
+ async listModels() {
299
+ return ["dev-stub"];
300
+ }
301
+ };
302
+ }
303
+ function createI18nStub() {
304
+ const translations = /* @__PURE__ */ new Map();
305
+ let defaultLocale = "en";
306
+ return {
307
+ _dev: true,
308
+ _serviceName: "i18n",
309
+ t(key, locale, params) {
310
+ const t = translations.get(locale);
311
+ const val = t?.[key];
312
+ if (typeof val === "string") {
313
+ return params ? val.replace(/\{\{(\w+)\}\}/g, (_, k) => String(params[k] ?? `{{${k}}}`)) : val;
314
+ }
315
+ return key;
316
+ },
317
+ getTranslations(locale) {
318
+ return translations.get(locale) ?? {};
319
+ },
320
+ loadTranslations(locale, data) {
321
+ translations.set(locale, { ...translations.get(locale), ...data });
322
+ },
323
+ getLocales() {
324
+ return [...translations.keys()];
325
+ },
326
+ getDefaultLocale() {
327
+ return defaultLocale;
328
+ },
329
+ setDefaultLocale(locale) {
330
+ defaultLocale = locale;
331
+ }
332
+ };
333
+ }
334
+ function createUIStub() {
335
+ const views = /* @__PURE__ */ new Map();
336
+ const dashboards = /* @__PURE__ */ new Map();
337
+ return {
338
+ _dev: true,
339
+ _serviceName: "ui",
340
+ getView(name) {
341
+ return views.get(name);
342
+ },
343
+ listViews(object) {
344
+ const all = [...views.values()];
345
+ return object ? all.filter((v) => v.object === object) : all;
346
+ },
347
+ getDashboard(name) {
348
+ return dashboards.get(name);
349
+ },
350
+ listDashboards() {
351
+ return [...dashboards.values()];
352
+ },
353
+ registerView(name, definition) {
354
+ views.set(name, definition);
355
+ },
356
+ registerDashboard(name, definition) {
357
+ dashboards.set(name, definition);
358
+ }
359
+ };
360
+ }
361
+ function createWorkflowStub() {
362
+ const states = /* @__PURE__ */ new Map();
363
+ const key = (obj, id) => `${obj}:${id}`;
364
+ return {
365
+ _dev: true,
366
+ _serviceName: "workflow",
367
+ async transition(t) {
368
+ states.set(key(t.object, t.recordId), t.targetState);
369
+ return { success: true, currentState: t.targetState };
370
+ },
371
+ async getStatus(object, recordId) {
372
+ return { recordId, object, currentState: states.get(key(object, recordId)) ?? "draft", availableTransitions: [] };
373
+ },
374
+ async getHistory() {
375
+ return [];
376
+ }
377
+ };
378
+ }
379
+ function createMetadataStub() {
380
+ const store = /* @__PURE__ */ new Map();
381
+ return {
382
+ _dev: true,
383
+ _serviceName: "metadata",
384
+ register(type, definition) {
385
+ if (!store.has(type)) store.set(type, /* @__PURE__ */ new Map());
386
+ store.get(type).set(definition.name ?? "", definition);
387
+ },
388
+ get(type, name) {
389
+ return store.get(type)?.get(name);
390
+ },
391
+ list(type) {
392
+ return [...store.get(type)?.values() ?? []];
393
+ },
394
+ unregister(type, name) {
395
+ store.get(type)?.delete(name);
396
+ },
397
+ getObject(name) {
398
+ return store.get("object")?.get(name);
399
+ },
400
+ listObjects() {
401
+ return [...store.get("object")?.values() ?? []];
402
+ },
403
+ unregisterPackage() {
404
+ }
405
+ };
406
+ }
407
+ function createAuthStub() {
408
+ return {
409
+ _dev: true,
410
+ _serviceName: "auth",
411
+ async handleRequest() {
412
+ return new Response(JSON.stringify({ success: true }), { status: 200 });
413
+ },
414
+ async verify() {
415
+ return { success: true, user: { id: "dev-admin", email: "admin@dev.local", name: "Admin", roles: ["admin"] } };
416
+ },
417
+ async logout() {
418
+ },
419
+ async getCurrentUser() {
420
+ return { id: "dev-admin", email: "admin@dev.local", name: "Admin", roles: ["admin"] };
421
+ }
422
+ };
423
+ }
424
+ function createDataStub() {
425
+ return {
426
+ _dev: true,
427
+ _serviceName: "data",
428
+ async find() {
429
+ return [];
430
+ },
431
+ async findOne() {
432
+ return void 0;
433
+ },
434
+ async insert(_obj, params) {
435
+ return { id: `dev-${Date.now()}`, ...params?.data };
436
+ },
437
+ async update(_obj, _id, params) {
438
+ return params?.data ?? {};
439
+ },
440
+ async delete() {
441
+ return true;
442
+ },
443
+ async count() {
444
+ return 0;
445
+ },
446
+ async aggregate() {
447
+ return [];
448
+ }
449
+ };
450
+ }
451
+ function createSecurityPermissionsStub() {
452
+ return {
453
+ _dev: true,
454
+ _serviceName: "security.permissions",
455
+ resolvePermissionSets() {
456
+ return [];
457
+ },
458
+ checkObjectPermission() {
459
+ return true;
460
+ },
461
+ getFieldPermissions() {
462
+ return {};
463
+ }
464
+ };
465
+ }
466
+ function createSecurityRLSStub() {
467
+ return {
468
+ _dev: true,
469
+ _serviceName: "security.rls",
470
+ compileFilter() {
471
+ return null;
472
+ },
473
+ getApplicablePolicies() {
474
+ return [];
475
+ }
476
+ };
477
+ }
478
+ function createSecurityFieldMaskerStub() {
479
+ return {
480
+ _dev: true,
481
+ _serviceName: "security.fieldMasker",
482
+ maskResults(results) {
483
+ return results;
484
+ }
485
+ };
486
+ }
487
+ var DEV_STUB_FACTORIES = {
488
+ "cache": createCacheStub,
489
+ "queue": createQueueStub,
490
+ "job": createJobStub,
491
+ "file-storage": createStorageStub,
492
+ "search": createSearchStub,
493
+ "automation": createAutomationStub,
494
+ "graphql": createGraphQLStub,
495
+ "analytics": createAnalyticsStub,
496
+ "realtime": createRealtimeStub,
497
+ "notification": createNotificationStub,
498
+ "ai": createAIStub,
499
+ "i18n": createI18nStub,
500
+ "ui": createUIStub,
501
+ "workflow": createWorkflowStub,
502
+ "metadata": createMetadataStub,
503
+ "data": createDataStub,
504
+ "auth": createAuthStub,
505
+ // Security sub-services
506
+ "security.permissions": createSecurityPermissionsStub,
507
+ "security.rls": createSecurityRLSStub,
508
+ "security.fieldMasker": createSecurityFieldMaskerStub
509
+ };
510
+ var DevPlugin = class {
511
+ constructor(options = {}) {
512
+ this.name = "com.objectstack.plugin.dev";
513
+ this.type = "standard";
514
+ this.version = "1.0.0";
515
+ this.childPlugins = [];
516
+ this.options = {
517
+ port: 3e3,
518
+ seedAdminUser: true,
519
+ authSecret: "objectstack-dev-secret-DO-NOT-USE-IN-PRODUCTION!!",
520
+ verbose: true,
521
+ ...options,
522
+ authBaseUrl: options.authBaseUrl ?? `http://localhost:${options.port ?? 3e3}`
523
+ };
524
+ }
525
+ /**
526
+ * Init Phase
527
+ *
528
+ * Dynamically imports and instantiates all core plugins.
529
+ * Uses dynamic imports so that peer dependencies remain optional —
530
+ * if a package isn't installed the service is silently skipped.
531
+ */
532
+ async init(ctx) {
533
+ ctx.logger.info("\u{1F680} DevPlugin initializing \u2014 auto-configuring all services for development");
534
+ const enabled = (name) => this.options.services?.[name] !== false;
535
+ if (enabled("objectql")) {
536
+ try {
537
+ const { ObjectQLPlugin } = await import("@objectstack/objectql");
538
+ const qlPlugin = new ObjectQLPlugin();
539
+ this.childPlugins.push(qlPlugin);
540
+ ctx.logger.info(" \u2714 ObjectQL engine enabled (data + metadata)");
541
+ } catch {
542
+ ctx.logger.warn(" \u2718 @objectstack/objectql not installed \u2014 skipping data engine");
543
+ }
544
+ }
545
+ if (enabled("driver")) {
546
+ try {
547
+ const { DriverPlugin } = await import("@objectstack/runtime");
548
+ const { InMemoryDriver } = await import("@objectstack/driver-memory");
549
+ const driver = new InMemoryDriver();
550
+ const driverPlugin = new DriverPlugin(driver, "memory");
551
+ this.childPlugins.push(driverPlugin);
552
+ ctx.logger.info(" \u2714 InMemoryDriver enabled");
553
+ } catch {
554
+ ctx.logger.warn(" \u2718 @objectstack/runtime or @objectstack/driver-memory not installed \u2014 skipping driver");
555
+ }
556
+ }
557
+ if (this.options.stack) {
558
+ try {
559
+ const { AppPlugin } = await import("@objectstack/runtime");
560
+ const appPlugin = new AppPlugin(this.options.stack);
561
+ this.childPlugins.push(appPlugin);
562
+ ctx.logger.info(" \u2714 App metadata loaded from stack definition");
563
+ } catch {
564
+ ctx.logger.warn(" \u2718 @objectstack/runtime not installed \u2014 skipping app metadata");
565
+ }
566
+ }
567
+ if (enabled("auth")) {
568
+ try {
569
+ const { AuthPlugin } = await import("@objectstack/plugin-auth");
570
+ const authPlugin = new AuthPlugin({
571
+ secret: this.options.authSecret,
572
+ baseUrl: this.options.authBaseUrl
573
+ });
574
+ this.childPlugins.push(authPlugin);
575
+ ctx.logger.info(" \u2714 Auth plugin enabled (dev credentials)");
576
+ } catch {
577
+ ctx.logger.warn(" \u2718 @objectstack/plugin-auth not installed \u2014 skipping auth");
578
+ }
579
+ }
580
+ if (enabled("security")) {
581
+ try {
582
+ const { SecurityPlugin } = await import("@objectstack/plugin-security");
583
+ const securityPlugin = new SecurityPlugin();
584
+ this.childPlugins.push(securityPlugin);
585
+ ctx.logger.info(" \u2714 Security plugin enabled (RBAC, RLS, field masking)");
586
+ } catch {
587
+ ctx.logger.debug(" \u2139 @objectstack/plugin-security not installed \u2014 skipping security");
588
+ }
589
+ }
590
+ if (enabled("server")) {
591
+ try {
592
+ const { HonoServerPlugin } = await import("@objectstack/plugin-hono-server");
593
+ const serverPlugin = new HonoServerPlugin({
594
+ port: this.options.port
595
+ });
596
+ this.childPlugins.push(serverPlugin);
597
+ ctx.logger.info(` \u2714 Hono HTTP server enabled on port ${this.options.port}`);
598
+ } catch {
599
+ ctx.logger.warn(" \u2718 @objectstack/plugin-hono-server not installed \u2014 skipping HTTP server");
600
+ }
601
+ }
602
+ if (enabled("rest")) {
603
+ try {
604
+ const { createRestApiPlugin } = await import("@objectstack/rest");
605
+ const restPlugin = createRestApiPlugin();
606
+ this.childPlugins.push(restPlugin);
607
+ ctx.logger.info(" \u2714 REST API endpoints enabled (CRUD + metadata)");
608
+ } catch {
609
+ ctx.logger.debug(" \u2139 @objectstack/rest not installed \u2014 skipping REST endpoints");
610
+ }
611
+ }
612
+ if (enabled("dispatcher")) {
613
+ try {
614
+ const { createDispatcherPlugin } = await import("@objectstack/runtime");
615
+ const dispatcherPlugin = createDispatcherPlugin();
616
+ this.childPlugins.push(dispatcherPlugin);
617
+ ctx.logger.info(" \u2714 Dispatcher enabled (auth, GraphQL, analytics, packages, storage)");
618
+ } catch {
619
+ ctx.logger.debug(" \u2139 Dispatcher not available \u2014 skipping extended API routes");
620
+ }
621
+ }
622
+ if (this.options.extraPlugins) {
623
+ this.childPlugins.push(...this.options.extraPlugins);
624
+ }
625
+ for (const plugin of this.childPlugins) {
626
+ try {
627
+ await plugin.init(ctx);
628
+ } catch (err) {
629
+ ctx.logger.error(`Failed to init child plugin ${plugin.name}: ${err.message}`);
630
+ }
631
+ }
632
+ const stubNames = [];
633
+ for (const svc of CORE_SERVICE_NAMES) {
634
+ if (!enabled(svc)) continue;
635
+ try {
636
+ ctx.getService(svc);
637
+ } catch {
638
+ const factory = DEV_STUB_FACTORIES[svc];
639
+ ctx.registerService(svc, factory ? factory() : { _dev: true, _serviceName: svc });
640
+ stubNames.push(svc);
641
+ }
642
+ }
643
+ if (enabled("security")) {
644
+ for (const svc of SECURITY_SERVICE_NAMES) {
645
+ try {
646
+ ctx.getService(svc);
647
+ } catch {
648
+ const factory = DEV_STUB_FACTORIES[svc];
649
+ ctx.registerService(svc, factory ? factory() : { _dev: true, _serviceName: svc });
650
+ stubNames.push(svc);
651
+ }
652
+ }
653
+ }
654
+ if (stubNames.length > 0) {
655
+ ctx.logger.info(` \u2714 Contract-compliant dev stubs registered for: ${stubNames.join(", ")}`);
656
+ }
657
+ ctx.logger.info(`DevPlugin initialized ${this.childPlugins.length} plugin(s) + ${stubNames.length} dev stub(s)`);
658
+ }
659
+ /**
660
+ * Start Phase
661
+ *
662
+ * Starts all child plugins and optionally seeds the dev admin user.
663
+ */
664
+ async start(ctx) {
665
+ for (const plugin of this.childPlugins) {
666
+ if (plugin.start) {
667
+ try {
668
+ await plugin.start(ctx);
669
+ } catch (err) {
670
+ ctx.logger.error(`Failed to start child plugin ${plugin.name}: ${err.message}`);
671
+ }
672
+ }
673
+ }
674
+ if (this.options.seedAdminUser) {
675
+ await this.seedAdmin(ctx);
676
+ }
677
+ ctx.logger.info("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
678
+ ctx.logger.info("\u{1F7E2} ObjectStack Dev Server ready");
679
+ ctx.logger.info(` http://localhost:${this.options.port}`);
680
+ ctx.logger.info("");
681
+ ctx.logger.info(" API: /api/v1/data/:object");
682
+ ctx.logger.info(" Metadata: /api/v1/meta/:type/:name");
683
+ ctx.logger.info(" Discovery: /.well-known/objectstack");
684
+ ctx.logger.info("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
685
+ }
686
+ /**
687
+ * Destroy Phase
688
+ *
689
+ * Cleans up all child plugins in reverse order.
690
+ */
691
+ async destroy() {
692
+ for (const plugin of [...this.childPlugins].reverse()) {
693
+ if (plugin.destroy) {
694
+ try {
695
+ await plugin.destroy();
696
+ } catch {
697
+ }
698
+ }
699
+ }
700
+ }
701
+ /**
702
+ * Seed a default admin user for development.
703
+ */
704
+ async seedAdmin(ctx) {
705
+ try {
706
+ const dataEngine = ctx.getService("data");
707
+ if (!dataEngine) return;
708
+ const existing = await dataEngine.find("user", {
709
+ filter: { email: "admin@dev.local" },
710
+ limit: 1
711
+ }).catch(() => null);
712
+ if (existing?.length) {
713
+ ctx.logger.debug("Dev admin user already exists");
714
+ return;
715
+ }
716
+ await dataEngine.insert("user", {
717
+ data: {
718
+ name: "Admin",
719
+ email: "admin@dev.local",
720
+ username: "admin",
721
+ role: "admin"
722
+ }
723
+ }).catch(() => {
724
+ });
725
+ ctx.logger.info("\u{1F511} Dev admin user seeded: admin@dev.local");
726
+ } catch {
727
+ ctx.logger.debug("Could not seed admin user (data engine may not be ready)");
728
+ }
729
+ }
730
+ };
731
+ // Annotate the CommonJS export names for ESM import in node:
732
+ 0 && (module.exports = {
733
+ DevPlugin
734
+ });
735
+ //# sourceMappingURL=index.js.map