@safaricom-mxl/nuxthub 0.0.3

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/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # @safaricom-mxl/nuxthub
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@safaricom-mxl/nuxthub?color=black)](https://npmjs.com/package/@safaricom-mxl/nuxthub)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@safaricom-mxl/nuxthub?color=black)](https://npm.chart.dev/@safaricom-mxl/nuxthub)
5
+ [![CI](https://img.shields.io/github/actions/workflow/status/HugoRCD/mxllog/ci.yml?branch=main&color=black)](https://github.com/HugoRCD/mxllog/actions/workflows/ci.yml)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-black?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
7
+ [![Nuxt](https://img.shields.io/badge/Nuxt-black?logo=nuxt&logoColor=white)](https://nuxt.com/)
8
+ [![Documentation](https://img.shields.io/badge/Documentation-black?logo=readme&logoColor=white)](https://mxllog.dev)
9
+ [![license](https://img.shields.io/github/license/HugoRCD/mxllog?color=black)](https://github.com/HugoRCD/mxllog/blob/main/LICENSE)
10
+
11
+ Self-hosted log retention for [mxllog](https://mxllog.dev) using [NuxtHub](https://hub.nuxt.com) database storage. Store, query, and automatically clean up your structured logs with zero external dependencies.
12
+
13
+ ## Setup
14
+
15
+ Install the packages:
16
+
17
+ ```bash
18
+ npx nuxi module add @nuxthub/core @safaricom-mxl/nuxthub
19
+ ```
20
+
21
+ Add the module to your `nuxt.config.ts`:
22
+
23
+ ```typescript
24
+ export default defineNuxtConfig({
25
+ modules: ['@nuxthub/core', '@safaricom-mxl/nuxthub'],
26
+
27
+ mxllog: {
28
+ retention: '7d',
29
+ },
30
+ })
31
+ ```
32
+
33
+ > `@safaricom-mxl/log/nuxt` and `@nuxthub/core` can be auto-installed if missing, but we recommend installing `@nuxthub/core` explicitly and registering it in `modules`.
34
+
35
+ ## Configuration
36
+
37
+ | Option | Type | Default | Description |
38
+ | --- | --- | --- | --- |
39
+ | `retention` | `string` | `'7d'` | How long to keep events. Accepts `d` (days), `h` (hours), or `m` (minutes). |
40
+
41
+ The cleanup cron schedule is automatically derived from the retention value.
42
+
43
+ ## Database Support
44
+
45
+ NuxtHub supports multiple database dialects. The `mxllog_events` table schema is automatically registered for:
46
+
47
+ - **SQLite** (default for Cloudflare D1)
48
+ - **MySQL**
49
+ - **PostgreSQL**
50
+
51
+ ## Deployment
52
+
53
+ For Vercel deployments, the module can create a `vercel.json` with the appropriate cron schedule during `nuxi module add`. For Cloudflare and other platforms, the Nitro scheduled task handles cleanup automatically.
54
+
55
+ ## Documentation
56
+
57
+ [Full documentation](https://mxllog.dev)
58
+
59
+ ## License
60
+
61
+ [MIT](https://github.com/HugoRCD/mxllog/blob/main/LICENSE)
@@ -0,0 +1,5 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ declare const _default: _nuxt_schema.NuxtModule<_nuxt_schema.ModuleOptions, _nuxt_schema.ModuleOptions, false>;
4
+
5
+ export { _default as default };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@safaricom-mxl/nuxthub",
3
+ "version": "0.0.3",
4
+ "configKey": "@safaricom-mxl/nuxthub",
5
+ "builder": {
6
+ "@nuxt/module-builder": "1.0.2",
7
+ "unbuild": "3.6.1"
8
+ }
9
+ }
@@ -0,0 +1,91 @@
1
+ import { existsSync, promises } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { defineNuxtModule, createResolver, hasNuxtModule, installModule, addTypeTemplate, addServerPlugin, addServerHandler } from '@nuxt/kit';
4
+ import { consola } from 'consola';
5
+ import { retentionToCron } from '../dist/runtime/utils/retention.js';
6
+
7
+ const name = "@safaricom-mxl/nuxthub";
8
+ const version = "0.0.3";
9
+
10
+ const module$1 = defineNuxtModule({
11
+ meta: {
12
+ name,
13
+ version
14
+ },
15
+ async onInstall(nuxt) {
16
+ const shouldSetup = await consola.prompt(
17
+ "Do you want to create a vercel.json with a cron schedule for mxllog cleanup?",
18
+ { type: "confirm", initial: false }
19
+ );
20
+ if (typeof shouldSetup !== "boolean" || !shouldSetup) return;
21
+ const vercelJsonPath = resolve(nuxt.options.rootDir, "vercel.json");
22
+ let config = {};
23
+ if (existsSync(vercelJsonPath)) {
24
+ config = JSON.parse(await promises.readFile(vercelJsonPath, "utf-8"));
25
+ }
26
+ const mxllogConfig = nuxt.options.mxllog || {};
27
+ const retention = mxllogConfig.retention ?? "7d";
28
+ const cron = retentionToCron(retention);
29
+ const crons = config.crons || [];
30
+ const existing = crons.findIndex((c) => c.path === "/api/_cron/mxllog-cleanup");
31
+ if (existing >= 0) {
32
+ crons[existing].schedule = cron;
33
+ } else {
34
+ crons.push({ path: "/api/_cron/mxllog-cleanup", schedule: cron });
35
+ }
36
+ config.crons = crons;
37
+ await promises.writeFile(vercelJsonPath, `${JSON.stringify(config, null, 2)}
38
+ `, "utf-8");
39
+ consola.success("Created vercel.json with mxllog cleanup cron schedule");
40
+ },
41
+ async setup(_moduleOptions, nuxt) {
42
+ const { resolve: resolve2 } = createResolver(import.meta.url);
43
+ if (!hasNuxtModule("@safaricom-mxl/log/nuxt")) {
44
+ await installModule("@safaricom-mxl/log/nuxt");
45
+ }
46
+ if (!hasNuxtModule("@nuxthub/core")) {
47
+ await installModule("@nuxthub/core");
48
+ }
49
+ addTypeTemplate({
50
+ filename: "types/mxllog-nuxthub.d.ts",
51
+ getContents: () => [
52
+ "declare module '@safaricom-mxl/log/nuxt' {",
53
+ " interface ModuleOptions {",
54
+ " retention?: string",
55
+ " }",
56
+ "}",
57
+ "export {}"
58
+ ].join("\n")
59
+ });
60
+ const mxllogConfig = nuxt.options.mxllog || {};
61
+ const retention = mxllogConfig.retention ?? "7d";
62
+ nuxt.hook("hub:db:schema:extend", ({ dialect, paths }) => {
63
+ paths.push(resolve2(`./runtime/db/schema/events.${dialect}`));
64
+ });
65
+ addServerPlugin(resolve2("./runtime/drain"));
66
+ addServerHandler({
67
+ route: "/api/_cron/mxllog-cleanup",
68
+ handler: resolve2("./runtime/api/_cron/mxllog-cleanup")
69
+ });
70
+ nuxt.hook("nitro:config", (nitroConfig) => {
71
+ nitroConfig.experimental = nitroConfig.experimental || {};
72
+ nitroConfig.experimental.tasks = true;
73
+ nitroConfig.tasks = nitroConfig.tasks || {};
74
+ nitroConfig.tasks["@safaricom-mxl/log:cleanup"] = {
75
+ handler: resolve2("./runtime/tasks/mxllog-cleanup")
76
+ };
77
+ const cron = retentionToCron(retention);
78
+ nitroConfig.scheduledTasks = nitroConfig.scheduledTasks || {};
79
+ const existing = nitroConfig.scheduledTasks[cron];
80
+ if (Array.isArray(existing)) {
81
+ existing.push("@safaricom-mxl/log:cleanup");
82
+ } else if (existing) {
83
+ nitroConfig.scheduledTasks[cron] = [existing, "@safaricom-mxl/log:cleanup"];
84
+ } else {
85
+ nitroConfig.scheduledTasks[cron] = ["@safaricom-mxl/log:cleanup"];
86
+ }
87
+ });
88
+ }
89
+ });
90
+
91
+ export { module$1 as default };
@@ -0,0 +1,5 @@
1
+ declare const _default: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
2
+ result?: unknown;
3
+ success: boolean;
4
+ }>>;
5
+ export default _default;
@@ -0,0 +1,13 @@
1
+ import { runTask } from "nitropack/runtime";
2
+ import { eventHandler, getHeader, createError } from "h3";
3
+ export default eventHandler(async (event) => {
4
+ const cronSecret = process.env.CRON_SECRET;
5
+ if (cronSecret) {
6
+ const authorization = getHeader(event, "authorization");
7
+ if (authorization !== `Bearer ${cronSecret}`) {
8
+ throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
9
+ }
10
+ }
11
+ const result = await runTask("@safaricom-mxl/log:cleanup");
12
+ return { success: true, ...result };
13
+ });
@@ -0,0 +1,245 @@
1
+ export declare const mxllogEvents: import("drizzle-orm/mysql-core").MySqlTableWithColumns<{
2
+ name: "mxllog_events";
3
+ schema: undefined;
4
+ columns: {
5
+ id: import("drizzle-orm/mysql-core").MySqlColumn<{
6
+ name: "id";
7
+ tableName: "mxllog_events";
8
+ dataType: "string";
9
+ columnType: "MySqlText";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: false;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: [string, ...string[]];
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ timestamp: import("drizzle-orm/mysql-core").MySqlColumn<{
23
+ name: "timestamp";
24
+ tableName: "mxllog_events";
25
+ dataType: "string";
26
+ columnType: "MySqlText";
27
+ data: string;
28
+ driverParam: string;
29
+ notNull: true;
30
+ hasDefault: false;
31
+ isPrimaryKey: false;
32
+ isAutoincrement: false;
33
+ hasRuntimeDefault: false;
34
+ enumValues: [string, ...string[]];
35
+ baseColumn: never;
36
+ identity: undefined;
37
+ generated: undefined;
38
+ }, {}, {}>;
39
+ level: import("drizzle-orm/mysql-core").MySqlColumn<{
40
+ name: "level";
41
+ tableName: "mxllog_events";
42
+ dataType: "string";
43
+ columnType: "MySqlText";
44
+ data: string;
45
+ driverParam: string;
46
+ notNull: true;
47
+ hasDefault: false;
48
+ isPrimaryKey: false;
49
+ isAutoincrement: false;
50
+ hasRuntimeDefault: false;
51
+ enumValues: [string, ...string[]];
52
+ baseColumn: never;
53
+ identity: undefined;
54
+ generated: undefined;
55
+ }, {}, {}>;
56
+ service: import("drizzle-orm/mysql-core").MySqlColumn<{
57
+ name: "service";
58
+ tableName: "mxllog_events";
59
+ dataType: "string";
60
+ columnType: "MySqlText";
61
+ data: string;
62
+ driverParam: string;
63
+ notNull: true;
64
+ hasDefault: false;
65
+ isPrimaryKey: false;
66
+ isAutoincrement: false;
67
+ hasRuntimeDefault: false;
68
+ enumValues: [string, ...string[]];
69
+ baseColumn: never;
70
+ identity: undefined;
71
+ generated: undefined;
72
+ }, {}, {}>;
73
+ environment: import("drizzle-orm/mysql-core").MySqlColumn<{
74
+ name: "environment";
75
+ tableName: "mxllog_events";
76
+ dataType: "string";
77
+ columnType: "MySqlText";
78
+ data: string;
79
+ driverParam: string;
80
+ notNull: true;
81
+ hasDefault: false;
82
+ isPrimaryKey: false;
83
+ isAutoincrement: false;
84
+ hasRuntimeDefault: false;
85
+ enumValues: [string, ...string[]];
86
+ baseColumn: never;
87
+ identity: undefined;
88
+ generated: undefined;
89
+ }, {}, {}>;
90
+ method: import("drizzle-orm/mysql-core").MySqlColumn<{
91
+ name: "method";
92
+ tableName: "mxllog_events";
93
+ dataType: "string";
94
+ columnType: "MySqlText";
95
+ data: string;
96
+ driverParam: string;
97
+ notNull: false;
98
+ hasDefault: false;
99
+ isPrimaryKey: false;
100
+ isAutoincrement: false;
101
+ hasRuntimeDefault: false;
102
+ enumValues: [string, ...string[]];
103
+ baseColumn: never;
104
+ identity: undefined;
105
+ generated: undefined;
106
+ }, {}, {}>;
107
+ path: import("drizzle-orm/mysql-core").MySqlColumn<{
108
+ name: "path";
109
+ tableName: "mxllog_events";
110
+ dataType: "string";
111
+ columnType: "MySqlText";
112
+ data: string;
113
+ driverParam: string;
114
+ notNull: false;
115
+ hasDefault: false;
116
+ isPrimaryKey: false;
117
+ isAutoincrement: false;
118
+ hasRuntimeDefault: false;
119
+ enumValues: [string, ...string[]];
120
+ baseColumn: never;
121
+ identity: undefined;
122
+ generated: undefined;
123
+ }, {}, {}>;
124
+ status: import("drizzle-orm/mysql-core").MySqlColumn<{
125
+ name: "status";
126
+ tableName: "mxllog_events";
127
+ dataType: "number";
128
+ columnType: "MySqlInt";
129
+ data: number;
130
+ driverParam: string | number;
131
+ notNull: false;
132
+ hasDefault: false;
133
+ isPrimaryKey: false;
134
+ isAutoincrement: false;
135
+ hasRuntimeDefault: false;
136
+ enumValues: undefined;
137
+ baseColumn: never;
138
+ identity: undefined;
139
+ generated: undefined;
140
+ }, {}, {}>;
141
+ durationMs: import("drizzle-orm/mysql-core").MySqlColumn<{
142
+ name: "duration_ms";
143
+ tableName: "mxllog_events";
144
+ dataType: "number";
145
+ columnType: "MySqlInt";
146
+ data: number;
147
+ driverParam: string | number;
148
+ notNull: false;
149
+ hasDefault: false;
150
+ isPrimaryKey: false;
151
+ isAutoincrement: false;
152
+ hasRuntimeDefault: false;
153
+ enumValues: undefined;
154
+ baseColumn: never;
155
+ identity: undefined;
156
+ generated: undefined;
157
+ }, {}, {}>;
158
+ requestId: import("drizzle-orm/mysql-core").MySqlColumn<{
159
+ name: "request_id";
160
+ tableName: "mxllog_events";
161
+ dataType: "string";
162
+ columnType: "MySqlText";
163
+ data: string;
164
+ driverParam: string;
165
+ notNull: false;
166
+ hasDefault: false;
167
+ isPrimaryKey: false;
168
+ isAutoincrement: false;
169
+ hasRuntimeDefault: false;
170
+ enumValues: [string, ...string[]];
171
+ baseColumn: never;
172
+ identity: undefined;
173
+ generated: undefined;
174
+ }, {}, {}>;
175
+ source: import("drizzle-orm/mysql-core").MySqlColumn<{
176
+ name: "source";
177
+ tableName: "mxllog_events";
178
+ dataType: "string";
179
+ columnType: "MySqlText";
180
+ data: string;
181
+ driverParam: string;
182
+ notNull: false;
183
+ hasDefault: false;
184
+ isPrimaryKey: false;
185
+ isAutoincrement: false;
186
+ hasRuntimeDefault: false;
187
+ enumValues: [string, ...string[]];
188
+ baseColumn: never;
189
+ identity: undefined;
190
+ generated: undefined;
191
+ }, {}, {}>;
192
+ error: import("drizzle-orm/mysql-core").MySqlColumn<{
193
+ name: "error";
194
+ tableName: "mxllog_events";
195
+ dataType: "json";
196
+ columnType: "MySqlJson";
197
+ data: unknown;
198
+ driverParam: string;
199
+ notNull: false;
200
+ hasDefault: false;
201
+ isPrimaryKey: false;
202
+ isAutoincrement: false;
203
+ hasRuntimeDefault: false;
204
+ enumValues: undefined;
205
+ baseColumn: never;
206
+ identity: undefined;
207
+ generated: undefined;
208
+ }, {}, {}>;
209
+ data: import("drizzle-orm/mysql-core").MySqlColumn<{
210
+ name: "data";
211
+ tableName: "mxllog_events";
212
+ dataType: "json";
213
+ columnType: "MySqlJson";
214
+ data: unknown;
215
+ driverParam: string;
216
+ notNull: false;
217
+ hasDefault: false;
218
+ isPrimaryKey: false;
219
+ isAutoincrement: false;
220
+ hasRuntimeDefault: false;
221
+ enumValues: undefined;
222
+ baseColumn: never;
223
+ identity: undefined;
224
+ generated: undefined;
225
+ }, {}, {}>;
226
+ createdAt: import("drizzle-orm/mysql-core").MySqlColumn<{
227
+ name: "created_at";
228
+ tableName: "mxllog_events";
229
+ dataType: "string";
230
+ columnType: "MySqlText";
231
+ data: string;
232
+ driverParam: string;
233
+ notNull: true;
234
+ hasDefault: false;
235
+ isPrimaryKey: false;
236
+ isAutoincrement: false;
237
+ hasRuntimeDefault: false;
238
+ enumValues: [string, ...string[]];
239
+ baseColumn: never;
240
+ identity: undefined;
241
+ generated: undefined;
242
+ }, {}, {}>;
243
+ };
244
+ dialect: "mysql";
245
+ }>;
@@ -0,0 +1,24 @@
1
+ import { index, int, json, mysqlTable, text } from "drizzle-orm/mysql-core";
2
+ export const mxllogEvents = mysqlTable("mxllog_events", {
3
+ id: text("id").primaryKey(),
4
+ timestamp: text("timestamp").notNull(),
5
+ level: text("level").notNull(),
6
+ service: text("service").notNull(),
7
+ environment: text("environment").notNull(),
8
+ method: text("method"),
9
+ path: text("path"),
10
+ status: int("status"),
11
+ durationMs: int("duration_ms"),
12
+ requestId: text("request_id"),
13
+ source: text("source"),
14
+ error: json("error"),
15
+ data: json("data"),
16
+ createdAt: text("created_at").notNull()
17
+ }, (table) => [
18
+ index("mxllog_events_timestamp_idx").on(table.timestamp),
19
+ index("mxllog_events_level_idx").on(table.level),
20
+ index("mxllog_events_service_idx").on(table.service),
21
+ index("mxllog_events_status_idx").on(table.status),
22
+ index("mxllog_events_request_id_idx").on(table.requestId),
23
+ index("mxllog_events_created_at_idx").on(table.createdAt)
24
+ ]);
@@ -0,0 +1,245 @@
1
+ export declare const mxllogEvents: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
+ name: "mxllog_events";
3
+ schema: undefined;
4
+ columns: {
5
+ id: import("drizzle-orm/pg-core").PgColumn<{
6
+ name: "id";
7
+ tableName: "mxllog_events";
8
+ dataType: "string";
9
+ columnType: "PgText";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: false;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: [string, ...string[]];
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ timestamp: import("drizzle-orm/pg-core").PgColumn<{
23
+ name: "timestamp";
24
+ tableName: "mxllog_events";
25
+ dataType: "string";
26
+ columnType: "PgText";
27
+ data: string;
28
+ driverParam: string;
29
+ notNull: true;
30
+ hasDefault: false;
31
+ isPrimaryKey: false;
32
+ isAutoincrement: false;
33
+ hasRuntimeDefault: false;
34
+ enumValues: [string, ...string[]];
35
+ baseColumn: never;
36
+ identity: undefined;
37
+ generated: undefined;
38
+ }, {}, {}>;
39
+ level: import("drizzle-orm/pg-core").PgColumn<{
40
+ name: "level";
41
+ tableName: "mxllog_events";
42
+ dataType: "string";
43
+ columnType: "PgText";
44
+ data: string;
45
+ driverParam: string;
46
+ notNull: true;
47
+ hasDefault: false;
48
+ isPrimaryKey: false;
49
+ isAutoincrement: false;
50
+ hasRuntimeDefault: false;
51
+ enumValues: [string, ...string[]];
52
+ baseColumn: never;
53
+ identity: undefined;
54
+ generated: undefined;
55
+ }, {}, {}>;
56
+ service: import("drizzle-orm/pg-core").PgColumn<{
57
+ name: "service";
58
+ tableName: "mxllog_events";
59
+ dataType: "string";
60
+ columnType: "PgText";
61
+ data: string;
62
+ driverParam: string;
63
+ notNull: true;
64
+ hasDefault: false;
65
+ isPrimaryKey: false;
66
+ isAutoincrement: false;
67
+ hasRuntimeDefault: false;
68
+ enumValues: [string, ...string[]];
69
+ baseColumn: never;
70
+ identity: undefined;
71
+ generated: undefined;
72
+ }, {}, {}>;
73
+ environment: import("drizzle-orm/pg-core").PgColumn<{
74
+ name: "environment";
75
+ tableName: "mxllog_events";
76
+ dataType: "string";
77
+ columnType: "PgText";
78
+ data: string;
79
+ driverParam: string;
80
+ notNull: true;
81
+ hasDefault: false;
82
+ isPrimaryKey: false;
83
+ isAutoincrement: false;
84
+ hasRuntimeDefault: false;
85
+ enumValues: [string, ...string[]];
86
+ baseColumn: never;
87
+ identity: undefined;
88
+ generated: undefined;
89
+ }, {}, {}>;
90
+ method: import("drizzle-orm/pg-core").PgColumn<{
91
+ name: "method";
92
+ tableName: "mxllog_events";
93
+ dataType: "string";
94
+ columnType: "PgText";
95
+ data: string;
96
+ driverParam: string;
97
+ notNull: false;
98
+ hasDefault: false;
99
+ isPrimaryKey: false;
100
+ isAutoincrement: false;
101
+ hasRuntimeDefault: false;
102
+ enumValues: [string, ...string[]];
103
+ baseColumn: never;
104
+ identity: undefined;
105
+ generated: undefined;
106
+ }, {}, {}>;
107
+ path: import("drizzle-orm/pg-core").PgColumn<{
108
+ name: "path";
109
+ tableName: "mxllog_events";
110
+ dataType: "string";
111
+ columnType: "PgText";
112
+ data: string;
113
+ driverParam: string;
114
+ notNull: false;
115
+ hasDefault: false;
116
+ isPrimaryKey: false;
117
+ isAutoincrement: false;
118
+ hasRuntimeDefault: false;
119
+ enumValues: [string, ...string[]];
120
+ baseColumn: never;
121
+ identity: undefined;
122
+ generated: undefined;
123
+ }, {}, {}>;
124
+ status: import("drizzle-orm/pg-core").PgColumn<{
125
+ name: "status";
126
+ tableName: "mxllog_events";
127
+ dataType: "number";
128
+ columnType: "PgInteger";
129
+ data: number;
130
+ driverParam: string | number;
131
+ notNull: false;
132
+ hasDefault: false;
133
+ isPrimaryKey: false;
134
+ isAutoincrement: false;
135
+ hasRuntimeDefault: false;
136
+ enumValues: undefined;
137
+ baseColumn: never;
138
+ identity: undefined;
139
+ generated: undefined;
140
+ }, {}, {}>;
141
+ durationMs: import("drizzle-orm/pg-core").PgColumn<{
142
+ name: "duration_ms";
143
+ tableName: "mxllog_events";
144
+ dataType: "number";
145
+ columnType: "PgInteger";
146
+ data: number;
147
+ driverParam: string | number;
148
+ notNull: false;
149
+ hasDefault: false;
150
+ isPrimaryKey: false;
151
+ isAutoincrement: false;
152
+ hasRuntimeDefault: false;
153
+ enumValues: undefined;
154
+ baseColumn: never;
155
+ identity: undefined;
156
+ generated: undefined;
157
+ }, {}, {}>;
158
+ requestId: import("drizzle-orm/pg-core").PgColumn<{
159
+ name: "request_id";
160
+ tableName: "mxllog_events";
161
+ dataType: "string";
162
+ columnType: "PgText";
163
+ data: string;
164
+ driverParam: string;
165
+ notNull: false;
166
+ hasDefault: false;
167
+ isPrimaryKey: false;
168
+ isAutoincrement: false;
169
+ hasRuntimeDefault: false;
170
+ enumValues: [string, ...string[]];
171
+ baseColumn: never;
172
+ identity: undefined;
173
+ generated: undefined;
174
+ }, {}, {}>;
175
+ source: import("drizzle-orm/pg-core").PgColumn<{
176
+ name: "source";
177
+ tableName: "mxllog_events";
178
+ dataType: "string";
179
+ columnType: "PgText";
180
+ data: string;
181
+ driverParam: string;
182
+ notNull: false;
183
+ hasDefault: false;
184
+ isPrimaryKey: false;
185
+ isAutoincrement: false;
186
+ hasRuntimeDefault: false;
187
+ enumValues: [string, ...string[]];
188
+ baseColumn: never;
189
+ identity: undefined;
190
+ generated: undefined;
191
+ }, {}, {}>;
192
+ error: import("drizzle-orm/pg-core").PgColumn<{
193
+ name: "error";
194
+ tableName: "mxllog_events";
195
+ dataType: "json";
196
+ columnType: "PgJsonb";
197
+ data: unknown;
198
+ driverParam: unknown;
199
+ notNull: false;
200
+ hasDefault: false;
201
+ isPrimaryKey: false;
202
+ isAutoincrement: false;
203
+ hasRuntimeDefault: false;
204
+ enumValues: undefined;
205
+ baseColumn: never;
206
+ identity: undefined;
207
+ generated: undefined;
208
+ }, {}, {}>;
209
+ data: import("drizzle-orm/pg-core").PgColumn<{
210
+ name: "data";
211
+ tableName: "mxllog_events";
212
+ dataType: "json";
213
+ columnType: "PgJsonb";
214
+ data: unknown;
215
+ driverParam: unknown;
216
+ notNull: false;
217
+ hasDefault: false;
218
+ isPrimaryKey: false;
219
+ isAutoincrement: false;
220
+ hasRuntimeDefault: false;
221
+ enumValues: undefined;
222
+ baseColumn: never;
223
+ identity: undefined;
224
+ generated: undefined;
225
+ }, {}, {}>;
226
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
227
+ name: "created_at";
228
+ tableName: "mxllog_events";
229
+ dataType: "string";
230
+ columnType: "PgText";
231
+ data: string;
232
+ driverParam: string;
233
+ notNull: true;
234
+ hasDefault: false;
235
+ isPrimaryKey: false;
236
+ isAutoincrement: false;
237
+ hasRuntimeDefault: false;
238
+ enumValues: [string, ...string[]];
239
+ baseColumn: never;
240
+ identity: undefined;
241
+ generated: undefined;
242
+ }, {}, {}>;
243
+ };
244
+ dialect: "pg";
245
+ }>;
@@ -0,0 +1,24 @@
1
+ import { index, integer, jsonb, pgTable, text } from "drizzle-orm/pg-core";
2
+ export const mxllogEvents = pgTable("mxllog_events", {
3
+ id: text("id").primaryKey(),
4
+ timestamp: text("timestamp").notNull(),
5
+ level: text("level").notNull(),
6
+ service: text("service").notNull(),
7
+ environment: text("environment").notNull(),
8
+ method: text("method"),
9
+ path: text("path"),
10
+ status: integer("status"),
11
+ durationMs: integer("duration_ms"),
12
+ requestId: text("request_id"),
13
+ source: text("source"),
14
+ error: jsonb("error"),
15
+ data: jsonb("data"),
16
+ createdAt: text("created_at").notNull()
17
+ }, (table) => [
18
+ index("mxllog_events_timestamp_idx").on(table.timestamp),
19
+ index("mxllog_events_level_idx").on(table.level),
20
+ index("mxllog_events_service_idx").on(table.service),
21
+ index("mxllog_events_status_idx").on(table.status),
22
+ index("mxllog_events_request_id_idx").on(table.requestId),
23
+ index("mxllog_events_created_at_idx").on(table.createdAt)
24
+ ]);
@@ -0,0 +1,269 @@
1
+ export declare const mxllogEvents: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
2
+ name: "mxllog_events";
3
+ schema: undefined;
4
+ columns: {
5
+ id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
6
+ name: "id";
7
+ tableName: "mxllog_events";
8
+ dataType: "string";
9
+ columnType: "SQLiteText";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: false;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: [string, ...string[]];
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {
22
+ length: number | undefined;
23
+ }>;
24
+ timestamp: import("drizzle-orm/sqlite-core").SQLiteColumn<{
25
+ name: "timestamp";
26
+ tableName: "mxllog_events";
27
+ dataType: "string";
28
+ columnType: "SQLiteText";
29
+ data: string;
30
+ driverParam: string;
31
+ notNull: true;
32
+ hasDefault: false;
33
+ isPrimaryKey: false;
34
+ isAutoincrement: false;
35
+ hasRuntimeDefault: false;
36
+ enumValues: [string, ...string[]];
37
+ baseColumn: never;
38
+ identity: undefined;
39
+ generated: undefined;
40
+ }, {}, {
41
+ length: number | undefined;
42
+ }>;
43
+ level: import("drizzle-orm/sqlite-core").SQLiteColumn<{
44
+ name: "level";
45
+ tableName: "mxllog_events";
46
+ dataType: "string";
47
+ columnType: "SQLiteText";
48
+ data: string;
49
+ driverParam: string;
50
+ notNull: true;
51
+ hasDefault: false;
52
+ isPrimaryKey: false;
53
+ isAutoincrement: false;
54
+ hasRuntimeDefault: false;
55
+ enumValues: [string, ...string[]];
56
+ baseColumn: never;
57
+ identity: undefined;
58
+ generated: undefined;
59
+ }, {}, {
60
+ length: number | undefined;
61
+ }>;
62
+ service: import("drizzle-orm/sqlite-core").SQLiteColumn<{
63
+ name: "service";
64
+ tableName: "mxllog_events";
65
+ dataType: "string";
66
+ columnType: "SQLiteText";
67
+ data: string;
68
+ driverParam: string;
69
+ notNull: true;
70
+ hasDefault: false;
71
+ isPrimaryKey: false;
72
+ isAutoincrement: false;
73
+ hasRuntimeDefault: false;
74
+ enumValues: [string, ...string[]];
75
+ baseColumn: never;
76
+ identity: undefined;
77
+ generated: undefined;
78
+ }, {}, {
79
+ length: number | undefined;
80
+ }>;
81
+ environment: import("drizzle-orm/sqlite-core").SQLiteColumn<{
82
+ name: "environment";
83
+ tableName: "mxllog_events";
84
+ dataType: "string";
85
+ columnType: "SQLiteText";
86
+ data: string;
87
+ driverParam: string;
88
+ notNull: true;
89
+ hasDefault: false;
90
+ isPrimaryKey: false;
91
+ isAutoincrement: false;
92
+ hasRuntimeDefault: false;
93
+ enumValues: [string, ...string[]];
94
+ baseColumn: never;
95
+ identity: undefined;
96
+ generated: undefined;
97
+ }, {}, {
98
+ length: number | undefined;
99
+ }>;
100
+ method: import("drizzle-orm/sqlite-core").SQLiteColumn<{
101
+ name: "method";
102
+ tableName: "mxllog_events";
103
+ dataType: "string";
104
+ columnType: "SQLiteText";
105
+ data: string;
106
+ driverParam: string;
107
+ notNull: false;
108
+ hasDefault: false;
109
+ isPrimaryKey: false;
110
+ isAutoincrement: false;
111
+ hasRuntimeDefault: false;
112
+ enumValues: [string, ...string[]];
113
+ baseColumn: never;
114
+ identity: undefined;
115
+ generated: undefined;
116
+ }, {}, {
117
+ length: number | undefined;
118
+ }>;
119
+ path: import("drizzle-orm/sqlite-core").SQLiteColumn<{
120
+ name: "path";
121
+ tableName: "mxllog_events";
122
+ dataType: "string";
123
+ columnType: "SQLiteText";
124
+ data: string;
125
+ driverParam: string;
126
+ notNull: false;
127
+ hasDefault: false;
128
+ isPrimaryKey: false;
129
+ isAutoincrement: false;
130
+ hasRuntimeDefault: false;
131
+ enumValues: [string, ...string[]];
132
+ baseColumn: never;
133
+ identity: undefined;
134
+ generated: undefined;
135
+ }, {}, {
136
+ length: number | undefined;
137
+ }>;
138
+ status: import("drizzle-orm/sqlite-core").SQLiteColumn<{
139
+ name: "status";
140
+ tableName: "mxllog_events";
141
+ dataType: "number";
142
+ columnType: "SQLiteInteger";
143
+ data: number;
144
+ driverParam: number;
145
+ notNull: false;
146
+ hasDefault: false;
147
+ isPrimaryKey: false;
148
+ isAutoincrement: false;
149
+ hasRuntimeDefault: false;
150
+ enumValues: undefined;
151
+ baseColumn: never;
152
+ identity: undefined;
153
+ generated: undefined;
154
+ }, {}, {}>;
155
+ durationMs: import("drizzle-orm/sqlite-core").SQLiteColumn<{
156
+ name: "duration_ms";
157
+ tableName: "mxllog_events";
158
+ dataType: "number";
159
+ columnType: "SQLiteInteger";
160
+ data: number;
161
+ driverParam: number;
162
+ notNull: false;
163
+ hasDefault: false;
164
+ isPrimaryKey: false;
165
+ isAutoincrement: false;
166
+ hasRuntimeDefault: false;
167
+ enumValues: undefined;
168
+ baseColumn: never;
169
+ identity: undefined;
170
+ generated: undefined;
171
+ }, {}, {}>;
172
+ requestId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
173
+ name: "request_id";
174
+ tableName: "mxllog_events";
175
+ dataType: "string";
176
+ columnType: "SQLiteText";
177
+ data: string;
178
+ driverParam: string;
179
+ notNull: false;
180
+ hasDefault: false;
181
+ isPrimaryKey: false;
182
+ isAutoincrement: false;
183
+ hasRuntimeDefault: false;
184
+ enumValues: [string, ...string[]];
185
+ baseColumn: never;
186
+ identity: undefined;
187
+ generated: undefined;
188
+ }, {}, {
189
+ length: number | undefined;
190
+ }>;
191
+ source: import("drizzle-orm/sqlite-core").SQLiteColumn<{
192
+ name: "source";
193
+ tableName: "mxllog_events";
194
+ dataType: "string";
195
+ columnType: "SQLiteText";
196
+ data: string;
197
+ driverParam: string;
198
+ notNull: false;
199
+ hasDefault: false;
200
+ isPrimaryKey: false;
201
+ isAutoincrement: false;
202
+ hasRuntimeDefault: false;
203
+ enumValues: [string, ...string[]];
204
+ baseColumn: never;
205
+ identity: undefined;
206
+ generated: undefined;
207
+ }, {}, {
208
+ length: number | undefined;
209
+ }>;
210
+ error: import("drizzle-orm/sqlite-core").SQLiteColumn<{
211
+ name: "error";
212
+ tableName: "mxllog_events";
213
+ dataType: "string";
214
+ columnType: "SQLiteText";
215
+ data: string;
216
+ driverParam: string;
217
+ notNull: false;
218
+ hasDefault: false;
219
+ isPrimaryKey: false;
220
+ isAutoincrement: false;
221
+ hasRuntimeDefault: false;
222
+ enumValues: [string, ...string[]];
223
+ baseColumn: never;
224
+ identity: undefined;
225
+ generated: undefined;
226
+ }, {}, {
227
+ length: number | undefined;
228
+ }>;
229
+ data: import("drizzle-orm/sqlite-core").SQLiteColumn<{
230
+ name: "data";
231
+ tableName: "mxllog_events";
232
+ dataType: "string";
233
+ columnType: "SQLiteText";
234
+ data: string;
235
+ driverParam: string;
236
+ notNull: false;
237
+ hasDefault: false;
238
+ isPrimaryKey: false;
239
+ isAutoincrement: false;
240
+ hasRuntimeDefault: false;
241
+ enumValues: [string, ...string[]];
242
+ baseColumn: never;
243
+ identity: undefined;
244
+ generated: undefined;
245
+ }, {}, {
246
+ length: number | undefined;
247
+ }>;
248
+ createdAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
249
+ name: "created_at";
250
+ tableName: "mxllog_events";
251
+ dataType: "string";
252
+ columnType: "SQLiteText";
253
+ data: string;
254
+ driverParam: string;
255
+ notNull: true;
256
+ hasDefault: false;
257
+ isPrimaryKey: false;
258
+ isAutoincrement: false;
259
+ hasRuntimeDefault: false;
260
+ enumValues: [string, ...string[]];
261
+ baseColumn: never;
262
+ identity: undefined;
263
+ generated: undefined;
264
+ }, {}, {
265
+ length: number | undefined;
266
+ }>;
267
+ };
268
+ dialect: "sqlite";
269
+ }>;
@@ -0,0 +1,24 @@
1
+ import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
2
+ export const mxllogEvents = sqliteTable("mxllog_events", {
3
+ id: text("id").primaryKey(),
4
+ timestamp: text("timestamp").notNull(),
5
+ level: text("level").notNull(),
6
+ service: text("service").notNull(),
7
+ environment: text("environment").notNull(),
8
+ method: text("method"),
9
+ path: text("path"),
10
+ status: integer("status"),
11
+ durationMs: integer("duration_ms"),
12
+ requestId: text("request_id"),
13
+ source: text("source"),
14
+ error: text("error"),
15
+ data: text("data"),
16
+ createdAt: text("created_at").notNull()
17
+ }, (table) => [
18
+ index("mxllog_events_timestamp_idx").on(table.timestamp),
19
+ index("mxllog_events_level_idx").on(table.level),
20
+ index("mxllog_events_service_idx").on(table.service),
21
+ index("mxllog_events_status_idx").on(table.status),
22
+ index("mxllog_events_request_id_idx").on(table.requestId),
23
+ index("mxllog_events_created_at_idx").on(table.createdAt)
24
+ ]);
@@ -0,0 +1,2 @@
1
+ declare const _default: import("nitropack").NitroAppPlugin;
2
+ export default _default;
@@ -0,0 +1,107 @@
1
+ import { defineNitroPlugin } from "nitropack/runtime";
2
+ import { db, schema } from "@nuxthub/db";
3
+ function parseDurationMs(event) {
4
+ if (typeof event.durationMs === "number") return event.durationMs;
5
+ if (typeof event.duration === "number") return event.duration;
6
+ if (typeof event.duration === "string") {
7
+ const str = event.duration;
8
+ const msMatch = str.match(/^([\d.]+)\s*ms$/);
9
+ if (msMatch) return Math.round(Number.parseFloat(msMatch[1]));
10
+ const sMatch = str.match(/^([\d.]+)\s*s$/);
11
+ if (sMatch) return Math.round(Number.parseFloat(sMatch[1]) * 1e3);
12
+ }
13
+ return null;
14
+ }
15
+ const INDEXED_FIELDS = /* @__PURE__ */ new Set([
16
+ "timestamp",
17
+ "level",
18
+ "service",
19
+ "environment",
20
+ "method",
21
+ "path",
22
+ "status",
23
+ "durationMs",
24
+ "duration",
25
+ "requestId",
26
+ "source",
27
+ "error"
28
+ ]);
29
+ function extractRow(ctx) {
30
+ const { event, request } = ctx;
31
+ const data = {};
32
+ for (const [key, value] of Object.entries(event)) {
33
+ if (!INDEXED_FIELDS.has(key) && value !== void 0) {
34
+ data[key] = value;
35
+ }
36
+ }
37
+ const errorValue = event.error;
38
+ let errorJson = null;
39
+ if (errorValue !== void 0 && errorValue !== null) {
40
+ if (typeof errorValue === "string") {
41
+ errorJson = errorValue;
42
+ } else {
43
+ try {
44
+ errorJson = JSON.stringify(errorValue);
45
+ } catch {
46
+ errorJson = String(errorValue);
47
+ }
48
+ }
49
+ }
50
+ return {
51
+ id: crypto.randomUUID(),
52
+ timestamp: event.timestamp,
53
+ level: event.level,
54
+ service: event.service,
55
+ environment: event.environment,
56
+ method: (request?.method ?? event.method) || null,
57
+ path: (request?.path ?? event.path) || null,
58
+ status: typeof event.status === "number" ? event.status : null,
59
+ durationMs: parseDurationMs(event),
60
+ requestId: (request?.requestId ?? event.requestId) || null,
61
+ source: event.source || null,
62
+ error: errorJson,
63
+ data: Object.keys(data).length > 0 ? JSON.stringify(data) : null,
64
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
65
+ };
66
+ }
67
+ const MAX_ATTEMPTS = 3;
68
+ const INITIAL_DELAY_MS = 500;
69
+ const RETRYABLE_CODES = /* @__PURE__ */ new Set([
70
+ "CONNECT_TIMEOUT",
71
+ "ETIMEDOUT",
72
+ "ECONNREFUSED",
73
+ "ECONNRESET",
74
+ "CONNECTION_ENDED"
75
+ ]);
76
+ function isRetryable(error) {
77
+ if (error instanceof Error) {
78
+ const cause = error.cause;
79
+ if (cause && typeof cause.code === "string") {
80
+ return RETRYABLE_CODES.has(cause.code);
81
+ }
82
+ }
83
+ return false;
84
+ }
85
+ async function insertWithRetry(rows) {
86
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
87
+ try {
88
+ await db.insert(schema.mxllogEvents).values(rows);
89
+ return;
90
+ } catch (error) {
91
+ if (attempt === MAX_ATTEMPTS || !isRetryable(error)) throw error;
92
+ await new Promise((r) => setTimeout(r, INITIAL_DELAY_MS * 2 ** (attempt - 1)));
93
+ }
94
+ }
95
+ }
96
+ export default defineNitroPlugin((nitroApp) => {
97
+ nitroApp.hooks.hook("@safaricom-mxl/log:drain", async (ctx) => {
98
+ try {
99
+ const contexts = Array.isArray(ctx) ? ctx : [ctx];
100
+ if (contexts.length === 0) return;
101
+ const rows = contexts.map(extractRow);
102
+ await insertWithRetry(rows);
103
+ } catch (error) {
104
+ console.error("[mxllog/nuxthub] Failed to insert events:", error);
105
+ }
106
+ });
107
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("nitropack").Task<string>;
2
+ export default _default;
@@ -0,0 +1,24 @@
1
+ import { defineTask, useRuntimeConfig } from "nitropack/runtime";
2
+ import { lt } from "drizzle-orm";
3
+ import { db, schema } from "@nuxthub/db";
4
+ import { parseRetention } from "../utils/retention.js";
5
+ export default defineTask({
6
+ meta: {
7
+ name: "@safaricom-mxl/log:cleanup",
8
+ description: "Clean up expired mxllog events based on retention policy"
9
+ },
10
+ async run() {
11
+ const config = useRuntimeConfig();
12
+ const retention = config.mxllog?.retention ?? "7d";
13
+ const { totalMs } = parseRetention(retention);
14
+ const cutoff = new Date(Date.now() - totalMs).toISOString();
15
+ try {
16
+ const result = await db.delete(schema.mxllogEvents).where(lt(schema.mxllogEvents.createdAt, cutoff));
17
+ console.log(`[mxllog/nuxthub] Cleanup: deleted events older than ${retention} (before ${cutoff})`, result);
18
+ return { result: "success" };
19
+ } catch (error) {
20
+ console.error("[mxllog/nuxthub] Cleanup task failed:", error);
21
+ return { result: "error" };
22
+ }
23
+ }
24
+ });
@@ -0,0 +1,7 @@
1
+ export declare function parseRetention(retention: string): {
2
+ value: number;
3
+ unit: string;
4
+ totalMinutes: number;
5
+ totalMs: number;
6
+ };
7
+ export declare function retentionToCron(retention: string): string;
@@ -0,0 +1,51 @@
1
+ import { createMxllogError } from "@safaricom-mxl/log";
2
+ export function parseRetention(retention) {
3
+ const match = retention.match(/^(\d+)(d|h|m)$/);
4
+ if (!match) {
5
+ throw createMxllogError({
6
+ message: `[mxllog/nuxthub] Invalid retention format: "${retention}"`,
7
+ why: "The retention value must be a number followed by a unit: d (days), h (hours), or m (minutes)",
8
+ fix: `Change retention to a valid format, e.g., "30d", "24h", or "60m"`,
9
+ link: "https://mxllog.dev/nuxthub/retention"
10
+ });
11
+ }
12
+ const [, numStr, unit] = match;
13
+ const num = Number(numStr);
14
+ let totalMinutes;
15
+ switch (unit) {
16
+ case "m":
17
+ totalMinutes = num;
18
+ break;
19
+ case "h":
20
+ totalMinutes = num * 60;
21
+ break;
22
+ case "d":
23
+ totalMinutes = num * 24 * 60;
24
+ break;
25
+ default:
26
+ throw createMxllogError({
27
+ message: `[mxllog/nuxthub] Unknown retention unit: "${unit}"`,
28
+ why: "The retention value must use one of the supported units: d (days), h (hours), or m (minutes)",
29
+ fix: `Change retention to a valid format, e.g., "30d", "24h", or "60m"`,
30
+ link: "https://mxllog.dev/nuxthub/retention"
31
+ });
32
+ }
33
+ return {
34
+ value: num,
35
+ unit,
36
+ totalMinutes,
37
+ totalMs: totalMinutes * 60 * 1e3
38
+ };
39
+ }
40
+ export function retentionToCron(retention) {
41
+ const { totalMinutes } = parseRetention(retention);
42
+ const halfMinutes = Math.max(1, Math.floor(totalMinutes / 2));
43
+ if (halfMinutes < 60) {
44
+ return `*/${halfMinutes} * * * *`;
45
+ }
46
+ const halfHours = Math.floor(halfMinutes / 60);
47
+ if (halfHours >= 24) {
48
+ return "0 3 * * *";
49
+ }
50
+ return `0 */${halfHours} * * *`;
51
+ }
@@ -0,0 +1,7 @@
1
+ import type { NuxtModule } from '@nuxt/schema'
2
+
3
+ import type { default as Module } from './module.mjs'
4
+
5
+ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
+
7
+ export { default } from './module.mjs'
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@safaricom-mxl/nuxthub",
3
+ "version": "0.0.3",
4
+ "description": "Self-hosted log retention for mxllog using NuxtHub database storage",
5
+ "author": "Francis Konde <fkonde@safaricom.co.ke>",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "sideEffects": false,
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/types.d.mts",
12
+ "import": "./dist/module.mjs"
13
+ }
14
+ },
15
+ "main": "./dist/module.mjs",
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "keywords": [
21
+ "nuxthub",
22
+ "mxllog",
23
+ "logging",
24
+ "nuxt",
25
+ "drizzle",
26
+ "database"
27
+ ],
28
+ "scripts": {
29
+ "build": "nuxt-module-build build",
30
+ "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare"
31
+ },
32
+ "devDependencies": {
33
+ "@nuxt/module-builder": "^1.0.2",
34
+ "@safaricom-mxl/log": "workspace:*",
35
+ "typescript": "^5.9.3"
36
+ },
37
+ "peerDependencies": {
38
+ "@safaricom-mxl/log": "workspace:*",
39
+ "@nuxthub/core": "^0.10.6",
40
+ "drizzle-orm": ">=0.45.1"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "drizzle-orm": {
44
+ "optional": false
45
+ },
46
+ "@safaricom-mxl/log": {
47
+ "optional": false
48
+ },
49
+ "@nuxthub/core": {
50
+ "optional": false
51
+ }
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ }
56
+ }