@nilejs/nile 0.0.1

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,1411 @@
1
+ // src/engine/create-action.ts
2
+ function createAction(config) {
3
+ return config;
4
+ }
5
+ function createActions(configs) {
6
+ return configs;
7
+ }
8
+
9
+ // src/engine/create-service.ts
10
+ function createService(config) {
11
+ return config;
12
+ }
13
+ function createServices(configs) {
14
+ return configs;
15
+ }
16
+
17
+ // src/logging/logger.ts
18
+ import {
19
+ appendFileSync,
20
+ existsSync,
21
+ mkdirSync,
22
+ readdirSync,
23
+ readFileSync
24
+ } from "fs";
25
+ import { join } from "path";
26
+ import { nanoid } from "nanoid";
27
+ import pino from "pino";
28
+ var getMode = () => {
29
+ if (!process.env.MODE) {
30
+ throw new Error("Missing MODE environment variable");
31
+ }
32
+ return process.env.MODE;
33
+ };
34
+ var logDir = join(process.cwd(), "logs");
35
+ if (!existsSync(logDir)) {
36
+ mkdirSync(logDir);
37
+ }
38
+ var MONTHLY_PATTERN = /^(\d{4})-(\d{2})$/;
39
+ var DAILY_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
40
+ var WEEKLY_PATTERN = /^(\d{4})-W(\d{2})$/;
41
+ function resolveLogPath(appName, config) {
42
+ const chunking = config?.chunking ?? "none";
43
+ if (chunking === "none") {
44
+ return join(logDir, `${appName}.log`);
45
+ }
46
+ const appDir = join(logDir, appName);
47
+ if (!existsSync(appDir)) {
48
+ mkdirSync(appDir, { recursive: true });
49
+ }
50
+ const now = /* @__PURE__ */ new Date();
51
+ const chunk = formatChunkName(now, chunking);
52
+ return join(appDir, `${chunk}.log`);
53
+ }
54
+ function formatChunkName(date, chunking) {
55
+ const year = date.getFullYear();
56
+ const month = String(date.getMonth() + 1).padStart(2, "0");
57
+ if (chunking === "monthly") {
58
+ return `${year}-${month}`;
59
+ }
60
+ if (chunking === "daily") {
61
+ const day = String(date.getDate()).padStart(2, "0");
62
+ return `${year}-${month}-${day}`;
63
+ }
64
+ const weekNum = getISOWeekNumber(date);
65
+ return `${year}-W${String(weekNum).padStart(2, "0")}`;
66
+ }
67
+ function getISOWeekNumber(date) {
68
+ const target = new Date(date.valueOf());
69
+ const dayNum = target.getDay() || 7;
70
+ target.setDate(target.getDate() + 4 - dayNum);
71
+ const yearStart = new Date(target.getFullYear(), 0, 1);
72
+ return Math.ceil(
73
+ ((target.getTime() - yearStart.getTime()) / 864e5 + 1) / 7
74
+ );
75
+ }
76
+ function createLoggerForApp(appName, config) {
77
+ const logFile = resolveLogPath(appName, config);
78
+ const transport = pino.transport({
79
+ targets: [
80
+ {
81
+ level: "info",
82
+ target: "pino/file",
83
+ options: {
84
+ destination: logFile,
85
+ mkdir: true
86
+ }
87
+ }
88
+ ]
89
+ });
90
+ return pino(
91
+ {
92
+ base: null,
93
+ timestamp: () => `,"time":"${(/* @__PURE__ */ new Date()).toISOString()}"`,
94
+ formatters: {
95
+ level(label) {
96
+ return { level: label };
97
+ }
98
+ }
99
+ },
100
+ transport
101
+ );
102
+ }
103
+ var createLog = (log, config) => {
104
+ if (!log.appName) {
105
+ throw new Error(`Missing appName in log: ${JSON.stringify(log)}`);
106
+ }
107
+ const level = log.level || "info";
108
+ const log_id = log.log_id || nanoid(6);
109
+ const logRecord = {
110
+ log_id,
111
+ appName: log.appName,
112
+ atFunction: log.atFunction,
113
+ message: log.message,
114
+ data: log.data ?? null,
115
+ level,
116
+ time: (/* @__PURE__ */ new Date()).toISOString()
117
+ };
118
+ const mode = getMode();
119
+ if (mode === "prod" || process.env.NODE_ENV === "test") {
120
+ const logFile = resolveLogPath(log.appName, config);
121
+ if (process.env.NODE_ENV === "test") {
122
+ appendFileSync(logFile, `${JSON.stringify(logRecord)}
123
+ `, "utf-8");
124
+ } else {
125
+ const appLogger = createLoggerForApp(log.appName, config);
126
+ appLogger[level](logRecord);
127
+ }
128
+ return log_id;
129
+ }
130
+ if (mode === "agentic") {
131
+ return JSON.stringify(logRecord);
132
+ }
133
+ console.log({
134
+ ...logRecord,
135
+ data: JSON.stringify(logRecord.data, null, 2)
136
+ });
137
+ return "dev-mode, see your dev console!";
138
+ };
139
+ var getLogs = (filters = {}, config) => {
140
+ const chunking = config?.chunking ?? "none";
141
+ const filesToRead = resolveLogFiles(filters, chunking);
142
+ const logs = readAndParseLogFiles(filesToRead);
143
+ return applyLogFilters(logs, filters);
144
+ };
145
+ function resolveLogFiles(filters, chunking) {
146
+ if (chunking === "none") {
147
+ const logFile = filters.appName ? join(logDir, `${filters.appName}.log`) : join(logDir, "app.log");
148
+ return existsSync(logFile) ? [logFile] : [];
149
+ }
150
+ if (!filters.appName) {
151
+ return [];
152
+ }
153
+ const appDir = join(logDir, filters.appName);
154
+ if (!existsSync(appDir)) {
155
+ return [];
156
+ }
157
+ const allFiles = readdirSync(appDir).filter((f) => f.endsWith(".log")).sort();
158
+ if (!(filters.from || filters.to)) {
159
+ return allFiles.map((f) => join(appDir, f));
160
+ }
161
+ return allFiles.filter((filename) => isChunkRelevant(filename, chunking, filters)).map((f) => join(appDir, f));
162
+ }
163
+ function isChunkRelevant(filename, chunking, filters) {
164
+ const chunkName = filename.replace(".log", "");
165
+ const range = getChunkDateRange(chunkName, chunking);
166
+ if (!range) {
167
+ return true;
168
+ }
169
+ const { start, end } = range;
170
+ if (filters.to && start > filters.to) {
171
+ return false;
172
+ }
173
+ if (filters.from && end < filters.from) {
174
+ return false;
175
+ }
176
+ return true;
177
+ }
178
+ function getChunkDateRange(chunkName, chunking) {
179
+ if (chunking === "monthly") {
180
+ const match2 = chunkName.match(MONTHLY_PATTERN);
181
+ if (!(match2?.[1] && match2[2])) {
182
+ return null;
183
+ }
184
+ const year2 = Number(match2[1]);
185
+ const month = Number(match2[2]) - 1;
186
+ const start2 = new Date(year2, month, 1);
187
+ const end2 = new Date(year2, month + 1, 0, 23, 59, 59, 999);
188
+ return { start: start2, end: end2 };
189
+ }
190
+ if (chunking === "daily") {
191
+ const match2 = chunkName.match(DAILY_PATTERN);
192
+ if (!(match2?.[1] && match2[2] && match2[3])) {
193
+ return null;
194
+ }
195
+ const year2 = Number(match2[1]);
196
+ const month = Number(match2[2]) - 1;
197
+ const day = Number(match2[3]);
198
+ const start2 = new Date(year2, month, day);
199
+ const end2 = new Date(year2, month, day, 23, 59, 59, 999);
200
+ return { start: start2, end: end2 };
201
+ }
202
+ const match = chunkName.match(WEEKLY_PATTERN);
203
+ if (!(match?.[1] && match[2])) {
204
+ return null;
205
+ }
206
+ const year = Number(match[1]);
207
+ const week = Number(match[2]);
208
+ const start = getDateFromISOWeek(year, week);
209
+ const end = new Date(start);
210
+ end.setDate(end.getDate() + 6);
211
+ end.setHours(23, 59, 59, 999);
212
+ return { start, end };
213
+ }
214
+ function getDateFromISOWeek(year, week) {
215
+ const jan4 = new Date(year, 0, 4);
216
+ const dayOfWeek = jan4.getDay() || 7;
217
+ const monday = new Date(jan4);
218
+ monday.setDate(jan4.getDate() - dayOfWeek + 1);
219
+ monday.setDate(monday.getDate() + (week - 1) * 7);
220
+ monday.setHours(0, 0, 0, 0);
221
+ return monday;
222
+ }
223
+ function readAndParseLogFiles(files) {
224
+ const logs = [];
225
+ for (const file of files) {
226
+ if (!existsSync(file)) {
227
+ continue;
228
+ }
229
+ const content = readFileSync(file, "utf-8").trim();
230
+ if (!content) {
231
+ continue;
232
+ }
233
+ const lines = content.split("\n");
234
+ for (const line of lines) {
235
+ try {
236
+ const parsed = JSON.parse(line);
237
+ logs.push(parsed);
238
+ } catch {
239
+ }
240
+ }
241
+ }
242
+ return logs;
243
+ }
244
+ function applyLogFilters(logs, filters) {
245
+ return logs.filter((log) => {
246
+ if (filters.appName && log.appName !== filters.appName) {
247
+ return false;
248
+ }
249
+ if (filters.log_id && log.log_id !== filters.log_id) {
250
+ return false;
251
+ }
252
+ if (filters.level && log.level !== filters.level) {
253
+ return false;
254
+ }
255
+ const time = new Date(log.time);
256
+ if (filters.from && time < filters.from) {
257
+ return false;
258
+ }
259
+ if (filters.to && time > filters.to) {
260
+ return false;
261
+ }
262
+ return true;
263
+ });
264
+ }
265
+
266
+ // src/logging/create-log.ts
267
+ var createLogger = (appName, config) => {
268
+ return {
269
+ info: (input) => createLog({ ...input, appName, level: "info" }, config),
270
+ warn: (input) => createLog({ ...input, appName, level: "warn" }, config),
271
+ error: (input) => createLog({ ...input, appName, level: "error" }, config)
272
+ };
273
+ };
274
+
275
+ // src/nile/server.ts
276
+ import { safeTry as safeTry4 } from "slang-ts";
277
+
278
+ // src/engine/engine.ts
279
+ import { Err as Err3, Ok as Ok3 } from "slang-ts";
280
+
281
+ // src/utils/db/create-model.ts
282
+ import { count, desc, eq, lt } from "drizzle-orm";
283
+ import { Ok, safeTry } from "slang-ts";
284
+
285
+ // src/utils/handle-error.ts
286
+ import { Err } from "slang-ts";
287
+ var CALLER_LINE_REGEX = /at\s+(\S+)\s+/;
288
+ function inferCallerName() {
289
+ const _err = new Error("capture stack trace");
290
+ const stack = _err.stack;
291
+ const callerLine = stack?.split("\n")[3] ?? "";
292
+ const match = callerLine.match(CALLER_LINE_REGEX);
293
+ return match?.[1] ?? "unknown";
294
+ }
295
+ function resolveLogger(explicit) {
296
+ if (explicit) {
297
+ return explicit;
298
+ }
299
+ try {
300
+ const ctx = getContext();
301
+ if (ctx.resources?.logger) {
302
+ return ctx.resources.logger;
303
+ }
304
+ } catch (_) {
305
+ }
306
+ throw new Error(
307
+ "handleError: No logger available. Provide a logger param or set resources.logger on server config."
308
+ );
309
+ }
310
+ function handleError(params) {
311
+ const atFunction = params.atFunction ?? inferCallerName();
312
+ const logger = resolveLogger(params.logger);
313
+ const logId = logger.error({
314
+ atFunction,
315
+ message: params.message,
316
+ data: params.data
317
+ });
318
+ return Err(`[${logId}] ${params.message}`);
319
+ }
320
+
321
+ // src/utils/db/create-transaction-variant.ts
322
+ function createTransactionVariant(fn) {
323
+ return async (params) => {
324
+ const { dbx, ...rest } = params;
325
+ if (!dbx) {
326
+ const result2 = await fn(params);
327
+ if (result2.isErr) {
328
+ throw new Error(String(result2.error));
329
+ }
330
+ return result2;
331
+ }
332
+ const hasTransaction = typeof dbx?.transaction === "function";
333
+ if (hasTransaction) {
334
+ return await dbx.transaction(async (tx) => {
335
+ const result2 = await fn({ ...rest, dbx: tx });
336
+ if (result2.isErr) {
337
+ throw new Error(String(result2.error));
338
+ }
339
+ return result2;
340
+ });
341
+ }
342
+ const result = await fn({ ...rest, dbx });
343
+ if (result.isErr) {
344
+ throw new Error(String(result.error));
345
+ }
346
+ return result;
347
+ };
348
+ }
349
+
350
+ // src/utils/db/get-zod-schema.ts
351
+ import {
352
+ createInsertSchema,
353
+ createSelectSchema,
354
+ createUpdateSchema
355
+ } from "drizzle-zod";
356
+ function getZodSchema(table) {
357
+ const isRelation = Object.hasOwn(table, "config") && Object.hasOwn(table, "table");
358
+ if (isRelation) {
359
+ throw new Error(
360
+ `${String(table)} is a relation schema, not a table schema`
361
+ );
362
+ }
363
+ const insertSchema = createInsertSchema(
364
+ table
365
+ );
366
+ const updateSchema = createUpdateSchema(
367
+ table
368
+ );
369
+ const selectSchema = createSelectSchema(
370
+ table
371
+ );
372
+ return {
373
+ insert: insertSchema,
374
+ update: updateSchema,
375
+ select: selectSchema
376
+ };
377
+ }
378
+
379
+ // src/utils/db/create-model.ts
380
+ function asDb(db) {
381
+ return db;
382
+ }
383
+ function resolveDb(explicitDb) {
384
+ if (explicitDb) {
385
+ return explicitDb;
386
+ }
387
+ try {
388
+ const ctx = getContext();
389
+ if (ctx.resources?.database) {
390
+ return ctx.resources.database;
391
+ }
392
+ } catch (_) {
393
+ }
394
+ throw new Error(
395
+ "createModel: No database available. Pass db in ModelOptions or set resources.database on server config."
396
+ );
397
+ }
398
+ function findTimestampColumn(table) {
399
+ if ("created_at" in table) {
400
+ return "created_at";
401
+ }
402
+ if ("createdAt" in table) {
403
+ return "createdAt";
404
+ }
405
+ return null;
406
+ }
407
+ function createModel(table, options) {
408
+ const { name, cursorColumn = "id" } = options;
409
+ const entityName = name.charAt(0).toUpperCase() + name.slice(1);
410
+ const schemas = getZodSchema(table);
411
+ const tableRef = table;
412
+ const getDb = () => asDb(resolveDb(options.db));
413
+ const create = async ({ data, dbx }) => {
414
+ const parsed = schemas.insert.safeParse(data);
415
+ if (!parsed.success) {
416
+ return handleError({
417
+ message: `Invalid ${name} data`,
418
+ data: { errors: parsed.error },
419
+ atFunction: `${name}.create`
420
+ });
421
+ }
422
+ const db = dbx ? asDb(dbx) : getDb();
423
+ const result = await safeTry(
424
+ () => db.insert(table).values(data).returning()
425
+ );
426
+ if (result.isErr) {
427
+ return handleError({
428
+ message: `Error creating ${name}`,
429
+ data: { error: result.error },
430
+ atFunction: `${name}.create`
431
+ });
432
+ }
433
+ const row = result.value?.[0] ?? null;
434
+ if (!row) {
435
+ return handleError({
436
+ message: `${entityName} creation returned no data`,
437
+ atFunction: `${name}.create`
438
+ });
439
+ }
440
+ return Ok(row);
441
+ };
442
+ const update = async ({ id, data, dbx }) => {
443
+ const parsed = schemas.update.safeParse(data);
444
+ if (!parsed.success) {
445
+ return handleError({
446
+ message: `Invalid ${name} data`,
447
+ data: { errors: parsed.error },
448
+ atFunction: `${name}.update`
449
+ });
450
+ }
451
+ const db = dbx ? asDb(dbx) : getDb();
452
+ const idCol = tableRef.id;
453
+ const result = await safeTry(
454
+ () => db.update(table).set(data).where(eq(idCol, id)).returning()
455
+ );
456
+ if (result.isErr) {
457
+ return handleError({
458
+ message: `Error updating ${name}`,
459
+ data: { id, error: result.error },
460
+ atFunction: `${name}.update`
461
+ });
462
+ }
463
+ const row = result.value?.[0] ?? null;
464
+ if (!row) {
465
+ return handleError({
466
+ message: `${entityName} not found`,
467
+ data: { id },
468
+ atFunction: `${name}.update`
469
+ });
470
+ }
471
+ return Ok(row);
472
+ };
473
+ const createTx = createTransactionVariant(
474
+ create
475
+ );
476
+ const updateTx = createTransactionVariant(
477
+ update
478
+ );
479
+ const findById = async (id) => {
480
+ const db = getDb();
481
+ const idCol = tableRef.id;
482
+ const result = await safeTry(
483
+ () => db.select().from(table).where(eq(idCol, id))
484
+ );
485
+ if (result.isErr) {
486
+ return handleError({
487
+ message: `Error getting ${name}`,
488
+ data: { id, error: result.error },
489
+ atFunction: `${name}.findById`
490
+ });
491
+ }
492
+ const row = result.value?.[0] ?? null;
493
+ if (!row) {
494
+ return handleError({
495
+ message: `${entityName} not found`,
496
+ data: { id },
497
+ atFunction: `${name}.findById`
498
+ });
499
+ }
500
+ return Ok(row);
501
+ };
502
+ const deleteFn = async (id) => {
503
+ const db = getDb();
504
+ const idCol = tableRef.id;
505
+ const result = await safeTry(
506
+ () => db.delete(table).where(eq(idCol, id)).returning()
507
+ );
508
+ if (result.isErr) {
509
+ return handleError({
510
+ message: `Error deleting ${name}`,
511
+ data: { id, error: result.error },
512
+ atFunction: `${name}.delete`
513
+ });
514
+ }
515
+ const row = result.value?.[0] ?? null;
516
+ if (!row) {
517
+ return handleError({
518
+ message: `${entityName} not found`,
519
+ data: { id },
520
+ atFunction: `${name}.delete`
521
+ });
522
+ }
523
+ return Ok(row);
524
+ };
525
+ const findAll = async () => {
526
+ const db = getDb();
527
+ const tsCol = findTimestampColumn(tableRef);
528
+ const result = await safeTry(() => {
529
+ const query = db.select().from(table);
530
+ if (!tsCol) {
531
+ return query;
532
+ }
533
+ return query.orderBy(desc(tableRef[tsCol]));
534
+ });
535
+ if (result.isErr) {
536
+ return handleError({
537
+ message: `Error getting all ${name}s`,
538
+ data: { error: result.error },
539
+ atFunction: `${name}.findAll`
540
+ });
541
+ }
542
+ return Ok(result.value ?? []);
543
+ };
544
+ const findOffsetPage = async (limit, offset) => {
545
+ const db = getDb();
546
+ const tsCol = findTimestampColumn(tableRef);
547
+ const itemsResult = await safeTry(() => {
548
+ const query = db.select().from(table);
549
+ const ordered = tsCol ? query.orderBy(desc(tableRef[tsCol])) : query;
550
+ return ordered.limit(limit).offset(offset);
551
+ });
552
+ if (itemsResult.isErr) {
553
+ return handleError({
554
+ message: `Error getting paginated ${name}s`,
555
+ data: { limit, offset, error: itemsResult.error },
556
+ atFunction: `${name}.findPaginated`
557
+ });
558
+ }
559
+ const countResult = await safeTry(
560
+ () => db.select({ total: count() }).from(table)
561
+ );
562
+ if (countResult.isErr) {
563
+ return handleError({
564
+ message: `Error getting ${name} count`,
565
+ data: { error: countResult.error },
566
+ atFunction: `${name}.findPaginated`
567
+ });
568
+ }
569
+ const items = itemsResult.value ?? [];
570
+ const total = countResult.value?.[0]?.total ?? 0;
571
+ return Ok({
572
+ items,
573
+ total,
574
+ hasMore: offset + items.length < total
575
+ });
576
+ };
577
+ const findCursorPage = async (limit, cursor, colName) => {
578
+ const db = getDb();
579
+ const column = tableRef[colName];
580
+ if (!column) {
581
+ return handleError({
582
+ message: `Cursor column '${colName}' does not exist on ${name} table`,
583
+ atFunction: `${name}.findPaginated`
584
+ });
585
+ }
586
+ const typedColumn = column;
587
+ const result = await safeTry(
588
+ () => db.select().from(table).where(lt(typedColumn, cursor)).orderBy(desc(typedColumn)).limit(limit + 1)
589
+ );
590
+ if (result.isErr) {
591
+ return handleError({
592
+ message: `Error getting paginated ${name}s`,
593
+ data: { cursor, cursorColumn: colName, error: result.error },
594
+ atFunction: `${name}.findPaginated`
595
+ });
596
+ }
597
+ const rows = result.value ?? [];
598
+ const hasMore = rows.length > limit;
599
+ const items = hasMore ? rows.slice(0, limit) : rows;
600
+ const lastItem = items.at(-1);
601
+ const nextCursor = lastItem ? String(lastItem[colName] ?? "") || null : null;
602
+ return Ok({
603
+ items,
604
+ nextCursor,
605
+ hasMore
606
+ });
607
+ };
608
+ const findPaginated = (opts = {}) => {
609
+ const limit = opts.limit ?? 50;
610
+ if ("cursor" in opts && opts.cursor) {
611
+ const colName = opts.cursorColumn ?? cursorColumn;
612
+ return findCursorPage(limit, opts.cursor, colName);
613
+ }
614
+ const offset = opts.offset ?? 0;
615
+ return findOffsetPage(limit, offset);
616
+ };
617
+ return {
618
+ create,
619
+ createTx,
620
+ findById,
621
+ update,
622
+ updateTx,
623
+ delete: deleteFn,
624
+ findAll,
625
+ findPaginated,
626
+ table,
627
+ schemas
628
+ };
629
+ }
630
+
631
+ // src/utils/diagnostics-log.ts
632
+ function isNileLogger(logger) {
633
+ return "warn" in logger && "error" in logger;
634
+ }
635
+ function createDiagnosticsLog(prefix, params) {
636
+ if (!params.diagnostics) {
637
+ return () => {
638
+ };
639
+ }
640
+ const { logger } = params;
641
+ return (message, data) => {
642
+ if (!logger) {
643
+ console.log(`[${prefix}] ${message}`, data ?? "");
644
+ return;
645
+ }
646
+ if (isNileLogger(logger)) {
647
+ logger.info({
648
+ atFunction: prefix,
649
+ message: `[${prefix}] ${message}`,
650
+ data
651
+ });
652
+ } else {
653
+ logger.info(`[${prefix}] ${message}`, data);
654
+ }
655
+ };
656
+ }
657
+
658
+ // src/engine/pipeline.ts
659
+ import { Err as Err2, Ok as Ok2, safeTry as safeTry2 } from "slang-ts";
660
+ import { prettifyError } from "zod";
661
+ async function runHook(hookDef, hookAction, input, nileContext) {
662
+ const result = await safeTry2(
663
+ () => hookAction.handler(input, nileContext)
664
+ );
665
+ return {
666
+ result,
667
+ logEntry: {
668
+ name: `${hookDef.service}.${hookDef.action}`,
669
+ input,
670
+ output: result.isOk ? result.value : result.error,
671
+ passed: result.isOk
672
+ }
673
+ };
674
+ }
675
+ async function processHooks(hooks, initialValue, getAction, nileContext, logTarget, log) {
676
+ let currentValue = initialValue;
677
+ for (const hookDef of hooks) {
678
+ const hookActionResult = getAction(hookDef.service, hookDef.action);
679
+ if (hookActionResult.isErr) {
680
+ const errorMsg = `${logTarget} hook '${hookDef.service}.${hookDef.action}' not found`;
681
+ log(errorMsg);
682
+ if (hookDef.isCritical) {
683
+ nileContext.setHookError(errorMsg);
684
+ return Err2(errorMsg);
685
+ }
686
+ continue;
687
+ }
688
+ const { result, logEntry } = await runHook(
689
+ hookDef,
690
+ hookActionResult.value,
691
+ currentValue,
692
+ nileContext
693
+ );
694
+ nileContext.addHookLog(logTarget, logEntry);
695
+ if (result.isErr) {
696
+ const errorMsg = String(result.error);
697
+ log(
698
+ `${logTarget} hook '${hookDef.service}.${hookDef.action}' failed`,
699
+ result.error
700
+ );
701
+ if (hookDef.isCritical) {
702
+ nileContext.setHookError(errorMsg);
703
+ return Err2(errorMsg);
704
+ }
705
+ continue;
706
+ }
707
+ currentValue = result.value;
708
+ }
709
+ return Ok2(currentValue);
710
+ }
711
+ async function runGlobalBeforeHook(handler, nileContext, action, payload, log) {
712
+ if (!handler) {
713
+ return Ok2(true);
714
+ }
715
+ const result = await safeTry2(() => handler({ nileContext, action, payload }));
716
+ if (result.isErr) {
717
+ log(`Global before hook failed for ${action.name}`);
718
+ nileContext.setHookError(result.error);
719
+ return Err2(result.error);
720
+ }
721
+ return Ok2(true);
722
+ }
723
+ async function runGlobalAfterHook(handler, nileContext, action, payload, currentResult, log) {
724
+ if (!handler) {
725
+ return Ok2(currentResult);
726
+ }
727
+ const result = await safeTry2(
728
+ () => handler({
729
+ nileContext,
730
+ action,
731
+ payload,
732
+ result: Ok2(currentResult)
733
+ })
734
+ );
735
+ if (result.isErr) {
736
+ log(`Global after hook failed for ${action.name}`);
737
+ nileContext.setHookError(result.error);
738
+ return Err2(result.error);
739
+ }
740
+ return Ok2(result.value);
741
+ }
742
+ function validatePayload(action, payload, nileContext, log) {
743
+ if (!action.validation) {
744
+ return Ok2(payload);
745
+ }
746
+ const parseResult = action.validation.safeParse(payload);
747
+ if (!parseResult.success) {
748
+ const validationError = prettifyError(parseResult.error);
749
+ log(`Validation failed for ${action.name}`, validationError);
750
+ nileContext.setHookError(validationError);
751
+ return Err2(`Validation failed: ${validationError}`);
752
+ }
753
+ return Ok2(parseResult.data);
754
+ }
755
+ async function runHandler(action, payload, nileContext, log) {
756
+ const result = await safeTry2(
757
+ () => action.handler(payload, nileContext)
758
+ );
759
+ if (result.isErr) {
760
+ log(`Handler failed for ${action.name}`, result.error);
761
+ nileContext.setHookError(result.error);
762
+ return Err2(result.error);
763
+ }
764
+ nileContext.setHookOutput(result.value);
765
+ return Ok2(result.value);
766
+ }
767
+
768
+ // src/engine/engine.ts
769
+ function createEngine(options) {
770
+ const { diagnostics, services, logger } = options;
771
+ const log = createDiagnosticsLog("Engine", {
772
+ diagnostics,
773
+ logger
774
+ });
775
+ const serviceSummaries = [];
776
+ const serviceActionsStore = {};
777
+ const actionStore = {};
778
+ const initStartTime = performance.now();
779
+ for (const service of services) {
780
+ const actionNames = [];
781
+ serviceActionsStore[service.name] = [];
782
+ actionStore[service.name] = {};
783
+ for (const action of service.actions) {
784
+ actionNames.push(action.name);
785
+ serviceActionsStore[service.name]?.push({
786
+ name: action.name,
787
+ description: action.description,
788
+ isProtected: !!action.isProtected,
789
+ validation: !!action.validation,
790
+ accessControl: action.accessControl || []
791
+ });
792
+ const serviceActions = actionStore[service.name];
793
+ if (serviceActions) {
794
+ serviceActions[action.name] = action;
795
+ }
796
+ }
797
+ serviceSummaries.push({
798
+ name: service.name,
799
+ description: service.description,
800
+ meta: service.meta,
801
+ actions: actionNames
802
+ });
803
+ }
804
+ log(
805
+ `Initialized in ${performance.now() - initStartTime}ms. Loaded ${services.length} services.`
806
+ );
807
+ const getServices = () => Ok3(serviceSummaries);
808
+ const getServiceActions = (serviceName) => {
809
+ const actions = serviceActionsStore[serviceName];
810
+ return actions ? Ok3(actions) : Err3(`Service '${serviceName}' not found`);
811
+ };
812
+ const getAction = (serviceName, actionName) => {
813
+ const serviceMap = actionStore[serviceName];
814
+ if (!serviceMap) {
815
+ return Err3(`Service '${serviceName}' not found`);
816
+ }
817
+ const action = serviceMap[actionName];
818
+ return action ? Ok3(action) : Err3(`Action '${actionName}' not found in service '${serviceName}'`);
819
+ };
820
+ const executeAction = async (serviceName, actionName, payload, nileContext) => {
821
+ const { onBeforeActionHandler, onAfterActionHandler } = options;
822
+ const actionResult = getAction(serviceName, actionName);
823
+ if (actionResult.isErr) {
824
+ return Err3(actionResult.error);
825
+ }
826
+ const action = actionResult.value;
827
+ nileContext.resetHookContext(`${serviceName}.${actionName}`, payload);
828
+ const globalBeforeResult = await runGlobalBeforeHook(
829
+ onBeforeActionHandler,
830
+ nileContext,
831
+ action,
832
+ payload,
833
+ log
834
+ );
835
+ if (globalBeforeResult.isErr) {
836
+ return Err3(globalBeforeResult.error);
837
+ }
838
+ const beforeHooksResult = await processHooks(
839
+ action.hooks?.before ?? [],
840
+ payload,
841
+ getAction,
842
+ nileContext,
843
+ "before",
844
+ log
845
+ );
846
+ if (beforeHooksResult.isErr) {
847
+ return Err3(beforeHooksResult.error);
848
+ }
849
+ const validationResult = validatePayload(
850
+ action,
851
+ beforeHooksResult.value,
852
+ nileContext,
853
+ log
854
+ );
855
+ if (validationResult.isErr) {
856
+ return Err3(validationResult.error);
857
+ }
858
+ const handlerResult = await runHandler(
859
+ action,
860
+ validationResult.value,
861
+ nileContext,
862
+ log
863
+ );
864
+ if (handlerResult.isErr) {
865
+ return Err3(handlerResult.error);
866
+ }
867
+ const afterHooksResult = await processHooks(
868
+ action.hooks?.after ?? [],
869
+ handlerResult.value,
870
+ getAction,
871
+ nileContext,
872
+ "after",
873
+ log
874
+ );
875
+ if (afterHooksResult.isErr) {
876
+ return Err3(afterHooksResult.error);
877
+ }
878
+ const globalAfterResult = await runGlobalAfterHook(
879
+ onAfterActionHandler,
880
+ nileContext,
881
+ action,
882
+ validationResult.value,
883
+ afterHooksResult.value,
884
+ log
885
+ );
886
+ if (globalAfterResult.isErr) {
887
+ return Err3(globalAfterResult.error);
888
+ }
889
+ return action.result?.pipeline ? Ok3({
890
+ data: globalAfterResult.value,
891
+ pipeline: nileContext.hookContext.log
892
+ }) : Ok3(globalAfterResult.value);
893
+ };
894
+ return {
895
+ getServices,
896
+ getServiceActions,
897
+ getAction,
898
+ executeAction
899
+ };
900
+ }
901
+
902
+ // src/rest/rest.ts
903
+ import { Hono } from "hono";
904
+ import z2 from "zod";
905
+
906
+ // src/cors/cors.ts
907
+ import { cors } from "hono/cors";
908
+ var buildDefaultCorsOptions = (config) => {
909
+ const getDefaultOrigin = (reqOrigin) => {
910
+ if (config.allowedOrigins.length > 0) {
911
+ return config.allowedOrigins.includes(reqOrigin ?? "") ? reqOrigin : "";
912
+ }
913
+ return "*";
914
+ };
915
+ return {
916
+ origin: config.cors?.defaults?.origin ?? getDefaultOrigin,
917
+ credentials: config.cors?.defaults?.credentials ?? true,
918
+ allowHeaders: config.cors?.defaults?.allowHeaders ?? [
919
+ "Content-Type",
920
+ "Authorization"
921
+ ],
922
+ allowMethods: config.cors?.defaults?.allowMethods ?? [
923
+ "POST",
924
+ "GET",
925
+ "OPTIONS"
926
+ ],
927
+ exposeHeaders: config.cors?.defaults?.exposeHeaders ?? ["Content-Length"],
928
+ maxAge: config.cors?.defaults?.maxAge ?? 600
929
+ };
930
+ };
931
+ var applyCorsConfig = (app, config) => {
932
+ const corsEnabled = config.cors?.enabled ?? "default";
933
+ if (corsEnabled === false) {
934
+ return;
935
+ }
936
+ const defaultCorsOpts = buildDefaultCorsOptions(config);
937
+ const corsRules = config.cors?.addCors ?? [];
938
+ for (const rule of corsRules) {
939
+ applyRouteCorsRule(app, rule, defaultCorsOpts);
940
+ }
941
+ app.use("*", cors(defaultCorsOpts));
942
+ };
943
+ var applyRouteCorsRule = (app, rule, defaultOpts) => {
944
+ if (rule.resolver) {
945
+ applyResolverBasedCors(app, rule.path, rule.resolver, defaultOpts);
946
+ } else if (rule.options) {
947
+ applyStaticCors(app, rule.path, rule.options, defaultOpts);
948
+ }
949
+ };
950
+ var applyResolverBasedCors = (app, path, resolver, defaultOpts) => {
951
+ if (!resolver) {
952
+ return;
953
+ }
954
+ app.use(path, (c, next) => {
955
+ const reqOrigin = c.req.header("origin") ?? "";
956
+ const corsOpts = evaluateResolver(resolver, reqOrigin, c, defaultOpts);
957
+ return cors(corsOpts)(c, next);
958
+ });
959
+ };
960
+ var applyStaticCors = (app, path, options, defaultOpts) => {
961
+ app.use(
962
+ path,
963
+ cors({ ...defaultOpts, ...options })
964
+ );
965
+ };
966
+ var evaluateResolver = (resolver, origin, c, defaultOpts) => {
967
+ if (!resolver) {
968
+ return defaultOpts;
969
+ }
970
+ try {
971
+ const result = resolver(origin, c);
972
+ if (result === true) {
973
+ return { ...defaultOpts, origin: origin || "*" };
974
+ }
975
+ if (result === false) {
976
+ return { ...defaultOpts, origin: "" };
977
+ }
978
+ if (result && typeof result === "object") {
979
+ return { ...defaultOpts, ...result };
980
+ }
981
+ return defaultOpts;
982
+ } catch (error) {
983
+ console.error("CORS resolver error:", error);
984
+ return { ...defaultOpts, origin: "" };
985
+ }
986
+ };
987
+
988
+ // src/rest/intent-handlers.ts
989
+ import { Ok as Ok4 } from "slang-ts";
990
+ import z from "zod";
991
+ function toExternalResponse(result, successMessage) {
992
+ if (result.isOk) {
993
+ const value = result.value;
994
+ const data = value !== null && typeof value === "object" && !Array.isArray(value) ? value : { result: value };
995
+ return { status: true, message: successMessage, data };
996
+ }
997
+ return { status: false, message: result.error, data: {} };
998
+ }
999
+ function handleExplore(engine, request) {
1000
+ const { service, action } = request;
1001
+ if (service === "*") {
1002
+ return toExternalResponse(engine.getServices(), "Available services");
1003
+ }
1004
+ if (action === "*") {
1005
+ return toExternalResponse(
1006
+ engine.getServiceActions(service),
1007
+ `Actions for '${service}'`
1008
+ );
1009
+ }
1010
+ const actionResult = engine.getAction(service, action);
1011
+ if (actionResult.isErr) {
1012
+ return toExternalResponse(actionResult, "");
1013
+ }
1014
+ const act = actionResult.value;
1015
+ return toExternalResponse(
1016
+ Ok4({
1017
+ name: act.name,
1018
+ description: act.description,
1019
+ isProtected: act.isProtected ?? false,
1020
+ accessControl: act.accessControl,
1021
+ hooks: act.hooks ? { before: act.hooks.before ?? [], after: act.hooks.after ?? [] } : null,
1022
+ meta: act.meta ?? null
1023
+ }),
1024
+ `Details for '${service}.${action}'`
1025
+ );
1026
+ }
1027
+ async function handleExecute(engine, request, nileContext) {
1028
+ const { service, action, payload } = request;
1029
+ if (service === "*" || action === "*") {
1030
+ return {
1031
+ status: false,
1032
+ message: "Execute intent requires specific service and action, wildcards not allowed",
1033
+ data: {}
1034
+ };
1035
+ }
1036
+ const result = await engine.executeAction(
1037
+ service,
1038
+ action,
1039
+ payload,
1040
+ nileContext
1041
+ );
1042
+ return toExternalResponse(result, `Action '${service}.${action}' executed`);
1043
+ }
1044
+ function handleSchema(engine, request) {
1045
+ const { service, action } = request;
1046
+ if (service === "*") {
1047
+ const servicesResult = engine.getServices();
1048
+ if (servicesResult.isErr) {
1049
+ return toExternalResponse(servicesResult, "");
1050
+ }
1051
+ const schemas = {};
1052
+ for (const svc of servicesResult.value) {
1053
+ const actionsResult = engine.getServiceActions(svc.name);
1054
+ if (actionsResult.isErr) {
1055
+ continue;
1056
+ }
1057
+ schemas[svc.name] = buildServiceSchemas(
1058
+ engine,
1059
+ svc.name,
1060
+ actionsResult.value.map((a) => a.name)
1061
+ );
1062
+ }
1063
+ return toExternalResponse(Ok4(schemas), "All service schemas");
1064
+ }
1065
+ if (action === "*") {
1066
+ const actionsResult = engine.getServiceActions(service);
1067
+ if (actionsResult.isErr) {
1068
+ return toExternalResponse(actionsResult, "");
1069
+ }
1070
+ const schemas = buildServiceSchemas(
1071
+ engine,
1072
+ service,
1073
+ actionsResult.value.map((a) => a.name)
1074
+ );
1075
+ return toExternalResponse(Ok4(schemas), `Schemas for '${service}'`);
1076
+ }
1077
+ const actionResult = engine.getAction(service, action);
1078
+ if (actionResult.isErr) {
1079
+ return toExternalResponse(actionResult, "");
1080
+ }
1081
+ const schema = extractActionSchema(actionResult.value);
1082
+ return toExternalResponse(
1083
+ Ok4({ [action]: schema }),
1084
+ `Schema for '${service}.${action}'`
1085
+ );
1086
+ }
1087
+ function buildServiceSchemas(engine, serviceName, actionNames) {
1088
+ const schemas = {};
1089
+ for (const actionName of actionNames) {
1090
+ const actionResult = engine.getAction(serviceName, actionName);
1091
+ if (actionResult.isErr) {
1092
+ continue;
1093
+ }
1094
+ schemas[actionName] = extractActionSchema(actionResult.value);
1095
+ }
1096
+ return schemas;
1097
+ }
1098
+ function extractActionSchema(action) {
1099
+ const schema = action.validation;
1100
+ if (!schema) {
1101
+ return null;
1102
+ }
1103
+ const { err, result } = safeTrySync(
1104
+ () => z.toJSONSchema(schema, { unrepresentable: "any" })
1105
+ );
1106
+ return err ? null : result;
1107
+ }
1108
+ function safeTrySync(fn) {
1109
+ try {
1110
+ return { err: null, result: fn() };
1111
+ } catch (error) {
1112
+ return { err: error, result: null };
1113
+ }
1114
+ }
1115
+ var intentHandlers = {
1116
+ explore: (engine, request) => handleExplore(engine, request),
1117
+ execute: (engine, request, nileContext) => handleExecute(engine, request, nileContext),
1118
+ schema: (engine, request) => handleSchema(engine, request)
1119
+ };
1120
+
1121
+ // src/rest/middleware.ts
1122
+ import { rateLimiter } from "hono-rate-limiter";
1123
+ import { safeTry as safeTry3 } from "slang-ts";
1124
+ var ASSETS_REGEX = /^\/assets\//;
1125
+ var DEFAULT_RATE_LIMIT_WINDOW_MS = 15 * 60 * 1e3;
1126
+ var DEFAULT_RATE_LIMIT_MAX = 100;
1127
+ var UNKNOWN_CLIENT_KEY = "__unknown_client__";
1128
+ function applyRateLimiting(app, config, log) {
1129
+ if (!config.rateLimiting?.limitingHeader) {
1130
+ return;
1131
+ }
1132
+ const { rateLimiting } = config;
1133
+ app.use(
1134
+ rateLimiter({
1135
+ windowMs: rateLimiting.windowMs ?? DEFAULT_RATE_LIMIT_WINDOW_MS,
1136
+ limit: rateLimiting.limit ?? DEFAULT_RATE_LIMIT_MAX,
1137
+ standardHeaders: rateLimiting.standardHeaders ?? true,
1138
+ keyGenerator: (c) => {
1139
+ const key = c.req.header(rateLimiting.limitingHeader);
1140
+ if (!key) {
1141
+ log(
1142
+ `Rate limiting header '${rateLimiting.limitingHeader}' missing from request`
1143
+ );
1144
+ return UNKNOWN_CLIENT_KEY;
1145
+ }
1146
+ return key;
1147
+ },
1148
+ store: rateLimiting.store ?? void 0
1149
+ })
1150
+ );
1151
+ log(
1152
+ `Rate limiting enabled: ${rateLimiting.limit ?? DEFAULT_RATE_LIMIT_MAX} requests per ${rateLimiting.windowMs ?? DEFAULT_RATE_LIMIT_WINDOW_MS}ms window`
1153
+ );
1154
+ }
1155
+ function applyStaticServing(app, config, runtime, log) {
1156
+ if (!config.enableStatic) {
1157
+ return;
1158
+ }
1159
+ if (runtime !== "bun") {
1160
+ log(`Static file serving not yet supported for runtime: ${runtime}`);
1161
+ return;
1162
+ }
1163
+ let cachedHandler = null;
1164
+ let importFailed = false;
1165
+ app.use("/assets/*", async (c, next) => {
1166
+ if (importFailed) {
1167
+ return next();
1168
+ }
1169
+ if (!cachedHandler) {
1170
+ const importResult = await safeTry3(async () => {
1171
+ const mod = await import("hono/bun");
1172
+ return mod.serveStatic({
1173
+ root: "./assets",
1174
+ rewriteRequestPath: (path) => path.replace(ASSETS_REGEX, "")
1175
+ });
1176
+ });
1177
+ if (importResult.isErr) {
1178
+ log("Failed to load static file adapter", importResult.error);
1179
+ importFailed = true;
1180
+ return next();
1181
+ }
1182
+ cachedHandler = importResult.value;
1183
+ }
1184
+ if (cachedHandler) {
1185
+ return cachedHandler(c, next);
1186
+ }
1187
+ });
1188
+ log("Static file serving enabled at /assets/*");
1189
+ }
1190
+
1191
+ // src/rest/rest.ts
1192
+ var externalRequestSchema = z2.object({
1193
+ intent: z2.enum(["explore", "execute", "schema"]),
1194
+ service: z2.string().min(1),
1195
+ action: z2.string().min(1),
1196
+ payload: z2.record(z2.string(), z2.unknown())
1197
+ });
1198
+ function createRestApp(params) {
1199
+ const { config, engine, nileContext, serverName, runtime } = params;
1200
+ const app = new Hono();
1201
+ const log = createDiagnosticsLog("REST", {
1202
+ diagnostics: config.diagnostics,
1203
+ logger: nileContext.resources?.logger
1204
+ });
1205
+ applyCorsConfig(app, config);
1206
+ applyRateLimiting(app, config, log);
1207
+ applyStaticServing(app, config, runtime, log);
1208
+ const servicesPath = `${config.baseUrl}/services`;
1209
+ app.post(servicesPath, async (c) => {
1210
+ const body = await c.req.json().catch(() => null);
1211
+ if (!body) {
1212
+ return c.json(
1213
+ {
1214
+ status: false,
1215
+ message: "Invalid or missing JSON body",
1216
+ data: {}
1217
+ },
1218
+ 400
1219
+ );
1220
+ }
1221
+ const parsed = externalRequestSchema.safeParse(body);
1222
+ if (!parsed.success) {
1223
+ return c.json(
1224
+ {
1225
+ status: false,
1226
+ message: "Invalid request format",
1227
+ data: { errors: parsed.error.issues }
1228
+ },
1229
+ 400
1230
+ );
1231
+ }
1232
+ const request = parsed.data;
1233
+ log(`${request.intent} -> ${request.service}.${request.action}`);
1234
+ const handler = intentHandlers[request.intent];
1235
+ const response = await handler(engine, request, nileContext);
1236
+ const statusCode = response.status ? 200 : 400;
1237
+ return c.json(response, statusCode);
1238
+ });
1239
+ if (config.enableStatus) {
1240
+ app.get("/status", (c) => {
1241
+ return c.json({
1242
+ status: true,
1243
+ message: `${serverName} is running`,
1244
+ data: {}
1245
+ });
1246
+ });
1247
+ }
1248
+ app.notFound((c) => {
1249
+ return c.json(
1250
+ {
1251
+ status: false,
1252
+ message: `Route not found. Use POST ${servicesPath} for all operations.`,
1253
+ data: {}
1254
+ },
1255
+ 404
1256
+ );
1257
+ });
1258
+ log(`REST interface ready at ${servicesPath}`);
1259
+ return app;
1260
+ }
1261
+
1262
+ // src/nile/nile.ts
1263
+ function createNileContext(params) {
1264
+ const store = /* @__PURE__ */ new Map();
1265
+ const interfaceContext = params?.interfaceContext;
1266
+ const sessions = {};
1267
+ const context = {
1268
+ rest: interfaceContext?.rest,
1269
+ ws: interfaceContext?.ws,
1270
+ rpc: interfaceContext?.rpc,
1271
+ resources: params?.resources,
1272
+ sessions,
1273
+ _store: store,
1274
+ get(key) {
1275
+ return store.get(key);
1276
+ },
1277
+ set(key, value) {
1278
+ store.set(key, value);
1279
+ },
1280
+ getSession(name) {
1281
+ return sessions[name];
1282
+ },
1283
+ setSession(name, data) {
1284
+ sessions[name] = data;
1285
+ },
1286
+ hookContext: {
1287
+ actionName: "",
1288
+ input: null,
1289
+ state: {},
1290
+ log: { before: [], after: [] }
1291
+ },
1292
+ updateHookState(key, value) {
1293
+ context.hookContext.state[key] = value;
1294
+ },
1295
+ addHookLog(phase, logEntry) {
1296
+ context.hookContext.log[phase].push(logEntry);
1297
+ },
1298
+ setHookError(error) {
1299
+ context.hookContext.error = error;
1300
+ },
1301
+ setHookOutput(output) {
1302
+ context.hookContext.output = output;
1303
+ },
1304
+ resetHookContext(actionName, input) {
1305
+ context.hookContext = {
1306
+ actionName,
1307
+ input,
1308
+ state: {},
1309
+ log: { before: [], after: [] }
1310
+ };
1311
+ }
1312
+ };
1313
+ return context;
1314
+ }
1315
+
1316
+ // src/nile/server.ts
1317
+ var _nileContext = null;
1318
+ function getContext() {
1319
+ if (!_nileContext) {
1320
+ throw new Error(
1321
+ "getContext: Server not initialized. Call createNileServer first."
1322
+ );
1323
+ }
1324
+ return _nileContext;
1325
+ }
1326
+ function createNileServer(config) {
1327
+ if (!config.services?.length) {
1328
+ throw new Error(
1329
+ "createNileServer requires at least one service in config.services"
1330
+ );
1331
+ }
1332
+ const log = createDiagnosticsLog("NileServer", {
1333
+ diagnostics: config.diagnostics,
1334
+ logger: config.resources?.logger
1335
+ });
1336
+ const nileContext = createNileContext({
1337
+ resources: config.resources
1338
+ });
1339
+ _nileContext = nileContext;
1340
+ const engine = createEngine({
1341
+ services: config.services,
1342
+ diagnostics: config.diagnostics,
1343
+ logger: config.resources?.logger,
1344
+ onBeforeActionHandler: config.onBeforeActionHandler,
1345
+ onAfterActionHandler: config.onAfterActionHandler
1346
+ });
1347
+ log(`Engine initialized with ${config.services.length} service(s)`);
1348
+ if (config.logServices !== false) {
1349
+ const servicesResult = engine.getServices();
1350
+ if (servicesResult.isOk) {
1351
+ const table = servicesResult.value.map((s) => ({
1352
+ Service: s.name,
1353
+ Description: s.description,
1354
+ Actions: s.actions.length
1355
+ }));
1356
+ console.table(table);
1357
+ }
1358
+ }
1359
+ const server = {
1360
+ config,
1361
+ engine,
1362
+ context: nileContext
1363
+ };
1364
+ if (config.rest) {
1365
+ const app = createRestApp({
1366
+ config: config.rest,
1367
+ engine,
1368
+ nileContext,
1369
+ serverName: config.serverName,
1370
+ runtime: config.runtime ?? "bun"
1371
+ });
1372
+ server.rest = { app, config: config.rest };
1373
+ const host = config.rest.host ?? "localhost";
1374
+ const port = config.rest.port ?? 3e3;
1375
+ const base = `http://${host}:${port}`;
1376
+ console.log(`
1377
+ POST ${base}${config.rest.baseUrl}/services`);
1378
+ if (config.rest.enableStatus) {
1379
+ console.log(` GET ${base}/status`);
1380
+ }
1381
+ console.log("");
1382
+ }
1383
+ if (config.onBoot) {
1384
+ const { fn } = config.onBoot;
1385
+ const _boot = (async () => {
1386
+ const result = await safeTry4(() => fn(nileContext));
1387
+ if (result.isErr) {
1388
+ console.error("[NileServer] onBoot failed:", result.error);
1389
+ }
1390
+ })();
1391
+ _boot;
1392
+ }
1393
+ log(`${config.serverName} server ready`);
1394
+ return server;
1395
+ }
1396
+ export {
1397
+ createAction,
1398
+ createActions,
1399
+ createLog,
1400
+ createLogger,
1401
+ createModel,
1402
+ createNileServer,
1403
+ createService,
1404
+ createServices,
1405
+ createTransactionVariant,
1406
+ getContext,
1407
+ getLogs,
1408
+ getZodSchema,
1409
+ handleError
1410
+ };
1411
+ //# sourceMappingURL=index.js.map