@njinlabs/njin 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/package.json +1 -1
- package/src/core/config.ts +9 -0
- package/src/core/helper.ts +5 -0
- package/src/core/model/index.ts +1 -0
- package/src/core/plugin.ts +2 -1
- package/src/modules/view.ts +5 -0
package/README.md
CHANGED
|
@@ -131,6 +131,37 @@ This automatically generates:
|
|
|
131
131
|
<title>{{ settings.siteName }}</title>
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
## Helpers — custom template functions
|
|
135
|
+
|
|
136
|
+
`helpers` register a plain, stateless function as an Edge global — unlike `vars`/`models`, there's no DB record and no auto-generated REST endpoint, just a function callable from any `.edge` template.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
// src/helpers/format_date.ts
|
|
140
|
+
import { defineHelper } from "@njinlabs/njin";
|
|
141
|
+
import moment from "moment";
|
|
142
|
+
|
|
143
|
+
export default defineHelper("formatDate", (date: string, format = "DD MMM YYYY") =>
|
|
144
|
+
moment(date).format(format),
|
|
145
|
+
);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Register it in `config.ts`:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
// config.ts
|
|
152
|
+
export default defineConfig({
|
|
153
|
+
helpers: [
|
|
154
|
+
() => import("./src/helpers/format_date"),
|
|
155
|
+
],
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Use it directly in a template, no `await` needed since it's a plain synchronous function (an `async` `fn` works too, called with `await` like any other async global):
|
|
160
|
+
|
|
161
|
+
```edge
|
|
162
|
+
<p>{{ formatDate(post.createdAt) }}</p>
|
|
163
|
+
```
|
|
164
|
+
|
|
134
165
|
## Events
|
|
135
166
|
|
|
136
167
|
A type-safe event bus for fan-out notifications (e.g. "an order was paid", "a user registered") — different from model hooks (`beforeCreate`/`afterCreate`/...): hooks are scoped to one model and can abort the operation by throwing, while events are fire-and-forget — a listener that throws is logged but never stops other listeners or the code that dispatched.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@njinlabs/njin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A modern framework for building company profiles, landing pages, and content-driven websites.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": ["bun", "elysia", "surrealdb", "edgejs", "cms", "framework"],
|
package/src/core/config.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { AnyElysia } from "elysia";
|
|
|
2
2
|
import type { FileAdapter } from "../modules/file";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import bunFilesystemAdapter from "./adapters/bun_filesystem";
|
|
5
|
+
import type { Helper } from "./helper";
|
|
5
6
|
import type { makeModel } from "./model";
|
|
6
7
|
import type { Plugin } from "./plugin";
|
|
7
8
|
import type { makeVars } from "./vars";
|
|
@@ -28,6 +29,11 @@ export type RouteFactory = () => Promise<{ default: AnyElysia }>;
|
|
|
28
29
|
// a singleton settings object, not a list of records like a model.
|
|
29
30
|
export type VarsFactory = () => Promise<{ default: ReturnType<typeof makeVars> }>;
|
|
30
31
|
|
|
32
|
+
// A helper file's default export is one defineHelper() — a stateless function
|
|
33
|
+
// registered as an Edge global by its own name, unlike models/vars which are
|
|
34
|
+
// DB-backed objects registered by prefix.
|
|
35
|
+
export type HelperFactory = () => Promise<{ default: Helper }>;
|
|
36
|
+
|
|
31
37
|
export type NjinConfig = {
|
|
32
38
|
port?: number;
|
|
33
39
|
db?: {
|
|
@@ -50,6 +56,7 @@ export type NjinConfig = {
|
|
|
50
56
|
events?: EventFactory[];
|
|
51
57
|
routes?: RouteFactory[];
|
|
52
58
|
vars?: VarsFactory[];
|
|
59
|
+
helpers?: HelperFactory[];
|
|
53
60
|
plugins?: Plugin[];
|
|
54
61
|
};
|
|
55
62
|
|
|
@@ -64,6 +71,7 @@ export type ResolvedConfig = {
|
|
|
64
71
|
events: EventFactory[];
|
|
65
72
|
routes: RouteFactory[];
|
|
66
73
|
vars: VarsFactory[];
|
|
74
|
+
helpers: HelperFactory[];
|
|
67
75
|
// Everything else a Plugin contributes (models/vars/hooks/events/routes) is already
|
|
68
76
|
// flattened into the arrays above — init() is the only part that can't be merged away,
|
|
69
77
|
// so it's the only piece of each Plugin that survives resolution on its own.
|
|
@@ -131,6 +139,7 @@ export const loadConfig = async (preloaded?: NjinConfig): Promise<void> => {
|
|
|
131
139
|
events: [...plugins.flatMap((p) => p.events ?? []), ...(userConfig.events ?? [])],
|
|
132
140
|
routes: [...plugins.flatMap((p) => p.routes ?? []), ...(userConfig.routes ?? [])],
|
|
133
141
|
vars: [...plugins.flatMap((p) => p.vars ?? []), ...(userConfig.vars ?? [])],
|
|
142
|
+
helpers: [...plugins.flatMap((p) => p.helpers ?? []), ...(userConfig.helpers ?? [])],
|
|
134
143
|
pluginInits: plugins.map((p) => p.init).filter((fn): fn is () => Promise<void> | void => typeof fn === "function"),
|
|
135
144
|
};
|
|
136
145
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2
|
+
export type Helper = { name: string; fn: (...args: any[]) => unknown };
|
|
3
|
+
|
|
4
|
+
// Identity function — DX/typing only, same purpose as defineConfig/definePlugin.
|
|
5
|
+
export const defineHelper = (name: string, fn: Helper["fn"]): Helper => ({ name, fn });
|
package/src/core/model/index.ts
CHANGED
|
@@ -272,6 +272,7 @@ export const makeModel = <Rules extends z.ZodObject>(
|
|
|
272
272
|
export * from "./data_type";
|
|
273
273
|
export * from "./hooks";
|
|
274
274
|
export * from "../event";
|
|
275
|
+
export * from "../helper";
|
|
275
276
|
export * from "../plugin";
|
|
276
277
|
export * from "../route";
|
|
277
278
|
export * from "../vars";
|
package/src/core/plugin.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { EventFactory, HookFactory, ModelFactory, RouteFactory, VarsFactory } from "./config";
|
|
1
|
+
import type { EventFactory, HelperFactory, HookFactory, ModelFactory, RouteFactory, VarsFactory } from "./config";
|
|
2
2
|
|
|
3
3
|
export type Plugin = {
|
|
4
4
|
models?: ModelFactory[];
|
|
5
5
|
vars?: VarsFactory[];
|
|
6
|
+
helpers?: HelperFactory[];
|
|
6
7
|
hooks?: HookFactory[];
|
|
7
8
|
events?: EventFactory[];
|
|
8
9
|
routes?: RouteFactory[];
|
package/src/modules/view.ts
CHANGED
|
@@ -102,6 +102,11 @@ const view = makeModule(() => {
|
|
|
102
102
|
edge.global(group.prefix, group);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
for (const helperPromise of getConfig().helpers) {
|
|
106
|
+
const { default: helper } = await helperPromise();
|
|
107
|
+
edge.global(helper.name, helper.fn);
|
|
108
|
+
}
|
|
109
|
+
|
|
105
110
|
const controller = new Elysia();
|
|
106
111
|
|
|
107
112
|
if (!isDev) {
|