@opble/core 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,504 +1,3 @@
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
- AppFactory: () => AppFactory,
34
- BaseController: () => BaseController,
35
- CorsModule: () => CorsModule,
36
- CrudController: () => CrudController,
37
- LoggerFactory: () => LoggerFactory,
38
- ModuleLoader: () => ModuleLoader,
39
- RateLimiterModule: () => RateLimiterModule,
40
- ServiceStatusModule: () => ServiceStatusModule,
41
- auth: () => auth,
42
- authAllowKeys: () => authAllowKeys,
43
- authAllowPermission: () => authAllowPermission,
44
- authAllowRole: () => authAllowRole,
45
- getCurrentUser: () => getCurrentUser,
46
- log: () => log,
47
- setUp: () => setUp,
48
- tearDown: () => tearDown
49
- });
50
- module.exports = __toCommonJS(index_exports);
51
-
52
- // src/controller/base.ts
53
- var BaseController = class {
54
- };
55
-
56
- // src/controller/crud.ts
57
- var import_types = require("@opble/types");
58
- var CrudController = class extends BaseController {
59
- constructor(factory, repository) {
60
- super();
61
- this.factory = factory;
62
- this.repository = repository;
63
- }
64
- createItem = async (context, options) => {
65
- const body = await context.req.json();
66
- const result = await options.schema.safeParseAsync(body);
67
- if (!result.success) {
68
- throw result.error;
69
- }
70
- const item = await this.repository.save(this.factory.create(result.data));
71
- return context.json(options.view ? options.view(item) : item, 201);
72
- };
73
- updateItem = async (context, options) => {
74
- const body = await context.req.json();
75
- const result = await options.schema.safeParseAsync(body);
76
- if (!result.success) {
77
- throw result.error;
78
- }
79
- const requestId = options.parseRequestId(context);
80
- let item = await this.repository.get(requestId);
81
- if (!item) {
82
- throw import_types.ResourceNotFoundError;
83
- }
84
- if (options.allow && !options.allow(context, item)) {
85
- throw import_types.ForbiddenError;
86
- }
87
- item = await this.repository.save(
88
- this.factory.create({
89
- ...item,
90
- ...result.data
91
- })
92
- );
93
- return context.json(options.view ? options.view(item) : item);
94
- };
95
- getItem = async (context, options) => {
96
- const requestId = options.parseRequestId(context);
97
- const item = await this.repository.get(requestId);
98
- if (!item) {
99
- throw import_types.ResourceNotFoundError;
100
- }
101
- if (options.allow && !options.allow(context, item)) {
102
- throw import_types.ForbiddenError;
103
- }
104
- return context.json(options.view ? options.view(item) : item);
105
- };
106
- deleteItem = async (context, options) => {
107
- const requestId = options.parseRequestId(context);
108
- const item = await this.repository.get(requestId);
109
- if (!item) {
110
- throw import_types.ResourceNotFoundError;
111
- }
112
- if (options.allow && !options.allow(context, item)) {
113
- throw import_types.ForbiddenError;
114
- }
115
- await this.repository.delete(requestId);
116
- return context.json(options.view ? options.view(item) : item);
117
- };
118
- };
119
-
120
- // src/lib/app.ts
121
- var import_hono = require("hono");
122
-
123
- // src/middleware/app.ts
124
- var import_types2 = require("@opble/types");
125
- var import_toolkit2 = require("@opble/toolkit");
126
- var import_factory = require("hono/factory");
127
- var import_zod2 = __toESM(require("zod"), 1);
128
-
129
- // src/lib/debugger.ts
130
- var import_debug = require("@opble/debug");
131
- var debug = (0, import_debug.createDebug)("opble:core");
132
-
133
- // src/lib/logger.ts
134
- var import_toolkit = require("@opble/toolkit");
135
- var winston = __toESM(require("winston"), 1);
136
-
137
- // src/lib/env.ts
138
- var import_zod = require("zod");
139
- var commaSeparatedToArray = (value) => {
140
- if (typeof value === "string") {
141
- const items = value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
142
- return items;
143
- }
144
- return [];
145
- };
146
- var envSchema = import_zod.z.object({
147
- LOGGER_TRANSPORT_DEFAULT: import_zod.z.enum(["console", "file"]).default("console"),
148
- LOGGER_TRANSPORT_FILE: import_zod.z.string().optional(),
149
- APP_BASE_PATH: import_zod.z.string().optional(),
150
- CORS_ALLOW_ORIGIN: import_zod.z.preprocess(commaSeparatedToArray, import_zod.z.array(import_zod.z.string())).default(["*"]),
151
- CORS_ALLOW_HEADERS: import_zod.z.preprocess(commaSeparatedToArray, import_zod.z.array(import_zod.z.string())).default(["Content-Type", "Authorization"]),
152
- CORS_ALLOW_METHODS: import_zod.z.preprocess(commaSeparatedToArray, import_zod.z.array(import_zod.z.string())).default(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]),
153
- USE_RATE_LIMIT: import_zod.z.transform((val) => {
154
- if (val === "true" || val === "1") return true;
155
- if (val === "false" || val === "0") return false;
156
- return Boolean(val);
157
- }).default(false),
158
- RATE_LIMIT_WINDOW: import_zod.z.preprocess((val) => {
159
- const n = Number(val);
160
- return isNaN(n) ? 15 * 60 * 1e3 : n;
161
- }, import_zod.z.number()).default(15 * 60 * 1e3),
162
- RATE_LIMIT: import_zod.z.preprocess((val) => {
163
- const n = Number(val);
164
- return isNaN(n) ? 100 : n;
165
- }, import_zod.z.number()).default(100)
166
- });
167
- var env = envSchema.parse(process.env);
168
-
169
- // src/lib/logger.ts
170
- var createDefaultWinstonTransport = () => {
171
- if (env.LOGGER_TRANSPORT_DEFAULT === "file" && env.LOGGER_TRANSPORT_FILE) {
172
- return new winston.transports.File({
173
- filename: env.LOGGER_TRANSPORT_FILE
174
- });
175
- }
176
- return new winston.transports.Console();
177
- };
178
- var createWinstonLogger = (...transports2) => winston.createLogger({
179
- level: "info",
180
- format: winston.format.combine(
181
- winston.format.splat(),
182
- winston.format.timestamp(),
183
- winston.format.json()
184
- ),
185
- transports: transports2.length ? transports2 : [createDefaultWinstonTransport()]
186
- });
187
- var winstonLogger = createWinstonLogger();
188
- var CoreLogger = class {
189
- logger;
190
- traceId;
191
- metadata;
192
- timer;
193
- constructor(metadata = {}) {
194
- const traceId = crypto.randomUUID();
195
- this.traceId = traceId;
196
- this.metadata = {
197
- traceId,
198
- ...metadata
199
- };
200
- this.logger = winstonLogger.child(this.metadata);
201
- this.timer = new import_toolkit.Timer();
202
- }
203
- log(level, message, ...args) {
204
- this.logger.log(level, message, ...args, { duration: this.timer.tock() });
205
- }
206
- debug(message, ...args) {
207
- this.log("debug", message, ...args);
208
- }
209
- info(message, ...args) {
210
- this.log("info", message, ...args);
211
- }
212
- warn(message, ...args) {
213
- this.log("warn", message, ...args);
214
- }
215
- error(message, ...args) {
216
- this.log("error", message, ...args);
217
- }
218
- };
219
- var LoggerFactory = {
220
- create(data) {
221
- return new CoreLogger(data);
222
- }
223
- };
224
- function log(context) {
225
- let logger = context.get("logger");
226
- if (!logger) {
227
- logger = LoggerFactory.create({});
228
- context.set("logger", logger);
229
- }
230
- return logger;
231
- }
232
-
233
- // src/middleware/app.ts
234
- function toHttpError(context, e) {
235
- let err;
236
- if (e instanceof import_zod2.ZodError) {
237
- err = import_types2.BadRequestError;
238
- err.message = import_zod2.default.prettifyError(e);
239
- } else if ((0, import_types2.isHttpError)(e)) {
240
- err = e;
241
- } else {
242
- err = import_types2.InternalServerError;
243
- log(context).error("Catch an error. Details: %s", e);
244
- }
245
- return err;
246
- }
247
- var setUp = (0, import_factory.createMiddleware)(
248
- async (context, next) => {
249
- const timer = new import_toolkit2.Timer();
250
- timer.tick();
251
- try {
252
- log(context).info("[start] %s %s", context.req.method, context.req.path);
253
- await next();
254
- log(context).info("[end]", {
255
- status: context.res.status,
256
- duration: timer.tock()
257
- });
258
- } catch (err) {
259
- const response = tearDown(err, context);
260
- log(context).info("[end]", {
261
- status: response.status,
262
- duration: timer.tock()
263
- });
264
- return response;
265
- }
266
- }
267
- );
268
- var tearDown = (err, context) => {
269
- debug("Tear down with error:", err);
270
- const { code, message, status } = toHttpError(context, err);
271
- return context.json({ code, message }, status);
272
- };
273
-
274
- // src/lib/loader.ts
275
- var ModuleLoader = class _ModuleLoader {
276
- modules;
277
- constructor(modules) {
278
- this.modules = modules;
279
- }
280
- static load(app, ...modules) {
281
- const loader = new _ModuleLoader(modules);
282
- loader.modules.forEach((module2) => {
283
- module2.install(app);
284
- debug(
285
- "Module is loaded => %s",
286
- `${Object.getPrototypeOf(module2).constructor.name}`
287
- );
288
- });
289
- }
290
- };
291
-
292
- // src/lib/app.ts
293
- function createApp(...modules) {
294
- let app;
295
- if (env.APP_BASE_PATH) {
296
- app = new import_hono.Hono().basePath(env.APP_BASE_PATH);
297
- debug(`App base path set to '${env.APP_BASE_PATH}'`);
298
- } else {
299
- app = new import_hono.Hono();
300
- }
301
- app.use(setUp);
302
- ModuleLoader.load(app, ...modules);
303
- app.onError(tearDown);
304
- return app;
305
- }
306
- var AppFactory = {
307
- create: (data) => {
308
- if (data && Array.isArray(data)) {
309
- return createApp(...data);
310
- }
311
- return new import_hono.Hono();
312
- }
313
- };
314
-
315
- // src/lib/auth.ts
316
- function getCurrentUser(context) {
317
- const user = context.get("user");
318
- if (!user) {
319
- throw new Error("User not found");
320
- }
321
- return user;
322
- }
323
-
324
- // src/middleware/auth.ts
325
- var import_auth0 = require("@opble/auth0");
326
- var import_entity_auth = require("@opble/entity-auth");
327
- var import_factory2 = require("hono/factory");
328
- var systemUser = import_entity_auth.PublicUserFactory.create({
329
- id: "system",
330
- name: "System",
331
- email: "system@wewise.net",
332
- role: "admin",
333
- emailVerified: true
334
- });
335
- var toHeaderGetter = (context) => {
336
- return {
337
- get: (name) => context.req.header(name)
338
- };
339
- };
340
- function isAuthenticated(context) {
341
- const user = context.get("user");
342
- if (!user) {
343
- return false;
344
- }
345
- return import_entity_auth.PublicUserSchema.safeParse(user).success;
346
- }
347
- var auth = (0, import_factory2.createMiddleware)(
348
- async (context, next) => {
349
- if (!isAuthenticated(context)) {
350
- try {
351
- const user = await (0, import_auth0.requireUser)(toHeaderGetter(context));
352
- context.set("user", user);
353
- } catch (e) {
354
- return tearDown(e, context);
355
- }
356
- }
357
- await next();
358
- }
359
- );
360
- var authAllowRole = (...roles) => {
361
- return (0, import_factory2.createMiddleware)(async (context, next) => {
362
- if (!isAuthenticated(context)) {
363
- context.set(
364
- "user",
365
- await (0, import_auth0.requireUserRole)(toHeaderGetter(context), ...roles)
366
- );
367
- }
368
- await next();
369
- });
370
- };
371
- var authAllowPermission = (...permissions) => {
372
- return (0, import_factory2.createMiddleware)(async (context, next) => {
373
- if (!isAuthenticated(context)) {
374
- context.set(
375
- "user",
376
- await (0, import_auth0.requireUserPermission)(toHeaderGetter(context), ...permissions)
377
- );
378
- }
379
- await next();
380
- });
381
- };
382
- var authAllowKeys = (...keys) => {
383
- return (0, import_factory2.createMiddleware)(async (context, next) => {
384
- if (!isAuthenticated(context)) {
385
- const token = (0, import_auth0.extractBearerToken)(toHeaderGetter(context));
386
- if (token && keys.includes(token)) {
387
- context.set("user", systemUser);
388
- }
389
- }
390
- await next();
391
- });
392
- };
393
-
394
- // src/lib/constant.ts
395
- var UP = "UP";
396
-
397
- // src/module/service-status.ts
398
- var ServiceStatusModule = class {
399
- options;
400
- constructor(options = {}) {
401
- this.options = options;
402
- }
403
- install(app) {
404
- const handler = new ServiceStatusHandler();
405
- app.get("/health", handler.checkHealth);
406
- }
407
- };
408
- var ServiceStatusHandler = class {
409
- checkHealth = (context) => {
410
- return context.json({
411
- status: UP
412
- });
413
- };
414
- };
415
-
416
- // src/module/cors.ts
417
- var import_debug2 = require("@opble/debug");
418
- var import_cors = require("hono/cors");
419
- var wildcardToRegExp = (pattern) => {
420
- const hasScheme = pattern.indexOf("://") !== -1;
421
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, (m) => `\\${m}`);
422
- const wildcardReplaced = escaped.replace(/\*/g, ".*");
423
- const regexStr = hasScheme ? `^${wildcardReplaced}$` : `^https?://${wildcardReplaced}$`;
424
- return new RegExp(regexStr);
425
- };
426
- var debug2 = (0, import_debug2.createDebug)("opble:core:CorsModule");
427
- var CorsModule = class {
428
- install(app) {
429
- debug2("CORS_ALLOW_ORIGIN:", env.CORS_ALLOW_ORIGIN);
430
- app.use(
431
- "/*",
432
- (0, import_cors.cors)({
433
- origin: (origin) => {
434
- for (const allowedOrigin of env.CORS_ALLOW_ORIGIN) {
435
- if (allowedOrigin === "*") {
436
- return origin ?? "";
437
- }
438
- if (origin === allowedOrigin) {
439
- return origin;
440
- }
441
- if (allowedOrigin.indexOf("*") !== -1) {
442
- const re = wildcardToRegExp(allowedOrigin);
443
- if (origin && re.test(origin)) {
444
- return origin;
445
- }
446
- }
447
- }
448
- return "";
449
- },
450
- allowHeaders: env.CORS_ALLOW_HEADERS,
451
- allowMethods: env.CORS_ALLOW_METHODS
452
- })
453
- );
454
- }
455
- };
456
-
457
- // src/module/rate-limiter.ts
458
- var import_debug3 = require("@opble/debug");
459
- var import_hono_rate_limiter = require("hono-rate-limiter");
460
- var debug3 = (0, import_debug3.createDebug)("opble:core:rate-limiter");
461
- var RateLimiterModule = class {
462
- constructor(options = { paths: ["/*"] }) {
463
- this.options = options;
464
- }
465
- install(app) {
466
- debug3("Use Rate Limiter:", env.USE_RATE_LIMIT);
467
- if (!env.USE_RATE_LIMIT) {
468
- return;
469
- }
470
- debug3("Installing RateLimiterModule with options:", this.options);
471
- debug3(
472
- `Rate limiting: ${env.RATE_LIMIT} requests per ${env.RATE_LIMIT_WINDOW} ms`
473
- );
474
- const limiter = (0, import_hono_rate_limiter.rateLimiter)({
475
- windowMs: env.RATE_LIMIT_WINDOW,
476
- limit: env.RATE_LIMIT,
477
- standardHeaders: "draft-7",
478
- keyGenerator: (c) => c.req.header("x-forwarded-for") ?? "anonymous"
479
- });
480
- for (const path of this.options.paths) {
481
- app.use(path, limiter);
482
- }
483
- }
484
- };
485
- // Annotate the CommonJS export names for ESM import in node:
486
- 0 && (module.exports = {
487
- AppFactory,
488
- BaseController,
489
- CorsModule,
490
- CrudController,
491
- LoggerFactory,
492
- ModuleLoader,
493
- RateLimiterModule,
494
- ServiceStatusModule,
495
- auth,
496
- authAllowKeys,
497
- authAllowPermission,
498
- authAllowRole,
499
- getCurrentUser,
500
- log,
501
- setUp,
502
- tearDown
503
- });
1
+ 'use strict';var types=require('@opble/types'),hono=require('hono'),toolkit=require('@opble/toolkit'),factory=require('hono/factory'),$=require('zod'),debug=require('@opble/debug'),m=require('winston'),auth0=require('@opble/auth0'),entityAuth=require('@opble/entity-auth'),cors=require('hono/cors'),honoRateLimiter=require('hono-rate-limiter');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var $__default=/*#__PURE__*/_interopDefault($);var m__namespace=/*#__PURE__*/_interopNamespace(m);var w=class{};var T=class extends w{constructor(t,o){super();this.factory=t;this.repository=o;}createItem=async(t,o)=>{let s=await t.req.json(),i=await o.schema.safeParseAsync(s);if(!i.success)throw i.error;let d=await this.repository.save(this.factory.create(i.data));return t.json(o.view?o.view(d):d,201)};updateItem=async(t,o)=>{let s=await t.req.json(),i=await o.schema.safeParseAsync(s);if(!i.success)throw i.error;let d=o.parseRequestId(t),l=await this.repository.get(d);if(!l)throw types.ResourceNotFoundError;if(o.allow&&!o.allow(t,l))throw types.ForbiddenError;return l=await this.repository.save(this.factory.create({...l,...i.data})),t.json(o.view?o.view(l):l)};getItem=async(t,o)=>{let s=o.parseRequestId(t),i=await this.repository.get(s);if(!i)throw types.ResourceNotFoundError;if(o.allow&&!o.allow(t,i))throw types.ForbiddenError;return t.json(o.view?o.view(i):i)};deleteItem=async(t,o)=>{let s=o.parseRequestId(t),i=await this.repository.get(s);if(!i)throw types.ResourceNotFoundError;if(o.allow&&!o.allow(t,i))throw types.ForbiddenError;return await this.repository.delete(s),t.json(o.view?o.view(i):i)}};var c=debug.createDebug("opble:core");var R=r=>typeof r=="string"?r.split(",").map(t=>t.trim()).filter(t=>t.length>0):[],H=$.z.object({LOGGER_TRANSPORT_DEFAULT:$.z.enum(["console","file"]).default("console"),LOGGER_TRANSPORT_FILE:$.z.string().optional(),APP_BASE_PATH:$.z.string().optional(),CORS_ALLOW_ORIGIN:$.z.preprocess(R,$.z.array($.z.string())).default(["*"]),CORS_ALLOW_HEADERS:$.z.preprocess(R,$.z.array($.z.string())).default(["Content-Type","Authorization"]),CORS_ALLOW_METHODS:$.z.preprocess(R,$.z.array($.z.string())).default(["GET","POST","PUT","PATCH","DELETE","OPTIONS"]),USE_RATE_LIMIT:$.z.transform(r=>r==="true"||r==="1"?true:r==="false"||r==="0"?false:!!r).default(false),RATE_LIMIT_WINDOW:$.z.preprocess(r=>{let e=Number(r);return isNaN(e)?900*1e3:e},$.z.number()).default(900*1e3),RATE_LIMIT:$.z.preprocess(r=>{let e=Number(r);return isNaN(e)?100:e},$.z.number()).default(100)}),n=H.parse(process.env);var N=()=>n.LOGGER_TRANSPORT_DEFAULT==="file"&&n.LOGGER_TRANSPORT_FILE?new m__namespace.transports.File({filename:n.LOGGER_TRANSPORT_FILE}):new m__namespace.transports.Console,D=(...r)=>m__namespace.createLogger({level:"info",format:m__namespace.format.combine(m__namespace.format.splat(),m__namespace.format.timestamp(),m__namespace.format.json()),transports:r.length?r:[N()]}),k=D(),x=class{logger;traceId;metadata;timer;constructor(e={}){let t=crypto.randomUUID();this.traceId=t,this.metadata={traceId:t,...e},this.logger=k.child(this.metadata),this.timer=new toolkit.Timer;}log(e,t,...o){this.logger.log(e,t,...o,{duration:this.timer.tock()});}debug(e,...t){this.log("debug",e,...t);}info(e,...t){this.log("info",e,...t);}warn(e,...t){this.log("warn",e,...t);}error(e,...t){this.log("error",e,...t);}},q={create(r){return new x(r)}};function u(r){let e=r.get("logger");return e||(e=q.create({}),r.set("logger",e)),e}function z(r,e){let t;return e instanceof $.ZodError?(t=types.BadRequestError,t.message=$__default.default.prettifyError(e)):types.isHttpError(e)?t=e:(t=types.InternalServerError,u(r).error("Catch an error. Details: %s",e)),t}var L=factory.createMiddleware(async(r,e)=>{let t=new toolkit.Timer;t.tick();try{u(r).info("[start] %s %s",r.req.method,r.req.path),await e(),u(r).info("[end]",{status:r.res.status,duration:t.tock()});}catch(o){let s=f(o,r);return u(r).info("[end]",{status:s.status,duration:t.tock()}),s}}),f=(r,e)=>{c("Tear down with error:",r);let{code:t,message:o,status:s}=z(e,r);return e.json({code:t,message:o},s)};var g=class r{modules;constructor(e){this.modules=e;}static load(e,...t){new r(t).modules.forEach(s=>{s.install(e),c("Module is loaded => %s",`${Object.getPrototypeOf(s).constructor.name}`);});}};function _(...r){let e;return n.APP_BASE_PATH?(e=new hono.Hono().basePath(n.APP_BASE_PATH),c(`App base path set to '${n.APP_BASE_PATH}'`)):e=new hono.Hono,e.use(L),g.load(e,...r),e.onError(f),e}var je={create:r=>r&&Array.isArray(r)?_(...r):_()};function ze(r){let e=r.get("user");if(!e)throw new Error("User not found");return e}var ee=entityAuth.PublicUserFactory.create({id:"system",name:"System",email:"system@wewise.net",role:"admin",emailVerified:true}),y=r=>({get:e=>r.req.header(e)});function A(r){let e=r.get("user");return e?entityAuth.PublicUserSchema.safeParse(e).success:false}var rt=factory.createMiddleware(async(r,e)=>{if(!A(r))try{let t=await auth0.requireUser(y(r));r.set("user",t);}catch(t){return f(t,r)}await e();}),ot=(...r)=>factory.createMiddleware(async(e,t)=>{A(e)||e.set("user",await auth0.requireUserRole(y(e),...r)),await t();}),st=(...r)=>factory.createMiddleware(async(e,t)=>{A(e)||e.set("user",await auth0.requireUserPermission(y(e),...r)),await t();}),nt=(...r)=>factory.createMiddleware(async(e,t)=>{if(!A(e)){let o=auth0.extractBearerToken(y(e));o&&r.includes(o)&&e.set("user",ee);}await t();});var P=class{options;constructor(e={}){this.options=e;}install(e){let t=new C;e.get("/health",t.checkHealth);}},C=class{checkHealth=e=>e.json({status:"UP"})};var se=r=>{let e=r.indexOf("://")!==-1,o=r.replace(/[.+?^${}()|[\]\\]/g,i=>`\\${i}`).replace(/\*/g,".*"),s=e?`^${o}$`:`^https?://${o}$`;return new RegExp(s)},ne=debug.createDebug("opble:core:CorsModule"),M=class{install(e){ne("CORS_ALLOW_ORIGIN:",n.CORS_ALLOW_ORIGIN),e.use("/*",cors.cors({origin:t=>{for(let o of n.CORS_ALLOW_ORIGIN){if(o==="*")return t??"";if(t===o)return t;if(o.indexOf("*")!==-1){let s=se(o);if(t&&s.test(t))return t}}return ""},allowHeaders:n.CORS_ALLOW_HEADERS,allowMethods:n.CORS_ALLOW_METHODS}));}};var I=debug.createDebug("opble:core:rate-limiter"),S=class{constructor(e={paths:["/*"]}){this.options=e;}install(e){if(I("Use Rate Limiter:",n.USE_RATE_LIMIT),!n.USE_RATE_LIMIT)return;I("Installing RateLimiterModule with options:",this.options),I(`Rate limiting: ${n.RATE_LIMIT} requests per ${n.RATE_LIMIT_WINDOW} ms`);let t=honoRateLimiter.rateLimiter({windowMs:n.RATE_LIMIT_WINDOW,limit:n.RATE_LIMIT,standardHeaders:"draft-7",keyGenerator:o=>o.req.header("x-forwarded-for")??"anonymous"});for(let o of this.options.paths)e.use(o,t);}};
2
+ exports.AppFactory=je;exports.BaseController=w;exports.CorsModule=M;exports.CrudController=T;exports.LoggerFactory=q;exports.ModuleLoader=g;exports.RateLimiterModule=S;exports.ServiceStatusModule=P;exports.auth=rt;exports.authAllowKeys=nt;exports.authAllowPermission=st;exports.authAllowRole=ot;exports.getCurrentUser=ze;exports.log=u;exports.setUp=L;exports.tearDown=f;//# sourceMappingURL=index.cjs.map
504
3
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/controller/base.ts","../src/controller/crud.ts","../src/lib/app.ts","../src/middleware/app.ts","../src/lib/debugger.ts","../src/lib/logger.ts","../src/lib/env.ts","../src/lib/loader.ts","../src/lib/auth.ts","../src/middleware/auth.ts","../src/lib/constant.ts","../src/module/service-status.ts","../src/module/cors.ts","../src/module/rate-limiter.ts"],"sourcesContent":["export * from './controller/base';\nexport * from './controller/crud';\nexport * from './lib/app';\nexport * from './lib/auth';\nexport * from './lib/handler';\nexport * from './lib/loader';\nexport * from './lib/logger';\nexport * from './lib/types';\nexport * from './middleware/app';\nexport * from './middleware/auth';\nexport * from './module/service-status';\nexport * from './module/cors';\nexport * from './module/rate-limiter';\n","export class BaseController {}\n","import {\n Factory,\n ForbiddenError,\n HashMap,\n ResourceNotFoundError,\n} from '@opble/types';\nimport { z } from 'zod';\n\nimport { AppContext } from '../lib/types';\n\nimport { BaseController } from './base';\nimport { DynamoDBRepository, TableKey } from '@opble/repository-dynamodb';\n\ntype View = (data: unknown) => HashMap;\n\ntype BaseOptions = {\n view?: View;\n};\n\ntype PayloadOptions = BaseOptions & {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema: z.ZodType<unknown, unknown, any>;\n};\n\ntype RequestIdOptions = {\n parseRequestId: (context: AppContext) => TableKey;\n};\n\ntype ResourcePolicyOptions<T extends HashMap> = {\n allow?: (context: AppContext, item: T) => boolean;\n};\n\ntype GetItemOptions = BaseOptions & RequestIdOptions;\ntype DeleteItemOptions = GetItemOptions;\n\ntype CreateItemOptions = PayloadOptions;\ntype UpdateItemOptions = PayloadOptions & RequestIdOptions;\n\nexport class CrudController<T extends HashMap> extends BaseController {\n constructor(\n protected factory: Factory<T>,\n private repository: DynamoDBRepository<T>\n ) {\n super();\n }\n\n createItem = async (context: AppContext, options: CreateItemOptions) => {\n const body = await context.req.json();\n const result = await options.schema.safeParseAsync(body);\n if (!result.success) {\n throw result.error;\n }\n\n const item = await this.repository.save(this.factory.create(result.data));\n\n return context.json(options.view ? options.view(item) : item, 201);\n };\n\n updateItem = async (\n context: AppContext,\n options: UpdateItemOptions & ResourcePolicyOptions<T>\n ) => {\n const body = await context.req.json();\n const result = await options.schema.safeParseAsync(body);\n if (!result.success) {\n throw result.error;\n }\n\n const requestId = options.parseRequestId(context);\n let item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n item = await this.repository.save(\n this.factory.create({\n ...item,\n ...result.data,\n })\n );\n\n return context.json(options.view ? options.view(item) : item);\n };\n\n getItem = async (\n context: AppContext,\n options: GetItemOptions & ResourcePolicyOptions<T>\n ) => {\n const requestId = options.parseRequestId(context);\n const item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n return context.json(options.view ? options.view(item) : item);\n };\n\n deleteItem = async (\n context: AppContext,\n options: DeleteItemOptions & ResourcePolicyOptions<T>\n ) => {\n const requestId = options.parseRequestId(context);\n const item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n await this.repository.delete(requestId);\n return context.json(options.view ? options.view(item) : item);\n };\n}\n","import { Factory } from '@opble/types';\nimport { Hono } from 'hono';\n\nimport { setUp, tearDown } from '../middleware/app';\n\nimport { debug } from './debugger';\nimport { env } from './env';\nimport { Module, ModuleLoader } from './loader';\nimport { App, CoreEnv } from './types';\n\nfunction createApp(...modules: Module[]) {\n let app: App;\n if (env.APP_BASE_PATH) {\n app = new Hono<CoreEnv>().basePath(env.APP_BASE_PATH);\n debug(`App base path set to '${env.APP_BASE_PATH}'`);\n } else {\n app = new Hono<CoreEnv>();\n }\n\n app.use(setUp);\n ModuleLoader.load(app, ...modules);\n app.onError(tearDown);\n\n return app;\n}\n\nexport const AppFactory: Factory<App> = {\n create: (data: unknown) => {\n if (data && Array.isArray(data)) {\n return createApp(...data);\n }\n\n return new Hono<CoreEnv>();\n },\n};\n","import {\n BadRequestError,\n HttpError,\n InternalServerError,\n isHttpError,\n} from '@opble/types';\nimport { Timer } from '@opble/toolkit';\nimport { Next } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { ContentfulStatusCode } from 'hono/utils/http-status';\nimport z, { ZodError } from 'zod';\n\nimport { debug } from '../lib/debugger.js';\nimport { log } from '../lib/logger';\nimport { AppContext } from '../lib/types';\n\nfunction toHttpError(context: AppContext, e: unknown): HttpError {\n let err: HttpError;\n if (e instanceof ZodError) {\n err = BadRequestError;\n err.message = z.prettifyError(e);\n } else if (isHttpError(e)) {\n err = e as HttpError;\n } else {\n err = InternalServerError;\n log(context).error('Catch an error. Details: %s', e);\n }\n\n return err;\n}\n\nexport const setUp = createMiddleware(\n async (context: AppContext, next: Next) => {\n const timer = new Timer();\n timer.tick();\n\n try {\n log(context).info('[start] %s %s', context.req.method, context.req.path);\n await next();\n log(context).info('[end]', {\n status: context.res.status,\n duration: timer.tock(),\n });\n } catch (err) {\n const response = tearDown(err, context);\n log(context).info('[end]', {\n status: response.status,\n duration: timer.tock(),\n });\n\n return response;\n }\n }\n);\n\nexport const tearDown = (err: unknown, context: AppContext) => {\n debug('Tear down with error:', err);\n const { code, message, status } = toHttpError(context, err);\n return context.json({ code, message }, status as ContentfulStatusCode);\n};\n","import { createDebug } from '@opble/debug';\n\nexport const debug = createDebug('opble:core');\n","import { Factory, StringHashMap } from '@opble/types';\nimport { Logger } from '@opble/types';\nimport { Timer } from '@opble/toolkit';\nimport { Context } from 'hono';\nimport * as winston from 'winston';\n\nimport { env } from './env';\nimport { CoreEnv } from './types';\n\nconst createDefaultWinstonTransport = (): winston.transport => {\n if (env.LOGGER_TRANSPORT_DEFAULT === 'file' && env.LOGGER_TRANSPORT_FILE) {\n return new winston.transports.File({\n filename: env.LOGGER_TRANSPORT_FILE,\n });\n }\n\n return new winston.transports.Console();\n};\n\nconst createWinstonLogger = (...transports: winston.transport[]) =>\n winston.createLogger({\n level: 'info',\n format: winston.format.combine(\n winston.format.splat(),\n winston.format.timestamp(),\n winston.format.json()\n ),\n transports: transports.length\n ? transports\n : [createDefaultWinstonTransport()],\n });\n\nconst winstonLogger: winston.Logger = createWinstonLogger();\n\nclass CoreLogger implements Logger {\n private readonly logger: winston.Logger;\n private readonly traceId: string;\n private metadata: StringHashMap;\n private readonly timer: Timer;\n\n constructor(metadata: StringHashMap = {}) {\n const traceId = crypto.randomUUID();\n this.traceId = traceId;\n this.metadata = {\n traceId,\n ...metadata,\n };\n this.logger = winstonLogger.child(this.metadata);\n this.timer = new Timer();\n }\n\n private log(level: string, message: string, ...args: unknown[]): void {\n this.logger.log(level, message, ...args, { duration: this.timer.tock() });\n }\n\n debug(message: string, ...args: unknown[]): void {\n this.log('debug', message, ...args);\n }\n\n info(message: string, ...args: unknown[]): void {\n this.log('info', message, ...args);\n }\n\n warn(message: string, ...args: unknown[]): void {\n this.log('warn', message, ...args);\n }\n\n error(message: string, ...args: unknown[]): void {\n this.log('error', message, ...args);\n }\n}\n\nexport const LoggerFactory: Factory<Logger> = {\n create(data: unknown): Logger {\n return new CoreLogger(data as StringHashMap);\n },\n};\n\nexport function log(context: Context<CoreEnv>): Logger {\n let logger = context.get('logger');\n if (!logger) {\n logger = LoggerFactory.create({});\n context.set('logger', logger);\n }\n\n return logger;\n}\n","import { z } from 'zod';\n\nconst commaSeparatedToArray = (value: unknown) => {\n if (typeof value === 'string') {\n const items = value\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return items;\n }\n\n // If undefined or not a string, return empty array by default\n return [] as string[];\n};\n\nconst envSchema = z.object({\n LOGGER_TRANSPORT_DEFAULT: z.enum(['console', 'file']).default('console'),\n LOGGER_TRANSPORT_FILE: z.string().optional(),\n\n APP_BASE_PATH: z.string().optional(),\n\n CORS_ALLOW_ORIGIN: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['*']),\n CORS_ALLOW_HEADERS: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['Content-Type', 'Authorization']),\n CORS_ALLOW_METHODS: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']),\n\n USE_RATE_LIMIT: z\n .transform((val) => {\n if (val === 'true' || val === '1') return true;\n if (val === 'false' || val === '0') return false;\n return Boolean(val);\n })\n .default(false),\n RATE_LIMIT_WINDOW: z\n .preprocess((val) => {\n const n = Number(val);\n return isNaN(n) ? 15 * 60 * 1000 : n; // default 15 minutes\n }, z.number())\n .default(15 * 60 * 1000),\n RATE_LIMIT: z\n .preprocess((val) => {\n const n = Number(val);\n return isNaN(n) ? 100 : n; // default 100 requests\n }, z.number())\n .default(100),\n});\n\nexport const env = envSchema.parse(process.env);\n","import { debug } from './debugger.js';\nimport { App } from './types';\n\nexport interface Module {\n install(app: App): void;\n}\n\nexport class ModuleLoader {\n private modules: Module[];\n\n constructor(modules: Module[]) {\n this.modules = modules;\n }\n\n static load(app: App, ...modules: Module[]) {\n const loader = new ModuleLoader(modules);\n loader.modules.forEach((module) => {\n module.install(app);\n debug(\n 'Module is loaded => %s',\n `${Object.getPrototypeOf(module).constructor.name}`\n );\n });\n }\n}\n","import { PublicUser } from '@opble/entity-auth';\nimport { Context } from 'hono';\n\nimport { CoreEnv } from './types';\n\nexport function getCurrentUser(context: Context<CoreEnv>): PublicUser {\n const user = context.get('user');\n if (!user) {\n throw new Error('User not found');\n }\n return user;\n}\n","import {\n requireUser,\n requireUserRole,\n requireUserPermission,\n extractBearerToken,\n} from '@opble/auth0';\nimport { PublicUserFactory, PublicUserSchema } from '@opble/entity-auth';\nimport { Context, Next } from 'hono';\nimport { createMiddleware } from 'hono/factory';\n\nimport { CoreEnv } from '../lib/types';\n\nimport { tearDown } from './app';\n\nconst systemUser = PublicUserFactory.create({\n id: 'system',\n name: 'System',\n email: 'system@wewise.net',\n role: 'admin',\n emailVerified: true,\n});\nconst toHeaderGetter = (context: Context<CoreEnv>) => {\n return {\n get: (name: string) => context.req.header(name),\n };\n};\n\nfunction isAuthenticated(context: Context<CoreEnv>): boolean {\n const user = context.get('user');\n if (!user) {\n return false;\n }\n\n return PublicUserSchema.safeParse(user).success;\n}\n\nexport const auth = createMiddleware(\n async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n try {\n const user = await requireUser(toHeaderGetter(context));\n context.set('user', user);\n } catch (e) {\n return tearDown(e, context);\n }\n }\n\n await next();\n }\n);\n\nexport const authAllowRole = (...roles: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n context.set(\n 'user',\n await requireUserRole(toHeaderGetter(context), ...roles)\n );\n }\n\n await next();\n });\n};\n\nexport const authAllowPermission = (...permissions: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n context.set(\n 'user',\n await requireUserPermission(toHeaderGetter(context), ...permissions)\n );\n }\n\n await next();\n });\n};\n\nexport const authAllowKeys = (...keys: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n const token = extractBearerToken(toHeaderGetter(context));\n if (token && keys.includes(token)) {\n context.set('user', systemUser);\n }\n }\n\n await next();\n });\n};\n","export const UP = 'UP';\n","import { HashMap } from '@opble/types';\n\nimport { UP } from '../lib/constant';\nimport { Module } from '../lib/loader';\nimport { App, AppContext } from '../lib/types';\n\nexport type ServiceStatusOptions = HashMap;\n\nexport class ServiceStatusModule implements Module {\n private options: ServiceStatusOptions;\n\n constructor(options: ServiceStatusOptions = {}) {\n this.options = options;\n }\n\n install(app: App) {\n const handler = new ServiceStatusHandler();\n app.get('/health', handler.checkHealth);\n }\n}\n\nclass ServiceStatusHandler {\n checkHealth = (context: AppContext) => {\n return context.json({\n status: UP,\n });\n };\n}\n","import { createDebug } from '@opble/debug';\nimport { cors } from 'hono/cors';\n\nimport { env } from '../lib/env';\nimport { Module } from '../lib/loader';\nimport { App } from '../lib/types';\n\n// Convert wildcard pattern like \"https://*.wewise.net\" into a RegExp\nconst wildcardToRegExp = (pattern: string): RegExp => {\n // If pattern doesn't include scheme, allow http(s)\n const hasScheme = pattern.indexOf('://') !== -1;\n // Escape regexp special chars except '*'\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, (m) => `\\\\${m}`);\n // Now replace escaped '*' (which would be '\\*') with '.*'\n const wildcardReplaced = escaped.replace(/\\*/g, '.*');\n const regexStr = hasScheme\n ? `^${wildcardReplaced}$`\n : `^https?://${wildcardReplaced}$`;\n return new RegExp(regexStr);\n};\n\nconst debug = createDebug('opble:core:CorsModule');\n\nexport class CorsModule implements Module {\n install(app: App) {\n debug('CORS_ALLOW_ORIGIN:', env.CORS_ALLOW_ORIGIN);\n app.use(\n '/*',\n cors({\n origin: (origin) => {\n for (const allowedOrigin of env.CORS_ALLOW_ORIGIN) {\n if (allowedOrigin === '*') {\n return origin ?? '';\n }\n\n // Direct match\n if (origin === allowedOrigin) {\n return origin;\n }\n\n // Wildcard matching\n if (allowedOrigin.indexOf('*') !== -1) {\n const re = wildcardToRegExp(allowedOrigin);\n if (origin && re.test(origin)) {\n return origin;\n }\n }\n }\n\n return '';\n },\n allowHeaders: env.CORS_ALLOW_HEADERS,\n allowMethods: env.CORS_ALLOW_METHODS,\n })\n );\n }\n}\n","import { createDebug } from '@opble/debug';\nimport { rateLimiter } from 'hono-rate-limiter';\n\nimport { env } from '../lib/env';\nimport { Module } from '../lib/loader';\nimport { App } from '../lib/types';\n\nconst debug = createDebug('opble:core:rate-limiter');\n\nexport type RateLimiterModuleOptions = {\n paths: string[];\n};\n\nexport class RateLimiterModule implements Module {\n constructor(private options: RateLimiterModuleOptions = { paths: ['/*'] }) {}\n\n install(app: App) {\n debug('Use Rate Limiter:', env.USE_RATE_LIMIT);\n if (!env.USE_RATE_LIMIT) {\n return;\n }\n\n debug('Installing RateLimiterModule with options:', this.options);\n debug(\n `Rate limiting: ${env.RATE_LIMIT} requests per ${env.RATE_LIMIT_WINDOW} ms`\n );\n\n /**\n * Notes:\n * - The rate limiter is applied to the specified paths.\n * - The limit and window are configurable via environment variables.\n * - The key generator uses the \"x-forwarded-for\" header to identify clients behind proxies.\n *\n * Important:\n * - RateLimiterModule must be installed before other modules so it could be executed first.\n */\n const limiter = rateLimiter({\n windowMs: env.RATE_LIMIT_WINDOW,\n limit: env.RATE_LIMIT,\n standardHeaders: 'draft-7',\n keyGenerator: (c) => c.req.header('x-forwarded-for') ?? 'anonymous',\n });\n for (const path of this.options.paths) {\n app.use(path, limiter);\n }\n }\n}\n"],"mappings":"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","names":["import_types","import_toolkit","import_zod","transports","z","module","import_factory","import_debug","debug","import_debug","debug"]}
1
+ {"version":3,"sources":["../src/controller/base.ts","../src/controller/crud.ts","../src/lib/debugger.ts","../src/lib/env.ts","../src/lib/logger.ts","../src/middleware/app.ts","../src/lib/loader.ts","../src/lib/app.ts","../src/lib/auth.ts","../src/middleware/auth.ts","../src/module/service-status.ts","../src/module/cors.ts","../src/module/rate-limiter.ts"],"names":["BaseController","CrudController","factory","repository","context","options","body","result","item","requestId","ResourceNotFoundError","ForbiddenError","debug","createDebug","commaSeparatedToArray","value","s","envSchema","z","val","n","env","createDefaultWinstonTransport","m","createWinstonLogger","transports","winstonLogger","CoreLogger","metadata","traceId","Timer","level","message","args","LoggerFactory","data","log","logger","toHttpError","err","ZodError","BadRequestError","isHttpError","InternalServerError","setUp","createMiddleware","next","timer","response","tearDown","code","status","ModuleLoader","_ModuleLoader","modules","app","module","createApp","Hono","AppFactory","getCurrentUser","user","systemUser","PublicUserFactory","toHeaderGetter","name","isAuthenticated","PublicUserSchema","auth","requireUser","e","authAllowRole","roles","requireUserRole","authAllowPermission","permissions","requireUserPermission","authAllowKeys","keys","token","extractBearerToken","ServiceStatusModule","handler","ServiceStatusHandler","wildcardToRegExp","pattern","hasScheme","wildcardReplaced","regexStr","CorsModule","cors","origin","allowedOrigin","re","RateLimiterModule","limiter","rateLimiter","c","path"],"mappings":"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","file":"index.cjs","sourcesContent":["export class BaseController {}\n","import { DynamoDBRepository, TableKey } from '@opble/repository-dynamodb';\nimport {\n Factory,\n ForbiddenError,\n HashMap,\n ResourceNotFoundError,\n} from '@opble/types';\nimport { z } from 'zod';\n\nimport { AppContext } from '../lib/types';\n\nimport { BaseController } from './base';\n\ntype View = (data: unknown) => HashMap;\n\ntype BaseOptions = {\n view?: View;\n};\n\ntype PayloadOptions = BaseOptions & {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema: z.ZodType<unknown, unknown, any>;\n};\n\ntype RequestIdOptions = {\n parseRequestId: (context: AppContext) => TableKey;\n};\n\ntype ResourcePolicyOptions<T extends HashMap> = {\n allow?: (context: AppContext, item: T) => boolean;\n};\n\ntype GetItemOptions = BaseOptions & RequestIdOptions;\ntype DeleteItemOptions = GetItemOptions;\n\ntype CreateItemOptions = PayloadOptions;\ntype UpdateItemOptions = PayloadOptions & RequestIdOptions;\n\nexport class CrudController<T extends HashMap> extends BaseController {\n constructor(\n protected factory: Factory<T>,\n private repository: DynamoDBRepository<T>\n ) {\n super();\n }\n\n createItem = async (context: AppContext, options: CreateItemOptions) => {\n const body = await context.req.json();\n const result = await options.schema.safeParseAsync(body);\n if (!result.success) {\n throw result.error;\n }\n\n const item = await this.repository.save(this.factory.create(result.data));\n\n return context.json(options.view ? options.view(item) : item, 201);\n };\n\n updateItem = async (\n context: AppContext,\n options: UpdateItemOptions & ResourcePolicyOptions<T>\n ) => {\n const body = await context.req.json();\n const result = await options.schema.safeParseAsync(body);\n if (!result.success) {\n throw result.error;\n }\n\n const requestId = options.parseRequestId(context);\n let item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n item = await this.repository.save(\n this.factory.create({\n ...item,\n ...result.data,\n })\n );\n\n return context.json(options.view ? options.view(item) : item);\n };\n\n getItem = async (\n context: AppContext,\n options: GetItemOptions & ResourcePolicyOptions<T>\n ) => {\n const requestId = options.parseRequestId(context);\n const item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n return context.json(options.view ? options.view(item) : item);\n };\n\n deleteItem = async (\n context: AppContext,\n options: DeleteItemOptions & ResourcePolicyOptions<T>\n ) => {\n const requestId = options.parseRequestId(context);\n const item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n await this.repository.delete(requestId);\n return context.json(options.view ? options.view(item) : item);\n };\n}\n","import { createDebug } from '@opble/debug';\n\nexport const debug = createDebug('opble:core');\n","import { z } from 'zod';\n\nconst commaSeparatedToArray = (value: unknown) => {\n if (typeof value === 'string') {\n const items = value\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return items;\n }\n\n // If undefined or not a string, return empty array by default\n return [] as string[];\n};\n\nconst envSchema = z.object({\n LOGGER_TRANSPORT_DEFAULT: z.enum(['console', 'file']).default('console'),\n LOGGER_TRANSPORT_FILE: z.string().optional(),\n\n APP_BASE_PATH: z.string().optional(),\n\n CORS_ALLOW_ORIGIN: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['*']),\n CORS_ALLOW_HEADERS: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['Content-Type', 'Authorization']),\n CORS_ALLOW_METHODS: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']),\n\n USE_RATE_LIMIT: z\n .transform((val) => {\n if (val === 'true' || val === '1') return true;\n if (val === 'false' || val === '0') return false;\n return Boolean(val);\n })\n .default(false),\n RATE_LIMIT_WINDOW: z\n .preprocess((val) => {\n const n = Number(val);\n return isNaN(n) ? 15 * 60 * 1000 : n; // default 15 minutes\n }, z.number())\n .default(15 * 60 * 1000),\n RATE_LIMIT: z\n .preprocess((val) => {\n const n = Number(val);\n return isNaN(n) ? 100 : n; // default 100 requests\n }, z.number())\n .default(100),\n});\n\nexport const env = envSchema.parse(process.env);\n","import { Timer } from '@opble/toolkit';\nimport { Factory, StringHashMap } from '@opble/types';\nimport { Logger } from '@opble/types';\nimport { Context } from 'hono';\nimport * as winston from 'winston';\n\nimport { env } from './env';\nimport { CoreEnv } from './types';\n\nconst createDefaultWinstonTransport = (): winston.transport => {\n if (env.LOGGER_TRANSPORT_DEFAULT === 'file' && env.LOGGER_TRANSPORT_FILE) {\n return new winston.transports.File({\n filename: env.LOGGER_TRANSPORT_FILE,\n });\n }\n\n return new winston.transports.Console();\n};\n\nconst createWinstonLogger = (...transports: winston.transport[]) =>\n winston.createLogger({\n level: 'info',\n format: winston.format.combine(\n winston.format.splat(),\n winston.format.timestamp(),\n winston.format.json()\n ),\n transports: transports.length\n ? transports\n : [createDefaultWinstonTransport()],\n });\n\nconst winstonLogger: winston.Logger = createWinstonLogger();\n\nclass CoreLogger implements Logger {\n private readonly logger: winston.Logger;\n private readonly traceId: string;\n private metadata: StringHashMap;\n private readonly timer: Timer;\n\n constructor(metadata: StringHashMap = {}) {\n const traceId = crypto.randomUUID();\n this.traceId = traceId;\n this.metadata = {\n traceId,\n ...metadata,\n };\n this.logger = winstonLogger.child(this.metadata);\n this.timer = new Timer();\n }\n\n private log(level: string, message: string, ...args: unknown[]): void {\n this.logger.log(level, message, ...args, { duration: this.timer.tock() });\n }\n\n debug(message: string, ...args: unknown[]): void {\n this.log('debug', message, ...args);\n }\n\n info(message: string, ...args: unknown[]): void {\n this.log('info', message, ...args);\n }\n\n warn(message: string, ...args: unknown[]): void {\n this.log('warn', message, ...args);\n }\n\n error(message: string, ...args: unknown[]): void {\n this.log('error', message, ...args);\n }\n}\n\nexport const LoggerFactory: Factory<Logger> = {\n create(data: unknown): Logger {\n return new CoreLogger(data as StringHashMap);\n },\n};\n\nexport function log(context: Context<CoreEnv>): Logger {\n let logger = context.get('logger');\n if (!logger) {\n logger = LoggerFactory.create({});\n context.set('logger', logger);\n }\n\n return logger;\n}\n","import { Timer } from '@opble/toolkit';\nimport {\n BadRequestError,\n HttpError,\n InternalServerError,\n isHttpError,\n} from '@opble/types';\nimport { Next } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { ContentfulStatusCode } from 'hono/utils/http-status';\nimport z, { ZodError } from 'zod';\n\nimport { debug } from '../lib/debugger.js';\nimport { log } from '../lib/logger';\nimport { AppContext } from '../lib/types';\n\nfunction toHttpError(context: AppContext, e: unknown): HttpError {\n let err: HttpError;\n if (e instanceof ZodError) {\n err = BadRequestError;\n err.message = z.prettifyError(e);\n } else if (isHttpError(e)) {\n err = e as HttpError;\n } else {\n err = InternalServerError;\n log(context).error('Catch an error. Details: %s', e);\n }\n\n return err;\n}\n\nexport const setUp = createMiddleware(\n async (context: AppContext, next: Next) => {\n const timer = new Timer();\n timer.tick();\n\n try {\n log(context).info('[start] %s %s', context.req.method, context.req.path);\n await next();\n log(context).info('[end]', {\n status: context.res.status,\n duration: timer.tock(),\n });\n } catch (err) {\n const response = tearDown(err, context);\n log(context).info('[end]', {\n status: response.status,\n duration: timer.tock(),\n });\n\n return response;\n }\n }\n);\n\nexport const tearDown = (err: unknown, context: AppContext) => {\n debug('Tear down with error:', err);\n const { code, message, status } = toHttpError(context, err);\n return context.json({ code, message }, status as ContentfulStatusCode);\n};\n","import { debug } from './debugger.js';\nimport { App } from './types';\n\nexport interface Module {\n install(app: App): void;\n}\n\nexport class ModuleLoader {\n private modules: Module[];\n\n constructor(modules: Module[]) {\n this.modules = modules;\n }\n\n static load(app: App, ...modules: Module[]) {\n const loader = new ModuleLoader(modules);\n loader.modules.forEach((module) => {\n module.install(app);\n debug(\n 'Module is loaded => %s',\n `${Object.getPrototypeOf(module).constructor.name}`\n );\n });\n }\n}\n","import { Factory } from '@opble/types';\nimport { Hono } from 'hono';\n\nimport { setUp, tearDown } from '../middleware/app';\n\nimport { debug } from './debugger';\nimport { env } from './env';\nimport { Module, ModuleLoader } from './loader';\nimport { App, CoreEnv } from './types';\n\nfunction createApp(...modules: Module[]): App {\n let app;\n if (env.APP_BASE_PATH) {\n app = new Hono<CoreEnv>().basePath(env.APP_BASE_PATH);\n debug(`App base path set to '${env.APP_BASE_PATH}'`);\n } else {\n app = new Hono<CoreEnv>();\n }\n\n app.use(setUp);\n ModuleLoader.load(app, ...modules);\n app.onError(tearDown);\n\n return app;\n}\n\nexport const AppFactory: Factory<App> = {\n create: (data: unknown): App => {\n if (data && Array.isArray(data)) {\n return createApp(...data);\n }\n\n return createApp();\n },\n};\n","import { PublicUser } from '@opble/entity-auth';\nimport { Context } from 'hono';\n\nimport { CoreEnv } from './types';\n\nexport function getCurrentUser(context: Context<CoreEnv>): PublicUser {\n const user = context.get('user');\n if (!user) {\n throw new Error('User not found');\n }\n return user;\n}\n","import {\n requireUser,\n requireUserRole,\n requireUserPermission,\n extractBearerToken,\n} from '@opble/auth0';\nimport { PublicUserFactory, PublicUserSchema } from '@opble/entity-auth';\nimport { Context, Next } from 'hono';\nimport { createMiddleware } from 'hono/factory';\n\nimport { CoreEnv } from '../lib/types';\n\nimport { tearDown } from './app';\n\nconst systemUser = PublicUserFactory.create({\n id: 'system',\n name: 'System',\n email: 'system@wewise.net',\n role: 'admin',\n emailVerified: true,\n});\nconst toHeaderGetter = (context: Context<CoreEnv>) => {\n return {\n get: (name: string) => context.req.header(name),\n };\n};\n\nfunction isAuthenticated(context: Context<CoreEnv>): boolean {\n const user = context.get('user');\n if (!user) {\n return false;\n }\n\n return PublicUserSchema.safeParse(user).success;\n}\n\nexport const auth = createMiddleware(\n async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n try {\n const user = await requireUser(toHeaderGetter(context));\n context.set('user', user);\n } catch (e) {\n return tearDown(e, context);\n }\n }\n\n await next();\n }\n);\n\nexport const authAllowRole = (...roles: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n context.set(\n 'user',\n await requireUserRole(toHeaderGetter(context), ...roles)\n );\n }\n\n await next();\n });\n};\n\nexport const authAllowPermission = (...permissions: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n context.set(\n 'user',\n await requireUserPermission(toHeaderGetter(context), ...permissions)\n );\n }\n\n await next();\n });\n};\n\nexport const authAllowKeys = (...keys: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n const token = extractBearerToken(toHeaderGetter(context));\n if (token && keys.includes(token)) {\n context.set('user', systemUser);\n }\n }\n\n await next();\n });\n};\n","import { HashMap } from '@opble/types';\n\nimport { UP } from '../lib/constant';\nimport { Module } from '../lib/loader';\nimport { App, AppContext } from '../lib/types';\n\nexport type ServiceStatusOptions = HashMap;\n\nexport class ServiceStatusModule implements Module {\n private options: ServiceStatusOptions;\n\n constructor(options: ServiceStatusOptions = {}) {\n this.options = options;\n }\n\n install(app: App) {\n const handler = new ServiceStatusHandler();\n app.get('/health', handler.checkHealth);\n }\n}\n\nclass ServiceStatusHandler {\n checkHealth = (context: AppContext) => {\n return context.json({\n status: UP,\n });\n };\n}\n","import { createDebug } from '@opble/debug';\nimport { cors } from 'hono/cors';\n\nimport { env } from '../lib/env';\nimport { Module } from '../lib/loader';\nimport { App } from '../lib/types';\n\n// Convert wildcard pattern like \"https://*.wewise.net\" into a RegExp\nconst wildcardToRegExp = (pattern: string): RegExp => {\n // If pattern doesn't include scheme, allow http(s)\n const hasScheme = pattern.indexOf('://') !== -1;\n // Escape regexp special chars except '*'\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, (m) => `\\\\${m}`);\n // Now replace escaped '*' (which would be '\\*') with '.*'\n const wildcardReplaced = escaped.replace(/\\*/g, '.*');\n const regexStr = hasScheme\n ? `^${wildcardReplaced}$`\n : `^https?://${wildcardReplaced}$`;\n return new RegExp(regexStr);\n};\n\nconst debug = createDebug('opble:core:CorsModule');\n\nexport class CorsModule implements Module {\n install(app: App) {\n debug('CORS_ALLOW_ORIGIN:', env.CORS_ALLOW_ORIGIN);\n app.use(\n '/*',\n cors({\n origin: (origin) => {\n for (const allowedOrigin of env.CORS_ALLOW_ORIGIN) {\n if (allowedOrigin === '*') {\n return origin ?? '';\n }\n\n // Direct match\n if (origin === allowedOrigin) {\n return origin;\n }\n\n // Wildcard matching\n if (allowedOrigin.indexOf('*') !== -1) {\n const re = wildcardToRegExp(allowedOrigin);\n if (origin && re.test(origin)) {\n return origin;\n }\n }\n }\n\n return '';\n },\n allowHeaders: env.CORS_ALLOW_HEADERS,\n allowMethods: env.CORS_ALLOW_METHODS,\n })\n );\n }\n}\n","import { createDebug } from '@opble/debug';\nimport { rateLimiter } from 'hono-rate-limiter';\n\nimport { env } from '../lib/env';\nimport { Module } from '../lib/loader';\nimport { App } from '../lib/types';\n\nconst debug = createDebug('opble:core:rate-limiter');\n\nexport type RateLimiterModuleOptions = {\n paths: string[];\n};\n\nexport class RateLimiterModule implements Module {\n constructor(private options: RateLimiterModuleOptions = { paths: ['/*'] }) {}\n\n install(app: App) {\n debug('Use Rate Limiter:', env.USE_RATE_LIMIT);\n if (!env.USE_RATE_LIMIT) {\n return;\n }\n\n debug('Installing RateLimiterModule with options:', this.options);\n debug(\n `Rate limiting: ${env.RATE_LIMIT} requests per ${env.RATE_LIMIT_WINDOW} ms`\n );\n\n /**\n * Notes:\n * - The rate limiter is applied to the specified paths.\n * - The limit and window are configurable via environment variables.\n * - The key generator uses the \"x-forwarded-for\" header to identify clients behind proxies.\n *\n * Important:\n * - RateLimiterModule must be installed before other modules so it could be executed first.\n */\n const limiter = rateLimiter({\n windowMs: env.RATE_LIMIT_WINDOW,\n limit: env.RATE_LIMIT,\n standardHeaders: 'draft-7',\n keyGenerator: (c) => c.req.header('x-forwarded-for') ?? 'anonymous',\n });\n for (const path of this.options.paths) {\n app.use(path, limiter);\n }\n }\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -2,10 +2,10 @@ import * as hono_utils_http_status from 'hono/utils/http-status';
2
2
  import * as hono from 'hono';
3
3
  import { Context, Hono } from 'hono';
4
4
  import * as hono_utils_types from 'hono/utils/types';
5
+ import { DynamoDBRepository, TableKey } from '@opble/repository-dynamodb';
5
6
  import { Logger, HashMap, Factory } from '@opble/types';
6
7
  import { z } from 'zod';
7
8
  import { PublicUser } from '@opble/entity-auth';
8
- import { DynamoDBRepository, TableKey } from '@opble/repository-dynamodb';
9
9
 
10
10
  declare class BaseController {
11
11
  }
package/dist/index.d.ts CHANGED
@@ -2,10 +2,10 @@ import * as hono_utils_http_status from 'hono/utils/http-status';
2
2
  import * as hono from 'hono';
3
3
  import { Context, Hono } from 'hono';
4
4
  import * as hono_utils_types from 'hono/utils/types';
5
+ import { DynamoDBRepository, TableKey } from '@opble/repository-dynamodb';
5
6
  import { Logger, HashMap, Factory } from '@opble/types';
6
7
  import { z } from 'zod';
7
8
  import { PublicUser } from '@opble/entity-auth';
8
- import { DynamoDBRepository, TableKey } from '@opble/repository-dynamodb';
9
9
 
10
10
  declare class BaseController {
11
11
  }
package/dist/index.js CHANGED
@@ -1,464 +1,3 @@
1
- // src/controller/base.ts
2
- var BaseController = class {
3
- };
4
-
5
- // src/controller/crud.ts
6
- import {
7
- ForbiddenError,
8
- ResourceNotFoundError
9
- } from "@opble/types";
10
- var CrudController = class extends BaseController {
11
- constructor(factory, repository) {
12
- super();
13
- this.factory = factory;
14
- this.repository = repository;
15
- }
16
- createItem = async (context, options) => {
17
- const body = await context.req.json();
18
- const result = await options.schema.safeParseAsync(body);
19
- if (!result.success) {
20
- throw result.error;
21
- }
22
- const item = await this.repository.save(this.factory.create(result.data));
23
- return context.json(options.view ? options.view(item) : item, 201);
24
- };
25
- updateItem = async (context, options) => {
26
- const body = await context.req.json();
27
- const result = await options.schema.safeParseAsync(body);
28
- if (!result.success) {
29
- throw result.error;
30
- }
31
- const requestId = options.parseRequestId(context);
32
- let item = await this.repository.get(requestId);
33
- if (!item) {
34
- throw ResourceNotFoundError;
35
- }
36
- if (options.allow && !options.allow(context, item)) {
37
- throw ForbiddenError;
38
- }
39
- item = await this.repository.save(
40
- this.factory.create({
41
- ...item,
42
- ...result.data
43
- })
44
- );
45
- return context.json(options.view ? options.view(item) : item);
46
- };
47
- getItem = async (context, options) => {
48
- const requestId = options.parseRequestId(context);
49
- const item = await this.repository.get(requestId);
50
- if (!item) {
51
- throw ResourceNotFoundError;
52
- }
53
- if (options.allow && !options.allow(context, item)) {
54
- throw ForbiddenError;
55
- }
56
- return context.json(options.view ? options.view(item) : item);
57
- };
58
- deleteItem = async (context, options) => {
59
- const requestId = options.parseRequestId(context);
60
- const item = await this.repository.get(requestId);
61
- if (!item) {
62
- throw ResourceNotFoundError;
63
- }
64
- if (options.allow && !options.allow(context, item)) {
65
- throw ForbiddenError;
66
- }
67
- await this.repository.delete(requestId);
68
- return context.json(options.view ? options.view(item) : item);
69
- };
70
- };
71
-
72
- // src/lib/app.ts
73
- import { Hono } from "hono";
74
-
75
- // src/middleware/app.ts
76
- import {
77
- BadRequestError,
78
- InternalServerError,
79
- isHttpError
80
- } from "@opble/types";
81
- import { Timer as Timer2 } from "@opble/toolkit";
82
- import { createMiddleware } from "hono/factory";
83
- import z2, { ZodError } from "zod";
84
-
85
- // src/lib/debugger.ts
86
- import { createDebug } from "@opble/debug";
87
- var debug = createDebug("opble:core");
88
-
89
- // src/lib/logger.ts
90
- import { Timer } from "@opble/toolkit";
91
- import * as winston from "winston";
92
-
93
- // src/lib/env.ts
94
- import { z } from "zod";
95
- var commaSeparatedToArray = (value) => {
96
- if (typeof value === "string") {
97
- const items = value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
98
- return items;
99
- }
100
- return [];
101
- };
102
- var envSchema = z.object({
103
- LOGGER_TRANSPORT_DEFAULT: z.enum(["console", "file"]).default("console"),
104
- LOGGER_TRANSPORT_FILE: z.string().optional(),
105
- APP_BASE_PATH: z.string().optional(),
106
- CORS_ALLOW_ORIGIN: z.preprocess(commaSeparatedToArray, z.array(z.string())).default(["*"]),
107
- CORS_ALLOW_HEADERS: z.preprocess(commaSeparatedToArray, z.array(z.string())).default(["Content-Type", "Authorization"]),
108
- CORS_ALLOW_METHODS: z.preprocess(commaSeparatedToArray, z.array(z.string())).default(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]),
109
- USE_RATE_LIMIT: z.transform((val) => {
110
- if (val === "true" || val === "1") return true;
111
- if (val === "false" || val === "0") return false;
112
- return Boolean(val);
113
- }).default(false),
114
- RATE_LIMIT_WINDOW: z.preprocess((val) => {
115
- const n = Number(val);
116
- return isNaN(n) ? 15 * 60 * 1e3 : n;
117
- }, z.number()).default(15 * 60 * 1e3),
118
- RATE_LIMIT: z.preprocess((val) => {
119
- const n = Number(val);
120
- return isNaN(n) ? 100 : n;
121
- }, z.number()).default(100)
122
- });
123
- var env = envSchema.parse(process.env);
124
-
125
- // src/lib/logger.ts
126
- var createDefaultWinstonTransport = () => {
127
- if (env.LOGGER_TRANSPORT_DEFAULT === "file" && env.LOGGER_TRANSPORT_FILE) {
128
- return new winston.transports.File({
129
- filename: env.LOGGER_TRANSPORT_FILE
130
- });
131
- }
132
- return new winston.transports.Console();
133
- };
134
- var createWinstonLogger = (...transports2) => winston.createLogger({
135
- level: "info",
136
- format: winston.format.combine(
137
- winston.format.splat(),
138
- winston.format.timestamp(),
139
- winston.format.json()
140
- ),
141
- transports: transports2.length ? transports2 : [createDefaultWinstonTransport()]
142
- });
143
- var winstonLogger = createWinstonLogger();
144
- var CoreLogger = class {
145
- logger;
146
- traceId;
147
- metadata;
148
- timer;
149
- constructor(metadata = {}) {
150
- const traceId = crypto.randomUUID();
151
- this.traceId = traceId;
152
- this.metadata = {
153
- traceId,
154
- ...metadata
155
- };
156
- this.logger = winstonLogger.child(this.metadata);
157
- this.timer = new Timer();
158
- }
159
- log(level, message, ...args) {
160
- this.logger.log(level, message, ...args, { duration: this.timer.tock() });
161
- }
162
- debug(message, ...args) {
163
- this.log("debug", message, ...args);
164
- }
165
- info(message, ...args) {
166
- this.log("info", message, ...args);
167
- }
168
- warn(message, ...args) {
169
- this.log("warn", message, ...args);
170
- }
171
- error(message, ...args) {
172
- this.log("error", message, ...args);
173
- }
174
- };
175
- var LoggerFactory = {
176
- create(data) {
177
- return new CoreLogger(data);
178
- }
179
- };
180
- function log(context) {
181
- let logger = context.get("logger");
182
- if (!logger) {
183
- logger = LoggerFactory.create({});
184
- context.set("logger", logger);
185
- }
186
- return logger;
187
- }
188
-
189
- // src/middleware/app.ts
190
- function toHttpError(context, e) {
191
- let err;
192
- if (e instanceof ZodError) {
193
- err = BadRequestError;
194
- err.message = z2.prettifyError(e);
195
- } else if (isHttpError(e)) {
196
- err = e;
197
- } else {
198
- err = InternalServerError;
199
- log(context).error("Catch an error. Details: %s", e);
200
- }
201
- return err;
202
- }
203
- var setUp = createMiddleware(
204
- async (context, next) => {
205
- const timer = new Timer2();
206
- timer.tick();
207
- try {
208
- log(context).info("[start] %s %s", context.req.method, context.req.path);
209
- await next();
210
- log(context).info("[end]", {
211
- status: context.res.status,
212
- duration: timer.tock()
213
- });
214
- } catch (err) {
215
- const response = tearDown(err, context);
216
- log(context).info("[end]", {
217
- status: response.status,
218
- duration: timer.tock()
219
- });
220
- return response;
221
- }
222
- }
223
- );
224
- var tearDown = (err, context) => {
225
- debug("Tear down with error:", err);
226
- const { code, message, status } = toHttpError(context, err);
227
- return context.json({ code, message }, status);
228
- };
229
-
230
- // src/lib/loader.ts
231
- var ModuleLoader = class _ModuleLoader {
232
- modules;
233
- constructor(modules) {
234
- this.modules = modules;
235
- }
236
- static load(app, ...modules) {
237
- const loader = new _ModuleLoader(modules);
238
- loader.modules.forEach((module) => {
239
- module.install(app);
240
- debug(
241
- "Module is loaded => %s",
242
- `${Object.getPrototypeOf(module).constructor.name}`
243
- );
244
- });
245
- }
246
- };
247
-
248
- // src/lib/app.ts
249
- function createApp(...modules) {
250
- let app;
251
- if (env.APP_BASE_PATH) {
252
- app = new Hono().basePath(env.APP_BASE_PATH);
253
- debug(`App base path set to '${env.APP_BASE_PATH}'`);
254
- } else {
255
- app = new Hono();
256
- }
257
- app.use(setUp);
258
- ModuleLoader.load(app, ...modules);
259
- app.onError(tearDown);
260
- return app;
261
- }
262
- var AppFactory = {
263
- create: (data) => {
264
- if (data && Array.isArray(data)) {
265
- return createApp(...data);
266
- }
267
- return new Hono();
268
- }
269
- };
270
-
271
- // src/lib/auth.ts
272
- function getCurrentUser(context) {
273
- const user = context.get("user");
274
- if (!user) {
275
- throw new Error("User not found");
276
- }
277
- return user;
278
- }
279
-
280
- // src/middleware/auth.ts
281
- import {
282
- requireUser,
283
- requireUserRole,
284
- requireUserPermission,
285
- extractBearerToken
286
- } from "@opble/auth0";
287
- import { PublicUserFactory, PublicUserSchema } from "@opble/entity-auth";
288
- import { createMiddleware as createMiddleware2 } from "hono/factory";
289
- var systemUser = PublicUserFactory.create({
290
- id: "system",
291
- name: "System",
292
- email: "system@wewise.net",
293
- role: "admin",
294
- emailVerified: true
295
- });
296
- var toHeaderGetter = (context) => {
297
- return {
298
- get: (name) => context.req.header(name)
299
- };
300
- };
301
- function isAuthenticated(context) {
302
- const user = context.get("user");
303
- if (!user) {
304
- return false;
305
- }
306
- return PublicUserSchema.safeParse(user).success;
307
- }
308
- var auth = createMiddleware2(
309
- async (context, next) => {
310
- if (!isAuthenticated(context)) {
311
- try {
312
- const user = await requireUser(toHeaderGetter(context));
313
- context.set("user", user);
314
- } catch (e) {
315
- return tearDown(e, context);
316
- }
317
- }
318
- await next();
319
- }
320
- );
321
- var authAllowRole = (...roles) => {
322
- return createMiddleware2(async (context, next) => {
323
- if (!isAuthenticated(context)) {
324
- context.set(
325
- "user",
326
- await requireUserRole(toHeaderGetter(context), ...roles)
327
- );
328
- }
329
- await next();
330
- });
331
- };
332
- var authAllowPermission = (...permissions) => {
333
- return createMiddleware2(async (context, next) => {
334
- if (!isAuthenticated(context)) {
335
- context.set(
336
- "user",
337
- await requireUserPermission(toHeaderGetter(context), ...permissions)
338
- );
339
- }
340
- await next();
341
- });
342
- };
343
- var authAllowKeys = (...keys) => {
344
- return createMiddleware2(async (context, next) => {
345
- if (!isAuthenticated(context)) {
346
- const token = extractBearerToken(toHeaderGetter(context));
347
- if (token && keys.includes(token)) {
348
- context.set("user", systemUser);
349
- }
350
- }
351
- await next();
352
- });
353
- };
354
-
355
- // src/lib/constant.ts
356
- var UP = "UP";
357
-
358
- // src/module/service-status.ts
359
- var ServiceStatusModule = class {
360
- options;
361
- constructor(options = {}) {
362
- this.options = options;
363
- }
364
- install(app) {
365
- const handler = new ServiceStatusHandler();
366
- app.get("/health", handler.checkHealth);
367
- }
368
- };
369
- var ServiceStatusHandler = class {
370
- checkHealth = (context) => {
371
- return context.json({
372
- status: UP
373
- });
374
- };
375
- };
376
-
377
- // src/module/cors.ts
378
- import { createDebug as createDebug2 } from "@opble/debug";
379
- import { cors } from "hono/cors";
380
- var wildcardToRegExp = (pattern) => {
381
- const hasScheme = pattern.indexOf("://") !== -1;
382
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, (m) => `\\${m}`);
383
- const wildcardReplaced = escaped.replace(/\*/g, ".*");
384
- const regexStr = hasScheme ? `^${wildcardReplaced}$` : `^https?://${wildcardReplaced}$`;
385
- return new RegExp(regexStr);
386
- };
387
- var debug2 = createDebug2("opble:core:CorsModule");
388
- var CorsModule = class {
389
- install(app) {
390
- debug2("CORS_ALLOW_ORIGIN:", env.CORS_ALLOW_ORIGIN);
391
- app.use(
392
- "/*",
393
- cors({
394
- origin: (origin) => {
395
- for (const allowedOrigin of env.CORS_ALLOW_ORIGIN) {
396
- if (allowedOrigin === "*") {
397
- return origin ?? "";
398
- }
399
- if (origin === allowedOrigin) {
400
- return origin;
401
- }
402
- if (allowedOrigin.indexOf("*") !== -1) {
403
- const re = wildcardToRegExp(allowedOrigin);
404
- if (origin && re.test(origin)) {
405
- return origin;
406
- }
407
- }
408
- }
409
- return "";
410
- },
411
- allowHeaders: env.CORS_ALLOW_HEADERS,
412
- allowMethods: env.CORS_ALLOW_METHODS
413
- })
414
- );
415
- }
416
- };
417
-
418
- // src/module/rate-limiter.ts
419
- import { createDebug as createDebug3 } from "@opble/debug";
420
- import { rateLimiter } from "hono-rate-limiter";
421
- var debug3 = createDebug3("opble:core:rate-limiter");
422
- var RateLimiterModule = class {
423
- constructor(options = { paths: ["/*"] }) {
424
- this.options = options;
425
- }
426
- install(app) {
427
- debug3("Use Rate Limiter:", env.USE_RATE_LIMIT);
428
- if (!env.USE_RATE_LIMIT) {
429
- return;
430
- }
431
- debug3("Installing RateLimiterModule with options:", this.options);
432
- debug3(
433
- `Rate limiting: ${env.RATE_LIMIT} requests per ${env.RATE_LIMIT_WINDOW} ms`
434
- );
435
- const limiter = rateLimiter({
436
- windowMs: env.RATE_LIMIT_WINDOW,
437
- limit: env.RATE_LIMIT,
438
- standardHeaders: "draft-7",
439
- keyGenerator: (c) => c.req.header("x-forwarded-for") ?? "anonymous"
440
- });
441
- for (const path of this.options.paths) {
442
- app.use(path, limiter);
443
- }
444
- }
445
- };
446
- export {
447
- AppFactory,
448
- BaseController,
449
- CorsModule,
450
- CrudController,
451
- LoggerFactory,
452
- ModuleLoader,
453
- RateLimiterModule,
454
- ServiceStatusModule,
455
- auth,
456
- authAllowKeys,
457
- authAllowPermission,
458
- authAllowRole,
459
- getCurrentUser,
460
- log,
461
- setUp,
462
- tearDown
463
- };
1
+ import {BadRequestError,isHttpError,InternalServerError,ResourceNotFoundError,ForbiddenError}from'@opble/types';import {Hono}from'hono';import {Timer}from'@opble/toolkit';import {createMiddleware}from'hono/factory';import V,{z,ZodError}from'zod';import {createDebug}from'@opble/debug';import*as l from'winston';import {requireUser,requireUserRole,requireUserPermission,extractBearerToken}from'@opble/auth0';import {PublicUserFactory,PublicUserSchema}from'@opble/entity-auth';import {cors}from'hono/cors';import {rateLimiter}from'hono-rate-limiter';var g=class{};var L=class extends g{constructor(t,o){super();this.factory=t;this.repository=o;}createItem=async(t,o)=>{let s=await t.req.json(),i=await o.schema.safeParseAsync(s);if(!i.success)throw i.error;let w=await this.repository.save(this.factory.create(i.data));return t.json(o.view?o.view(w):w,201)};updateItem=async(t,o)=>{let s=await t.req.json(),i=await o.schema.safeParseAsync(s);if(!i.success)throw i.error;let w=o.parseRequestId(t),c=await this.repository.get(w);if(!c)throw ResourceNotFoundError;if(o.allow&&!o.allow(t,c))throw ForbiddenError;return c=await this.repository.save(this.factory.create({...c,...i.data})),t.json(o.view?o.view(c):c)};getItem=async(t,o)=>{let s=o.parseRequestId(t),i=await this.repository.get(s);if(!i)throw ResourceNotFoundError;if(o.allow&&!o.allow(t,i))throw ForbiddenError;return t.json(o.view?o.view(i):i)};deleteItem=async(t,o)=>{let s=o.parseRequestId(t),i=await this.repository.get(s);if(!i)throw ResourceNotFoundError;if(o.allow&&!o.allow(t,i))throw ForbiddenError;return await this.repository.delete(s),t.json(o.view?o.view(i):i)}};var u=createDebug("opble:core");var x=r=>typeof r=="string"?r.split(",").map(t=>t.trim()).filter(t=>t.length>0):[],U=z.object({LOGGER_TRANSPORT_DEFAULT:z.enum(["console","file"]).default("console"),LOGGER_TRANSPORT_FILE:z.string().optional(),APP_BASE_PATH:z.string().optional(),CORS_ALLOW_ORIGIN:z.preprocess(x,z.array(z.string())).default(["*"]),CORS_ALLOW_HEADERS:z.preprocess(x,z.array(z.string())).default(["Content-Type","Authorization"]),CORS_ALLOW_METHODS:z.preprocess(x,z.array(z.string())).default(["GET","POST","PUT","PATCH","DELETE","OPTIONS"]),USE_RATE_LIMIT:z.transform(r=>r==="true"||r==="1"?true:r==="false"||r==="0"?false:!!r).default(false),RATE_LIMIT_WINDOW:z.preprocess(r=>{let e=Number(r);return isNaN(e)?900*1e3:e},z.number()).default(900*1e3),RATE_LIMIT:z.preprocess(r=>{let e=Number(r);return isNaN(e)?100:e},z.number()).default(100)}),n=U.parse(process.env);var D=()=>n.LOGGER_TRANSPORT_DEFAULT==="file"&&n.LOGGER_TRANSPORT_FILE?new l.transports.File({filename:n.LOGGER_TRANSPORT_FILE}):new l.transports.Console,k=(...r)=>l.createLogger({level:"info",format:l.format.combine(l.format.splat(),l.format.timestamp(),l.format.json()),transports:r.length?r:[D()]}),q=k(),C=class{logger;traceId;metadata;timer;constructor(e={}){let t=crypto.randomUUID();this.traceId=t,this.metadata={traceId:t,...e},this.logger=q.child(this.metadata),this.timer=new Timer;}log(e,t,...o){this.logger.log(e,t,...o,{duration:this.timer.tock()});}debug(e,...t){this.log("debug",e,...t);}info(e,...t){this.log("info",e,...t);}warn(e,...t){this.log("warn",e,...t);}error(e,...t){this.log("error",e,...t);}},G={create(r){return new C(r)}};function f(r){let e=r.get("logger");return e||(e=G.create({}),r.set("logger",e)),e}function K(r,e){let t;return e instanceof ZodError?(t=BadRequestError,t.message=V.prettifyError(e)):isHttpError(e)?t=e:(t=InternalServerError,f(r).error("Catch an error. Details: %s",e)),t}var v=createMiddleware(async(r,e)=>{let t=new Timer;t.tick();try{f(r).info("[start] %s %s",r.req.method,r.req.path),await e(),f(r).info("[end]",{status:r.res.status,duration:t.tock()});}catch(o){let s=d(o,r);return f(r).info("[end]",{status:s.status,duration:t.tock()}),s}}),d=(r,e)=>{u("Tear down with error:",r);let{code:t,message:o,status:s}=K(e,r);return e.json({code:t,message:o},s)};var h=class r{modules;constructor(e){this.modules=e;}static load(e,...t){new r(t).modules.forEach(s=>{s.install(e),u("Module is loaded => %s",`${Object.getPrototypeOf(s).constructor.name}`);});}};function P(...r){let e;return n.APP_BASE_PATH?(e=new Hono().basePath(n.APP_BASE_PATH),u(`App base path set to '${n.APP_BASE_PATH}'`)):e=new Hono,e.use(v),h.load(e,...r),e.onError(d),e}var $e={create:r=>r&&Array.isArray(r)?P(...r):P()};function Ke(r){let e=r.get("user");if(!e)throw new Error("User not found");return e}var te=PublicUserFactory.create({id:"system",name:"System",email:"system@wewise.net",role:"admin",emailVerified:true}),A=r=>({get:e=>r.req.header(e)});function E(r){let e=r.get("user");return e?PublicUserSchema.safeParse(e).success:false}var ot=createMiddleware(async(r,e)=>{if(!E(r))try{let t=await requireUser(A(r));r.set("user",t);}catch(t){return d(t,r)}await e();}),st=(...r)=>createMiddleware(async(e,t)=>{E(e)||e.set("user",await requireUserRole(A(e),...r)),await t();}),nt=(...r)=>createMiddleware(async(e,t)=>{E(e)||e.set("user",await requireUserPermission(A(e),...r)),await t();}),it=(...r)=>createMiddleware(async(e,t)=>{if(!E(e)){let o=extractBearerToken(A(e));o&&r.includes(o)&&e.set("user",te);}await t();});var M=class{options;constructor(e={}){this.options=e;}install(e){let t=new I;e.get("/health",t.checkHealth);}},I=class{checkHealth=e=>e.json({status:"UP"})};var ne=r=>{let e=r.indexOf("://")!==-1,o=r.replace(/[.+?^${}()|[\]\\]/g,i=>`\\${i}`).replace(/\*/g,".*"),s=e?`^${o}$`:`^https?://${o}$`;return new RegExp(s)},ie=createDebug("opble:core:CorsModule"),S=class{install(e){ie("CORS_ALLOW_ORIGIN:",n.CORS_ALLOW_ORIGIN),e.use("/*",cors({origin:t=>{for(let o of n.CORS_ALLOW_ORIGIN){if(o==="*")return t??"";if(t===o)return t;if(o.indexOf("*")!==-1){let s=ne(o);if(t&&s.test(t))return t}}return ""},allowHeaders:n.CORS_ALLOW_HEADERS,allowMethods:n.CORS_ALLOW_METHODS}));}};var T=createDebug("opble:core:rate-limiter"),b=class{constructor(e={paths:["/*"]}){this.options=e;}install(e){if(T("Use Rate Limiter:",n.USE_RATE_LIMIT),!n.USE_RATE_LIMIT)return;T("Installing RateLimiterModule with options:",this.options),T(`Rate limiting: ${n.RATE_LIMIT} requests per ${n.RATE_LIMIT_WINDOW} ms`);let t=rateLimiter({windowMs:n.RATE_LIMIT_WINDOW,limit:n.RATE_LIMIT,standardHeaders:"draft-7",keyGenerator:o=>o.req.header("x-forwarded-for")??"anonymous"});for(let o of this.options.paths)e.use(o,t);}};
2
+ export{$e as AppFactory,g as BaseController,S as CorsModule,L as CrudController,G as LoggerFactory,h as ModuleLoader,b as RateLimiterModule,M as ServiceStatusModule,ot as auth,it as authAllowKeys,nt as authAllowPermission,st as authAllowRole,Ke as getCurrentUser,f as log,v as setUp,d as tearDown};//# sourceMappingURL=index.js.map
464
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/controller/base.ts","../src/controller/crud.ts","../src/lib/app.ts","../src/middleware/app.ts","../src/lib/debugger.ts","../src/lib/logger.ts","../src/lib/env.ts","../src/lib/loader.ts","../src/lib/auth.ts","../src/middleware/auth.ts","../src/lib/constant.ts","../src/module/service-status.ts","../src/module/cors.ts","../src/module/rate-limiter.ts"],"sourcesContent":["export class BaseController {}\n","import {\n Factory,\n ForbiddenError,\n HashMap,\n ResourceNotFoundError,\n} from '@opble/types';\nimport { z } from 'zod';\n\nimport { AppContext } from '../lib/types';\n\nimport { BaseController } from './base';\nimport { DynamoDBRepository, TableKey } from '@opble/repository-dynamodb';\n\ntype View = (data: unknown) => HashMap;\n\ntype BaseOptions = {\n view?: View;\n};\n\ntype PayloadOptions = BaseOptions & {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema: z.ZodType<unknown, unknown, any>;\n};\n\ntype RequestIdOptions = {\n parseRequestId: (context: AppContext) => TableKey;\n};\n\ntype ResourcePolicyOptions<T extends HashMap> = {\n allow?: (context: AppContext, item: T) => boolean;\n};\n\ntype GetItemOptions = BaseOptions & RequestIdOptions;\ntype DeleteItemOptions = GetItemOptions;\n\ntype CreateItemOptions = PayloadOptions;\ntype UpdateItemOptions = PayloadOptions & RequestIdOptions;\n\nexport class CrudController<T extends HashMap> extends BaseController {\n constructor(\n protected factory: Factory<T>,\n private repository: DynamoDBRepository<T>\n ) {\n super();\n }\n\n createItem = async (context: AppContext, options: CreateItemOptions) => {\n const body = await context.req.json();\n const result = await options.schema.safeParseAsync(body);\n if (!result.success) {\n throw result.error;\n }\n\n const item = await this.repository.save(this.factory.create(result.data));\n\n return context.json(options.view ? options.view(item) : item, 201);\n };\n\n updateItem = async (\n context: AppContext,\n options: UpdateItemOptions & ResourcePolicyOptions<T>\n ) => {\n const body = await context.req.json();\n const result = await options.schema.safeParseAsync(body);\n if (!result.success) {\n throw result.error;\n }\n\n const requestId = options.parseRequestId(context);\n let item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n item = await this.repository.save(\n this.factory.create({\n ...item,\n ...result.data,\n })\n );\n\n return context.json(options.view ? options.view(item) : item);\n };\n\n getItem = async (\n context: AppContext,\n options: GetItemOptions & ResourcePolicyOptions<T>\n ) => {\n const requestId = options.parseRequestId(context);\n const item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n return context.json(options.view ? options.view(item) : item);\n };\n\n deleteItem = async (\n context: AppContext,\n options: DeleteItemOptions & ResourcePolicyOptions<T>\n ) => {\n const requestId = options.parseRequestId(context);\n const item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n await this.repository.delete(requestId);\n return context.json(options.view ? options.view(item) : item);\n };\n}\n","import { Factory } from '@opble/types';\nimport { Hono } from 'hono';\n\nimport { setUp, tearDown } from '../middleware/app';\n\nimport { debug } from './debugger';\nimport { env } from './env';\nimport { Module, ModuleLoader } from './loader';\nimport { App, CoreEnv } from './types';\n\nfunction createApp(...modules: Module[]) {\n let app: App;\n if (env.APP_BASE_PATH) {\n app = new Hono<CoreEnv>().basePath(env.APP_BASE_PATH);\n debug(`App base path set to '${env.APP_BASE_PATH}'`);\n } else {\n app = new Hono<CoreEnv>();\n }\n\n app.use(setUp);\n ModuleLoader.load(app, ...modules);\n app.onError(tearDown);\n\n return app;\n}\n\nexport const AppFactory: Factory<App> = {\n create: (data: unknown) => {\n if (data && Array.isArray(data)) {\n return createApp(...data);\n }\n\n return new Hono<CoreEnv>();\n },\n};\n","import {\n BadRequestError,\n HttpError,\n InternalServerError,\n isHttpError,\n} from '@opble/types';\nimport { Timer } from '@opble/toolkit';\nimport { Next } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { ContentfulStatusCode } from 'hono/utils/http-status';\nimport z, { ZodError } from 'zod';\n\nimport { debug } from '../lib/debugger.js';\nimport { log } from '../lib/logger';\nimport { AppContext } from '../lib/types';\n\nfunction toHttpError(context: AppContext, e: unknown): HttpError {\n let err: HttpError;\n if (e instanceof ZodError) {\n err = BadRequestError;\n err.message = z.prettifyError(e);\n } else if (isHttpError(e)) {\n err = e as HttpError;\n } else {\n err = InternalServerError;\n log(context).error('Catch an error. Details: %s', e);\n }\n\n return err;\n}\n\nexport const setUp = createMiddleware(\n async (context: AppContext, next: Next) => {\n const timer = new Timer();\n timer.tick();\n\n try {\n log(context).info('[start] %s %s', context.req.method, context.req.path);\n await next();\n log(context).info('[end]', {\n status: context.res.status,\n duration: timer.tock(),\n });\n } catch (err) {\n const response = tearDown(err, context);\n log(context).info('[end]', {\n status: response.status,\n duration: timer.tock(),\n });\n\n return response;\n }\n }\n);\n\nexport const tearDown = (err: unknown, context: AppContext) => {\n debug('Tear down with error:', err);\n const { code, message, status } = toHttpError(context, err);\n return context.json({ code, message }, status as ContentfulStatusCode);\n};\n","import { createDebug } from '@opble/debug';\n\nexport const debug = createDebug('opble:core');\n","import { Factory, StringHashMap } from '@opble/types';\nimport { Logger } from '@opble/types';\nimport { Timer } from '@opble/toolkit';\nimport { Context } from 'hono';\nimport * as winston from 'winston';\n\nimport { env } from './env';\nimport { CoreEnv } from './types';\n\nconst createDefaultWinstonTransport = (): winston.transport => {\n if (env.LOGGER_TRANSPORT_DEFAULT === 'file' && env.LOGGER_TRANSPORT_FILE) {\n return new winston.transports.File({\n filename: env.LOGGER_TRANSPORT_FILE,\n });\n }\n\n return new winston.transports.Console();\n};\n\nconst createWinstonLogger = (...transports: winston.transport[]) =>\n winston.createLogger({\n level: 'info',\n format: winston.format.combine(\n winston.format.splat(),\n winston.format.timestamp(),\n winston.format.json()\n ),\n transports: transports.length\n ? transports\n : [createDefaultWinstonTransport()],\n });\n\nconst winstonLogger: winston.Logger = createWinstonLogger();\n\nclass CoreLogger implements Logger {\n private readonly logger: winston.Logger;\n private readonly traceId: string;\n private metadata: StringHashMap;\n private readonly timer: Timer;\n\n constructor(metadata: StringHashMap = {}) {\n const traceId = crypto.randomUUID();\n this.traceId = traceId;\n this.metadata = {\n traceId,\n ...metadata,\n };\n this.logger = winstonLogger.child(this.metadata);\n this.timer = new Timer();\n }\n\n private log(level: string, message: string, ...args: unknown[]): void {\n this.logger.log(level, message, ...args, { duration: this.timer.tock() });\n }\n\n debug(message: string, ...args: unknown[]): void {\n this.log('debug', message, ...args);\n }\n\n info(message: string, ...args: unknown[]): void {\n this.log('info', message, ...args);\n }\n\n warn(message: string, ...args: unknown[]): void {\n this.log('warn', message, ...args);\n }\n\n error(message: string, ...args: unknown[]): void {\n this.log('error', message, ...args);\n }\n}\n\nexport const LoggerFactory: Factory<Logger> = {\n create(data: unknown): Logger {\n return new CoreLogger(data as StringHashMap);\n },\n};\n\nexport function log(context: Context<CoreEnv>): Logger {\n let logger = context.get('logger');\n if (!logger) {\n logger = LoggerFactory.create({});\n context.set('logger', logger);\n }\n\n return logger;\n}\n","import { z } from 'zod';\n\nconst commaSeparatedToArray = (value: unknown) => {\n if (typeof value === 'string') {\n const items = value\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return items;\n }\n\n // If undefined or not a string, return empty array by default\n return [] as string[];\n};\n\nconst envSchema = z.object({\n LOGGER_TRANSPORT_DEFAULT: z.enum(['console', 'file']).default('console'),\n LOGGER_TRANSPORT_FILE: z.string().optional(),\n\n APP_BASE_PATH: z.string().optional(),\n\n CORS_ALLOW_ORIGIN: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['*']),\n CORS_ALLOW_HEADERS: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['Content-Type', 'Authorization']),\n CORS_ALLOW_METHODS: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']),\n\n USE_RATE_LIMIT: z\n .transform((val) => {\n if (val === 'true' || val === '1') return true;\n if (val === 'false' || val === '0') return false;\n return Boolean(val);\n })\n .default(false),\n RATE_LIMIT_WINDOW: z\n .preprocess((val) => {\n const n = Number(val);\n return isNaN(n) ? 15 * 60 * 1000 : n; // default 15 minutes\n }, z.number())\n .default(15 * 60 * 1000),\n RATE_LIMIT: z\n .preprocess((val) => {\n const n = Number(val);\n return isNaN(n) ? 100 : n; // default 100 requests\n }, z.number())\n .default(100),\n});\n\nexport const env = envSchema.parse(process.env);\n","import { debug } from './debugger.js';\nimport { App } from './types';\n\nexport interface Module {\n install(app: App): void;\n}\n\nexport class ModuleLoader {\n private modules: Module[];\n\n constructor(modules: Module[]) {\n this.modules = modules;\n }\n\n static load(app: App, ...modules: Module[]) {\n const loader = new ModuleLoader(modules);\n loader.modules.forEach((module) => {\n module.install(app);\n debug(\n 'Module is loaded => %s',\n `${Object.getPrototypeOf(module).constructor.name}`\n );\n });\n }\n}\n","import { PublicUser } from '@opble/entity-auth';\nimport { Context } from 'hono';\n\nimport { CoreEnv } from './types';\n\nexport function getCurrentUser(context: Context<CoreEnv>): PublicUser {\n const user = context.get('user');\n if (!user) {\n throw new Error('User not found');\n }\n return user;\n}\n","import {\n requireUser,\n requireUserRole,\n requireUserPermission,\n extractBearerToken,\n} from '@opble/auth0';\nimport { PublicUserFactory, PublicUserSchema } from '@opble/entity-auth';\nimport { Context, Next } from 'hono';\nimport { createMiddleware } from 'hono/factory';\n\nimport { CoreEnv } from '../lib/types';\n\nimport { tearDown } from './app';\n\nconst systemUser = PublicUserFactory.create({\n id: 'system',\n name: 'System',\n email: 'system@wewise.net',\n role: 'admin',\n emailVerified: true,\n});\nconst toHeaderGetter = (context: Context<CoreEnv>) => {\n return {\n get: (name: string) => context.req.header(name),\n };\n};\n\nfunction isAuthenticated(context: Context<CoreEnv>): boolean {\n const user = context.get('user');\n if (!user) {\n return false;\n }\n\n return PublicUserSchema.safeParse(user).success;\n}\n\nexport const auth = createMiddleware(\n async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n try {\n const user = await requireUser(toHeaderGetter(context));\n context.set('user', user);\n } catch (e) {\n return tearDown(e, context);\n }\n }\n\n await next();\n }\n);\n\nexport const authAllowRole = (...roles: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n context.set(\n 'user',\n await requireUserRole(toHeaderGetter(context), ...roles)\n );\n }\n\n await next();\n });\n};\n\nexport const authAllowPermission = (...permissions: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n context.set(\n 'user',\n await requireUserPermission(toHeaderGetter(context), ...permissions)\n );\n }\n\n await next();\n });\n};\n\nexport const authAllowKeys = (...keys: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n const token = extractBearerToken(toHeaderGetter(context));\n if (token && keys.includes(token)) {\n context.set('user', systemUser);\n }\n }\n\n await next();\n });\n};\n","export const UP = 'UP';\n","import { HashMap } from '@opble/types';\n\nimport { UP } from '../lib/constant';\nimport { Module } from '../lib/loader';\nimport { App, AppContext } from '../lib/types';\n\nexport type ServiceStatusOptions = HashMap;\n\nexport class ServiceStatusModule implements Module {\n private options: ServiceStatusOptions;\n\n constructor(options: ServiceStatusOptions = {}) {\n this.options = options;\n }\n\n install(app: App) {\n const handler = new ServiceStatusHandler();\n app.get('/health', handler.checkHealth);\n }\n}\n\nclass ServiceStatusHandler {\n checkHealth = (context: AppContext) => {\n return context.json({\n status: UP,\n });\n };\n}\n","import { createDebug } from '@opble/debug';\nimport { cors } from 'hono/cors';\n\nimport { env } from '../lib/env';\nimport { Module } from '../lib/loader';\nimport { App } from '../lib/types';\n\n// Convert wildcard pattern like \"https://*.wewise.net\" into a RegExp\nconst wildcardToRegExp = (pattern: string): RegExp => {\n // If pattern doesn't include scheme, allow http(s)\n const hasScheme = pattern.indexOf('://') !== -1;\n // Escape regexp special chars except '*'\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, (m) => `\\\\${m}`);\n // Now replace escaped '*' (which would be '\\*') with '.*'\n const wildcardReplaced = escaped.replace(/\\*/g, '.*');\n const regexStr = hasScheme\n ? `^${wildcardReplaced}$`\n : `^https?://${wildcardReplaced}$`;\n return new RegExp(regexStr);\n};\n\nconst debug = createDebug('opble:core:CorsModule');\n\nexport class CorsModule implements Module {\n install(app: App) {\n debug('CORS_ALLOW_ORIGIN:', env.CORS_ALLOW_ORIGIN);\n app.use(\n '/*',\n cors({\n origin: (origin) => {\n for (const allowedOrigin of env.CORS_ALLOW_ORIGIN) {\n if (allowedOrigin === '*') {\n return origin ?? '';\n }\n\n // Direct match\n if (origin === allowedOrigin) {\n return origin;\n }\n\n // Wildcard matching\n if (allowedOrigin.indexOf('*') !== -1) {\n const re = wildcardToRegExp(allowedOrigin);\n if (origin && re.test(origin)) {\n return origin;\n }\n }\n }\n\n return '';\n },\n allowHeaders: env.CORS_ALLOW_HEADERS,\n allowMethods: env.CORS_ALLOW_METHODS,\n })\n );\n }\n}\n","import { createDebug } from '@opble/debug';\nimport { rateLimiter } from 'hono-rate-limiter';\n\nimport { env } from '../lib/env';\nimport { Module } from '../lib/loader';\nimport { App } from '../lib/types';\n\nconst debug = createDebug('opble:core:rate-limiter');\n\nexport type RateLimiterModuleOptions = {\n paths: string[];\n};\n\nexport class RateLimiterModule implements Module {\n constructor(private options: RateLimiterModuleOptions = { paths: ['/*'] }) {}\n\n install(app: App) {\n debug('Use Rate Limiter:', env.USE_RATE_LIMIT);\n if (!env.USE_RATE_LIMIT) {\n return;\n }\n\n debug('Installing RateLimiterModule with options:', this.options);\n debug(\n `Rate limiting: ${env.RATE_LIMIT} requests per ${env.RATE_LIMIT_WINDOW} ms`\n );\n\n /**\n * Notes:\n * - The rate limiter is applied to the specified paths.\n * - The limit and window are configurable via environment variables.\n * - The key generator uses the \"x-forwarded-for\" header to identify clients behind proxies.\n *\n * Important:\n * - RateLimiterModule must be installed before other modules so it could be executed first.\n */\n const limiter = rateLimiter({\n windowMs: env.RATE_LIMIT_WINDOW,\n limit: env.RATE_LIMIT,\n standardHeaders: 'draft-7',\n keyGenerator: (c) => c.req.header('x-forwarded-for') ?? 'anonymous',\n });\n for (const path of this.options.paths) {\n app.use(path, limiter);\n }\n }\n}\n"],"mappings":"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","names":["Timer","z","transports","z","Timer","createMiddleware","createMiddleware","createDebug","debug","createDebug","createDebug","debug","createDebug"]}
1
+ {"version":3,"sources":["../src/controller/base.ts","../src/controller/crud.ts","../src/lib/debugger.ts","../src/lib/env.ts","../src/lib/logger.ts","../src/middleware/app.ts","../src/lib/loader.ts","../src/lib/app.ts","../src/lib/auth.ts","../src/middleware/auth.ts","../src/module/service-status.ts","../src/module/cors.ts","../src/module/rate-limiter.ts"],"names":["BaseController","CrudController","factory","repository","context","options","body","result","item","requestId","ResourceNotFoundError","ForbiddenError","debug","createDebug","commaSeparatedToArray","value","s","envSchema","z","val","n","env","createDefaultWinstonTransport","createWinstonLogger","transports","winstonLogger","CoreLogger","metadata","traceId","Timer","level","message","args","LoggerFactory","data","log","logger","toHttpError","err","ZodError","BadRequestError","isHttpError","InternalServerError","setUp","createMiddleware","next","timer","response","tearDown","code","status","ModuleLoader","_ModuleLoader","modules","app","module","createApp","Hono","AppFactory","getCurrentUser","user","systemUser","PublicUserFactory","toHeaderGetter","name","isAuthenticated","PublicUserSchema","auth","requireUser","e","authAllowRole","roles","requireUserRole","authAllowPermission","permissions","requireUserPermission","authAllowKeys","keys","token","extractBearerToken","ServiceStatusModule","handler","ServiceStatusHandler","wildcardToRegExp","pattern","hasScheme","wildcardReplaced","m","regexStr","CorsModule","cors","origin","allowedOrigin","re","RateLimiterModule","limiter","rateLimiter","c","path"],"mappings":"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","file":"index.js","sourcesContent":["export class BaseController {}\n","import { DynamoDBRepository, TableKey } from '@opble/repository-dynamodb';\nimport {\n Factory,\n ForbiddenError,\n HashMap,\n ResourceNotFoundError,\n} from '@opble/types';\nimport { z } from 'zod';\n\nimport { AppContext } from '../lib/types';\n\nimport { BaseController } from './base';\n\ntype View = (data: unknown) => HashMap;\n\ntype BaseOptions = {\n view?: View;\n};\n\ntype PayloadOptions = BaseOptions & {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema: z.ZodType<unknown, unknown, any>;\n};\n\ntype RequestIdOptions = {\n parseRequestId: (context: AppContext) => TableKey;\n};\n\ntype ResourcePolicyOptions<T extends HashMap> = {\n allow?: (context: AppContext, item: T) => boolean;\n};\n\ntype GetItemOptions = BaseOptions & RequestIdOptions;\ntype DeleteItemOptions = GetItemOptions;\n\ntype CreateItemOptions = PayloadOptions;\ntype UpdateItemOptions = PayloadOptions & RequestIdOptions;\n\nexport class CrudController<T extends HashMap> extends BaseController {\n constructor(\n protected factory: Factory<T>,\n private repository: DynamoDBRepository<T>\n ) {\n super();\n }\n\n createItem = async (context: AppContext, options: CreateItemOptions) => {\n const body = await context.req.json();\n const result = await options.schema.safeParseAsync(body);\n if (!result.success) {\n throw result.error;\n }\n\n const item = await this.repository.save(this.factory.create(result.data));\n\n return context.json(options.view ? options.view(item) : item, 201);\n };\n\n updateItem = async (\n context: AppContext,\n options: UpdateItemOptions & ResourcePolicyOptions<T>\n ) => {\n const body = await context.req.json();\n const result = await options.schema.safeParseAsync(body);\n if (!result.success) {\n throw result.error;\n }\n\n const requestId = options.parseRequestId(context);\n let item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n item = await this.repository.save(\n this.factory.create({\n ...item,\n ...result.data,\n })\n );\n\n return context.json(options.view ? options.view(item) : item);\n };\n\n getItem = async (\n context: AppContext,\n options: GetItemOptions & ResourcePolicyOptions<T>\n ) => {\n const requestId = options.parseRequestId(context);\n const item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n return context.json(options.view ? options.view(item) : item);\n };\n\n deleteItem = async (\n context: AppContext,\n options: DeleteItemOptions & ResourcePolicyOptions<T>\n ) => {\n const requestId = options.parseRequestId(context);\n const item = await this.repository.get(requestId);\n if (!item) {\n throw ResourceNotFoundError;\n }\n if (options.allow && !options.allow(context, item)) {\n throw ForbiddenError;\n }\n\n await this.repository.delete(requestId);\n return context.json(options.view ? options.view(item) : item);\n };\n}\n","import { createDebug } from '@opble/debug';\n\nexport const debug = createDebug('opble:core');\n","import { z } from 'zod';\n\nconst commaSeparatedToArray = (value: unknown) => {\n if (typeof value === 'string') {\n const items = value\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return items;\n }\n\n // If undefined or not a string, return empty array by default\n return [] as string[];\n};\n\nconst envSchema = z.object({\n LOGGER_TRANSPORT_DEFAULT: z.enum(['console', 'file']).default('console'),\n LOGGER_TRANSPORT_FILE: z.string().optional(),\n\n APP_BASE_PATH: z.string().optional(),\n\n CORS_ALLOW_ORIGIN: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['*']),\n CORS_ALLOW_HEADERS: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['Content-Type', 'Authorization']),\n CORS_ALLOW_METHODS: z\n .preprocess(commaSeparatedToArray, z.array(z.string()))\n .default(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']),\n\n USE_RATE_LIMIT: z\n .transform((val) => {\n if (val === 'true' || val === '1') return true;\n if (val === 'false' || val === '0') return false;\n return Boolean(val);\n })\n .default(false),\n RATE_LIMIT_WINDOW: z\n .preprocess((val) => {\n const n = Number(val);\n return isNaN(n) ? 15 * 60 * 1000 : n; // default 15 minutes\n }, z.number())\n .default(15 * 60 * 1000),\n RATE_LIMIT: z\n .preprocess((val) => {\n const n = Number(val);\n return isNaN(n) ? 100 : n; // default 100 requests\n }, z.number())\n .default(100),\n});\n\nexport const env = envSchema.parse(process.env);\n","import { Timer } from '@opble/toolkit';\nimport { Factory, StringHashMap } from '@opble/types';\nimport { Logger } from '@opble/types';\nimport { Context } from 'hono';\nimport * as winston from 'winston';\n\nimport { env } from './env';\nimport { CoreEnv } from './types';\n\nconst createDefaultWinstonTransport = (): winston.transport => {\n if (env.LOGGER_TRANSPORT_DEFAULT === 'file' && env.LOGGER_TRANSPORT_FILE) {\n return new winston.transports.File({\n filename: env.LOGGER_TRANSPORT_FILE,\n });\n }\n\n return new winston.transports.Console();\n};\n\nconst createWinstonLogger = (...transports: winston.transport[]) =>\n winston.createLogger({\n level: 'info',\n format: winston.format.combine(\n winston.format.splat(),\n winston.format.timestamp(),\n winston.format.json()\n ),\n transports: transports.length\n ? transports\n : [createDefaultWinstonTransport()],\n });\n\nconst winstonLogger: winston.Logger = createWinstonLogger();\n\nclass CoreLogger implements Logger {\n private readonly logger: winston.Logger;\n private readonly traceId: string;\n private metadata: StringHashMap;\n private readonly timer: Timer;\n\n constructor(metadata: StringHashMap = {}) {\n const traceId = crypto.randomUUID();\n this.traceId = traceId;\n this.metadata = {\n traceId,\n ...metadata,\n };\n this.logger = winstonLogger.child(this.metadata);\n this.timer = new Timer();\n }\n\n private log(level: string, message: string, ...args: unknown[]): void {\n this.logger.log(level, message, ...args, { duration: this.timer.tock() });\n }\n\n debug(message: string, ...args: unknown[]): void {\n this.log('debug', message, ...args);\n }\n\n info(message: string, ...args: unknown[]): void {\n this.log('info', message, ...args);\n }\n\n warn(message: string, ...args: unknown[]): void {\n this.log('warn', message, ...args);\n }\n\n error(message: string, ...args: unknown[]): void {\n this.log('error', message, ...args);\n }\n}\n\nexport const LoggerFactory: Factory<Logger> = {\n create(data: unknown): Logger {\n return new CoreLogger(data as StringHashMap);\n },\n};\n\nexport function log(context: Context<CoreEnv>): Logger {\n let logger = context.get('logger');\n if (!logger) {\n logger = LoggerFactory.create({});\n context.set('logger', logger);\n }\n\n return logger;\n}\n","import { Timer } from '@opble/toolkit';\nimport {\n BadRequestError,\n HttpError,\n InternalServerError,\n isHttpError,\n} from '@opble/types';\nimport { Next } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { ContentfulStatusCode } from 'hono/utils/http-status';\nimport z, { ZodError } from 'zod';\n\nimport { debug } from '../lib/debugger.js';\nimport { log } from '../lib/logger';\nimport { AppContext } from '../lib/types';\n\nfunction toHttpError(context: AppContext, e: unknown): HttpError {\n let err: HttpError;\n if (e instanceof ZodError) {\n err = BadRequestError;\n err.message = z.prettifyError(e);\n } else if (isHttpError(e)) {\n err = e as HttpError;\n } else {\n err = InternalServerError;\n log(context).error('Catch an error. Details: %s', e);\n }\n\n return err;\n}\n\nexport const setUp = createMiddleware(\n async (context: AppContext, next: Next) => {\n const timer = new Timer();\n timer.tick();\n\n try {\n log(context).info('[start] %s %s', context.req.method, context.req.path);\n await next();\n log(context).info('[end]', {\n status: context.res.status,\n duration: timer.tock(),\n });\n } catch (err) {\n const response = tearDown(err, context);\n log(context).info('[end]', {\n status: response.status,\n duration: timer.tock(),\n });\n\n return response;\n }\n }\n);\n\nexport const tearDown = (err: unknown, context: AppContext) => {\n debug('Tear down with error:', err);\n const { code, message, status } = toHttpError(context, err);\n return context.json({ code, message }, status as ContentfulStatusCode);\n};\n","import { debug } from './debugger.js';\nimport { App } from './types';\n\nexport interface Module {\n install(app: App): void;\n}\n\nexport class ModuleLoader {\n private modules: Module[];\n\n constructor(modules: Module[]) {\n this.modules = modules;\n }\n\n static load(app: App, ...modules: Module[]) {\n const loader = new ModuleLoader(modules);\n loader.modules.forEach((module) => {\n module.install(app);\n debug(\n 'Module is loaded => %s',\n `${Object.getPrototypeOf(module).constructor.name}`\n );\n });\n }\n}\n","import { Factory } from '@opble/types';\nimport { Hono } from 'hono';\n\nimport { setUp, tearDown } from '../middleware/app';\n\nimport { debug } from './debugger';\nimport { env } from './env';\nimport { Module, ModuleLoader } from './loader';\nimport { App, CoreEnv } from './types';\n\nfunction createApp(...modules: Module[]): App {\n let app;\n if (env.APP_BASE_PATH) {\n app = new Hono<CoreEnv>().basePath(env.APP_BASE_PATH);\n debug(`App base path set to '${env.APP_BASE_PATH}'`);\n } else {\n app = new Hono<CoreEnv>();\n }\n\n app.use(setUp);\n ModuleLoader.load(app, ...modules);\n app.onError(tearDown);\n\n return app;\n}\n\nexport const AppFactory: Factory<App> = {\n create: (data: unknown): App => {\n if (data && Array.isArray(data)) {\n return createApp(...data);\n }\n\n return createApp();\n },\n};\n","import { PublicUser } from '@opble/entity-auth';\nimport { Context } from 'hono';\n\nimport { CoreEnv } from './types';\n\nexport function getCurrentUser(context: Context<CoreEnv>): PublicUser {\n const user = context.get('user');\n if (!user) {\n throw new Error('User not found');\n }\n return user;\n}\n","import {\n requireUser,\n requireUserRole,\n requireUserPermission,\n extractBearerToken,\n} from '@opble/auth0';\nimport { PublicUserFactory, PublicUserSchema } from '@opble/entity-auth';\nimport { Context, Next } from 'hono';\nimport { createMiddleware } from 'hono/factory';\n\nimport { CoreEnv } from '../lib/types';\n\nimport { tearDown } from './app';\n\nconst systemUser = PublicUserFactory.create({\n id: 'system',\n name: 'System',\n email: 'system@wewise.net',\n role: 'admin',\n emailVerified: true,\n});\nconst toHeaderGetter = (context: Context<CoreEnv>) => {\n return {\n get: (name: string) => context.req.header(name),\n };\n};\n\nfunction isAuthenticated(context: Context<CoreEnv>): boolean {\n const user = context.get('user');\n if (!user) {\n return false;\n }\n\n return PublicUserSchema.safeParse(user).success;\n}\n\nexport const auth = createMiddleware(\n async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n try {\n const user = await requireUser(toHeaderGetter(context));\n context.set('user', user);\n } catch (e) {\n return tearDown(e, context);\n }\n }\n\n await next();\n }\n);\n\nexport const authAllowRole = (...roles: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n context.set(\n 'user',\n await requireUserRole(toHeaderGetter(context), ...roles)\n );\n }\n\n await next();\n });\n};\n\nexport const authAllowPermission = (...permissions: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n context.set(\n 'user',\n await requireUserPermission(toHeaderGetter(context), ...permissions)\n );\n }\n\n await next();\n });\n};\n\nexport const authAllowKeys = (...keys: string[]) => {\n return createMiddleware(async (context: Context<CoreEnv>, next: Next) => {\n if (!isAuthenticated(context)) {\n const token = extractBearerToken(toHeaderGetter(context));\n if (token && keys.includes(token)) {\n context.set('user', systemUser);\n }\n }\n\n await next();\n });\n};\n","import { HashMap } from '@opble/types';\n\nimport { UP } from '../lib/constant';\nimport { Module } from '../lib/loader';\nimport { App, AppContext } from '../lib/types';\n\nexport type ServiceStatusOptions = HashMap;\n\nexport class ServiceStatusModule implements Module {\n private options: ServiceStatusOptions;\n\n constructor(options: ServiceStatusOptions = {}) {\n this.options = options;\n }\n\n install(app: App) {\n const handler = new ServiceStatusHandler();\n app.get('/health', handler.checkHealth);\n }\n}\n\nclass ServiceStatusHandler {\n checkHealth = (context: AppContext) => {\n return context.json({\n status: UP,\n });\n };\n}\n","import { createDebug } from '@opble/debug';\nimport { cors } from 'hono/cors';\n\nimport { env } from '../lib/env';\nimport { Module } from '../lib/loader';\nimport { App } from '../lib/types';\n\n// Convert wildcard pattern like \"https://*.wewise.net\" into a RegExp\nconst wildcardToRegExp = (pattern: string): RegExp => {\n // If pattern doesn't include scheme, allow http(s)\n const hasScheme = pattern.indexOf('://') !== -1;\n // Escape regexp special chars except '*'\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, (m) => `\\\\${m}`);\n // Now replace escaped '*' (which would be '\\*') with '.*'\n const wildcardReplaced = escaped.replace(/\\*/g, '.*');\n const regexStr = hasScheme\n ? `^${wildcardReplaced}$`\n : `^https?://${wildcardReplaced}$`;\n return new RegExp(regexStr);\n};\n\nconst debug = createDebug('opble:core:CorsModule');\n\nexport class CorsModule implements Module {\n install(app: App) {\n debug('CORS_ALLOW_ORIGIN:', env.CORS_ALLOW_ORIGIN);\n app.use(\n '/*',\n cors({\n origin: (origin) => {\n for (const allowedOrigin of env.CORS_ALLOW_ORIGIN) {\n if (allowedOrigin === '*') {\n return origin ?? '';\n }\n\n // Direct match\n if (origin === allowedOrigin) {\n return origin;\n }\n\n // Wildcard matching\n if (allowedOrigin.indexOf('*') !== -1) {\n const re = wildcardToRegExp(allowedOrigin);\n if (origin && re.test(origin)) {\n return origin;\n }\n }\n }\n\n return '';\n },\n allowHeaders: env.CORS_ALLOW_HEADERS,\n allowMethods: env.CORS_ALLOW_METHODS,\n })\n );\n }\n}\n","import { createDebug } from '@opble/debug';\nimport { rateLimiter } from 'hono-rate-limiter';\n\nimport { env } from '../lib/env';\nimport { Module } from '../lib/loader';\nimport { App } from '../lib/types';\n\nconst debug = createDebug('opble:core:rate-limiter');\n\nexport type RateLimiterModuleOptions = {\n paths: string[];\n};\n\nexport class RateLimiterModule implements Module {\n constructor(private options: RateLimiterModuleOptions = { paths: ['/*'] }) {}\n\n install(app: App) {\n debug('Use Rate Limiter:', env.USE_RATE_LIMIT);\n if (!env.USE_RATE_LIMIT) {\n return;\n }\n\n debug('Installing RateLimiterModule with options:', this.options);\n debug(\n `Rate limiting: ${env.RATE_LIMIT} requests per ${env.RATE_LIMIT_WINDOW} ms`\n );\n\n /**\n * Notes:\n * - The rate limiter is applied to the specified paths.\n * - The limit and window are configurable via environment variables.\n * - The key generator uses the \"x-forwarded-for\" header to identify clients behind proxies.\n *\n * Important:\n * - RateLimiterModule must be installed before other modules so it could be executed first.\n */\n const limiter = rateLimiter({\n windowMs: env.RATE_LIMIT_WINDOW,\n limit: env.RATE_LIMIT,\n standardHeaders: 'draft-7',\n keyGenerator: (c) => c.req.header('x-forwarded-for') ?? 'anonymous',\n });\n for (const path of this.options.paths) {\n app.use(path, limiter);\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@opble/core",
3
3
  "description": "Provide core utilities and core components.",
4
- "version": "1.0.0",
4
+ "version": "1.0.2",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
7
7
  "main": "./dist/index.cjs",