@potentiajs/core 0.1.0-preview.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/CHANGELOG.md +45 -0
- package/LICENSE +21 -0
- package/README.md +441 -0
- package/bin/potentia.js +14 -0
- package/examples/file-routing-basic/README.md +156 -0
- package/examples/file-routing-basic/app.js +16 -0
- package/examples/file-routing-basic/generate.js +21 -0
- package/examples/file-routing-basic/routes/health.js +3 -0
- package/examples/file-routing-basic/routes/index.js +3 -0
- package/examples/file-routing-basic/routes/users/[id].js +6 -0
- package/examples/file-routing-basic/routes/users/_routes.js +15 -0
- package/examples/form-rendering-basic/README.md +43 -0
- package/examples/form-rendering-basic/index.js +120 -0
- package/examples/full-flow-basic/README.md +141 -0
- package/examples/full-flow-basic/app.js +16 -0
- package/examples/full-flow-basic/form.js +205 -0
- package/examples/full-flow-basic/generate.js +21 -0
- package/examples/full-flow-basic/routes/index.js +5 -0
- package/examples/full-flow-basic/routes/users/[id].js +5 -0
- package/examples/full-flow-basic/routes/users/_routes.js +8 -0
- package/examples/full-flow-basic/routes/users/index.js +5 -0
- package/examples/full-flow-basic/routes/users/new.js +5 -0
- package/package.json +90 -0
- package/src/cli.js +407 -0
- package/src/dev/file-routing/diagnostics.js +24 -0
- package/src/dev/file-routing/generator.js +126 -0
- package/src/dev/file-routing/index.js +5 -0
- package/src/dev/file-routing/path-mapping.js +125 -0
- package/src/dev/file-routing/scanner.js +154 -0
- package/src/dev/file-routing/writer.js +100 -0
- package/src/file-routing.d.ts +36 -0
- package/src/file-routing.js +1 -0
- package/src/forms.d.ts +9 -0
- package/src/forms.js +334 -0
- package/src/index.d.ts +185 -0
- package/src/index.js +15 -0
- package/src/index.mjs +25 -0
- package/src/kernel/action-projection.js +35 -0
- package/src/kernel/action.js +172 -0
- package/src/kernel/app.js +144 -0
- package/src/kernel/context.js +37 -0
- package/src/kernel/contract-projection.js +129 -0
- package/src/kernel/contract.js +135 -0
- package/src/kernel/diagnostics.js +147 -0
- package/src/kernel/effect.js +119 -0
- package/src/kernel/error.js +93 -0
- package/src/kernel/form-projection.js +251 -0
- package/src/kernel/form-state.js +173 -0
- package/src/kernel/plugin.js +46 -0
- package/src/kernel/response.js +187 -0
- package/src/kernel/result.js +45 -0
- package/src/kernel/route-collection.js +148 -0
- package/src/kernel/route-id.js +11 -0
- package/src/kernel/route-manifest.js +129 -0
- package/src/kernel/route-projection.js +139 -0
- package/src/kernel/route.js +156 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0-preview.0
|
|
4
|
+
|
|
5
|
+
Experimental public preview for `@potentiajs/core`.
|
|
6
|
+
|
|
7
|
+
PotentiaJS is still not production-ready and has no stable public API commitment. All root exports remain experimental and may change before a stable release.
|
|
8
|
+
|
|
9
|
+
### Highlights
|
|
10
|
+
|
|
11
|
+
- Added a Bun-first routing kernel for explicit, contract-driven request handling.
|
|
12
|
+
- Added explicit route composition with route collections and mounts.
|
|
13
|
+
- Added SigilJS request, response, and action contract boundaries.
|
|
14
|
+
- Added experimental effect descriptors and helpers.
|
|
15
|
+
- Added experimental actions with JSON input and URL-encoded form-compatible action input.
|
|
16
|
+
- Added normalized diagnostics for route/action validation failures.
|
|
17
|
+
- Added safe form state helper for failed form submissions.
|
|
18
|
+
- Added renderer-independent form projection metadata.
|
|
19
|
+
- Added route, action, manifest, and contract projection metadata.
|
|
20
|
+
- Added focused examples for kernel, SigilJS, composition, actions, and form state.
|
|
21
|
+
- Pruned root exports to the intentional public preview surface.
|
|
22
|
+
|
|
23
|
+
### Not included
|
|
24
|
+
|
|
25
|
+
- Stable API commitment.
|
|
26
|
+
- Production-readiness claim.
|
|
27
|
+
- Frontend runtime.
|
|
28
|
+
- Renderer or form generator.
|
|
29
|
+
- Client SDK.
|
|
30
|
+
- OpenAPI generator.
|
|
31
|
+
- Multipart/file upload helpers.
|
|
32
|
+
- Session/flash helpers.
|
|
33
|
+
- TypeScript source conversion.
|
|
34
|
+
- Public file-routing API.
|
|
35
|
+
- Package split.
|
|
36
|
+
- DB/auth integrations.
|
|
37
|
+
|
|
38
|
+
### Notes
|
|
39
|
+
|
|
40
|
+
- Runtime target: Bun.
|
|
41
|
+
- Source remains plain JavaScript ES modules.
|
|
42
|
+
- Runtime dependency: `@weipertda/sigiljs@0.18.0`.
|
|
43
|
+
- Minimal TypeScript declarations are included for editor/type discovery, but they are conservative and experimental.
|
|
44
|
+
- Post-release verification should confirm the registry artifact users receive.
|
|
45
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel Weipert
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
# PotentiaJS
|
|
2
|
+
|
|
3
|
+
PotentiaJS is an experimental Bun-first JavaScript framework kernel for explicit, contract-driven request handling.
|
|
4
|
+
|
|
5
|
+
It is **not production-ready** and has **no stable public API** yet.
|
|
6
|
+
|
|
7
|
+
## Current status
|
|
8
|
+
|
|
9
|
+
- Package: `@potentiajs/core`
|
|
10
|
+
- Version: `0.1.0-preview.0`
|
|
11
|
+
- Visibility: public preview package candidate
|
|
12
|
+
- License: MIT
|
|
13
|
+
- Runtime: Bun
|
|
14
|
+
- Source: plain JavaScript ES modules
|
|
15
|
+
- TypeScript source: none
|
|
16
|
+
- Runtime dependency: `@weipertda/sigiljs@0.18.0`
|
|
17
|
+
- Type declarations: minimal conservative declarations included
|
|
18
|
+
|
|
19
|
+
Current kernel flow:
|
|
20
|
+
|
|
21
|
+
```txt
|
|
22
|
+
Request → Route Match → Contract Boundary → Effect Execution → Result Normalization → Response
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Install / local usage
|
|
26
|
+
|
|
27
|
+
Once registry visibility is confirmed, the preview package install shape is:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bun add @potentiajs/core
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
From this repository:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bun install
|
|
37
|
+
bun run test
|
|
38
|
+
bun run check
|
|
39
|
+
bun run check:preview
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If the registry install command fails, check the post-release verification notes: the registry artifact may not be visible yet even though release metadata is prepared.
|
|
43
|
+
|
|
44
|
+
## Minimal route
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
import { createApp, json, ok, route } from '@potentiajs/core';
|
|
48
|
+
|
|
49
|
+
const app = createApp({
|
|
50
|
+
routes: [
|
|
51
|
+
route('GET', '/', () => ok(json({ ok: true })))
|
|
52
|
+
]
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
Bun.serve({ fetch: app.fetch });
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## SigilJS contract route
|
|
59
|
+
|
|
60
|
+
Potentia uses SigilJS for runtime contracts.
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
import { sigil, optional } from '@weipertda/sigiljs';
|
|
64
|
+
import { createApp, json, ok, route } from '@potentiajs/core';
|
|
65
|
+
|
|
66
|
+
const UserParams = sigil({ id: String });
|
|
67
|
+
const UserQuery = sigil({ include: optional(String) });
|
|
68
|
+
const UserResponse = sigil({ id: String, name: String });
|
|
69
|
+
|
|
70
|
+
const app = createApp({
|
|
71
|
+
routes: [
|
|
72
|
+
route('GET', '/users/:id', (ctx) => ok(json({
|
|
73
|
+
id: ctx.params.id,
|
|
74
|
+
name: ctx.query.include === 'full' ? 'Ada Lovelace' : 'Ada'
|
|
75
|
+
})), {
|
|
76
|
+
params: UserParams,
|
|
77
|
+
query: UserQuery,
|
|
78
|
+
response: UserResponse
|
|
79
|
+
})
|
|
80
|
+
]
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
SigilJS `0.18.0` uses JavaScript constructors/helpers such as `sigil({ id: String })` and `optional(String)`.
|
|
85
|
+
|
|
86
|
+
## Route composition
|
|
87
|
+
|
|
88
|
+
Composition is explicit. File routing is optional generated projection, not auto-discovery during request handling or a global mutable route registry.
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
import { createApp, createRoutes, json, mount, ok, route } from '@potentiajs/core';
|
|
92
|
+
|
|
93
|
+
const userRoutes = createRoutes({
|
|
94
|
+
prefix: '/users',
|
|
95
|
+
routes: [
|
|
96
|
+
route('GET', '/', () => ok(json({ users: [] }))),
|
|
97
|
+
route('GET', '/:id', (ctx) => ok(json({ id: ctx.params.id })))
|
|
98
|
+
]
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const app = createApp({
|
|
102
|
+
routes: [
|
|
103
|
+
route('GET', '/', () => ok(json({ ok: true }))),
|
|
104
|
+
mount(userRoutes, { prefix: '/api' })
|
|
105
|
+
]
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Effective paths:
|
|
110
|
+
|
|
111
|
+
- `/`
|
|
112
|
+
- `/api/users`
|
|
113
|
+
- `/api/users/:id`
|
|
114
|
+
|
|
115
|
+
The experimental file-routing subpath follows this explicit model:
|
|
116
|
+
|
|
117
|
+
```txt
|
|
118
|
+
filesystem → route modules → createRoutes / route / mount → createApp
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The file-routing preview API can scan a route tree and generate an explicit `.potentia/routes.generated.js` module. It is a package subpath, not a root export.
|
|
122
|
+
|
|
123
|
+
Route files:
|
|
124
|
+
|
|
125
|
+
```txt
|
|
126
|
+
routes/
|
|
127
|
+
index.js
|
|
128
|
+
health.js
|
|
129
|
+
users/
|
|
130
|
+
[id].js
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Generate explicit routes with the experimental CLI:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
potentia routes generate
|
|
137
|
+
potentia routes check
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Equivalent explicit form:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
potentia routes generate --root routes --out .potentia/routes.generated.js --package @potentiajs/core
|
|
144
|
+
potentia routes check --root routes --out .potentia/routes.generated.js --package @potentiajs/core
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`generate` writes `.potentia/routes.generated.js`. `check` verifies the generated output is current without writing files, which makes it suitable for CI. Both commands support the same `--root`, `--out`, `--package`, and `--cwd` flags.
|
|
148
|
+
|
|
149
|
+
For scripts and CI tooling, add `--json` to either command:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
potentia routes generate --json
|
|
153
|
+
potentia routes check --json
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
JSON changes output format only: `generate --json` still writes files, `check --json` remains non-mutating, JSON envelopes go to stdout, and exit codes remain the same. The CLI does not include watch mode, config files, a dev server, or a compiler.
|
|
157
|
+
|
|
158
|
+
You can also call the programmatic API directly:
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
import { generateFileRoutes } from '@potentiajs/core/file-routing';
|
|
162
|
+
|
|
163
|
+
await generateFileRoutes({
|
|
164
|
+
rootDir: 'routes',
|
|
165
|
+
outputFile: '.potentia/routes.generated.js'
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Use the generated module explicitly:
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
import { createApp } from '@potentiajs/core';
|
|
173
|
+
import routes from './.potentia/routes.generated.js';
|
|
174
|
+
|
|
175
|
+
const app = createApp({
|
|
176
|
+
routes: [routes]
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
File routing remains projection over explicit route composition: generated modules import `createRoutes(...)` / `mount(...)` from `@potentiajs/core`, while runtime apps still consume normal route collections. The generated `.potentia/` directory is ignored by default and is usually not committed. The runtime kernel does not perform production runtime filesystem scanning. Watch mode, config files, dev server behavior, and compiler integration remain deferred.
|
|
181
|
+
|
|
182
|
+
See [`examples/file-routing-basic/`](examples/file-routing-basic/) for a runnable example.
|
|
183
|
+
|
|
184
|
+
See [`examples/full-flow-basic/`](examples/full-flow-basic/) for a compact full-flow app combining file routing, actions, form projection, server-rendered forms, form state, and redirects.
|
|
185
|
+
|
|
186
|
+
## Effects
|
|
187
|
+
|
|
188
|
+
Handlers and hooks may be plain functions, async functions, or experimental `effect(...)` descriptors. Effect helpers are small command constructors that keep generator workflows readable.
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
import { call, effect, json, ok, route } from '@potentiajs/core';
|
|
192
|
+
|
|
193
|
+
const loadGreeting = (name) => ({ message: `hello ${name}` });
|
|
194
|
+
|
|
195
|
+
route('GET', '/hello', effect(function* hello(ctx) {
|
|
196
|
+
const greeting = yield call(loadGreeting, ctx.query.name || 'world');
|
|
197
|
+
return ok(json(greeting));
|
|
198
|
+
}));
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Available helpers are `call(fn, ...args)`, `value(value)`, and `context(key)`. Raw command objects remain supported for compatibility, but helpers are preferred for normal usage.
|
|
202
|
+
|
|
203
|
+
The current effect runner is intentionally small. It is not a full effect system: concurrency, retries, cancellation, timeouts, queues, supervisors, and workflow tracing are deferred.
|
|
204
|
+
|
|
205
|
+
## Contract projection
|
|
206
|
+
|
|
207
|
+
Projection APIs are experimental metadata helpers for docs, tests, examples, and future tooling. They do not execute handlers, hooks, or generic contract logic.
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
import { createRouteManifest, projectContract, projectRoute, projectRoutes } from '@potentiajs/core';
|
|
211
|
+
|
|
212
|
+
const contract = projectContract(UserResponse);
|
|
213
|
+
const single = projectRoute(route('GET', '/users/:id', handler, { response: UserResponse }));
|
|
214
|
+
const collection = projectRoutes(app);
|
|
215
|
+
const manifest = createRouteManifest(app, { packageName: '@potentiajs/core', packageVersion: '0.1.0-preview.0' });
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`projectContract()` reports honest metadata such as capability, opacity, schema, field summaries, required fields, and optional fields. Generic function/parse/check contracts remain opaque. SigilJS contracts expose richer metadata only where SigilJS safely provides it.
|
|
219
|
+
|
|
220
|
+
`projectRoute()` and `projectRoutes()` summarize methods, paths, contract boundaries, and hook counts without running application code. These helpers are not OpenAPI, client, forms, or docs generators; those remain deferred.
|
|
221
|
+
|
|
222
|
+
Route metadata is experimental. Routes may include optional `name`, `meta`, and `source` options for manifest/tooling descriptions. `createRouteManifest()` creates a deterministic route manifest with route IDs, names, contracts, hook counts, source/meta, diagnostics, and lookup tables. Manifest creation does not execute handlers, hooks, or contracts; it is a foundation for future docs, tests, clients, forms/actions, and dev tooling, not a completed generator.
|
|
223
|
+
|
|
224
|
+
## Actions
|
|
225
|
+
|
|
226
|
+
Actions are experimental server-side contract boundaries. They support JSON input and `application/x-www-form-urlencoded` input. Parsed and validated action input is attached to `ctx.input`, handlers may use plain/async/effect execution, and output contracts validate the logical response body before route response projection.
|
|
227
|
+
|
|
228
|
+
```js
|
|
229
|
+
import { action, call, effect, json, ok, route } from '@potentiajs/core';
|
|
230
|
+
|
|
231
|
+
const createUser = action('users.create', effect(function* createUser(ctx) {
|
|
232
|
+
const user = yield call(insertUser, ctx.input);
|
|
233
|
+
return ok(json(user));
|
|
234
|
+
}), {
|
|
235
|
+
input: CreateUserInput,
|
|
236
|
+
output: CreateUserOutput
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
route('POST', '/users', createUser);
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
URL-encoded submissions parse into plain objects. Repeated fields become arrays, empty values stay empty strings, and values remain strings until input contracts transform them.
|
|
243
|
+
|
|
244
|
+
Actions return normal Potentia results and responses. Use `ok(...)` for success, `fail(...)` for intentional domain failures, and `redirect(...)` for explicit post-action redirects. Validation failures are deterministic and safe: action input failures return `ok: false`, an action error code, boundary metadata, and an issues array.
|
|
245
|
+
|
|
246
|
+
Server validation remains authoritative. Form generation, multipart/file uploads, frontend runtime, client SDK, and OpenAPI generation remain deferred. Future client-side validation will be projection-only convenience.
|
|
247
|
+
|
|
248
|
+
`createFormState(...)` is an experimental opt-in helper for failed form submissions. It preserves safe parsed values only, omits sensitive fields such as passwords/tokens/secrets, groups canonical issues by field with `_form` for root-level issues, and leaves default action behavior unchanged. Redirects remain explicit; successful form submissions should use `redirect('/path', 303)` when post-submit navigation is desired.
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
return fail(createFormState({
|
|
252
|
+
ok: false,
|
|
253
|
+
values: ctx.input,
|
|
254
|
+
error: { code: 'USER_EMAIL_TAKEN', message: 'Email is already in use' }
|
|
255
|
+
}), 409);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Potentia also includes an experimental optional server-side HTML string renderer on the forms subpath. Rendering is metadata-driven, escaped by default, and state-aware. It does not add a frontend runtime, JSX, hydration, custom renderer system, client SDK, OpenAPI generator, session/flash helper, or multipart/file upload helper.
|
|
259
|
+
|
|
260
|
+
```js
|
|
261
|
+
import { createFormState, projectForm } from '@potentiajs/core';
|
|
262
|
+
import { renderForm } from '@potentiajs/core/forms';
|
|
263
|
+
|
|
264
|
+
const formProjection = projectForm(createUser);
|
|
265
|
+
const formState = createFormState({
|
|
266
|
+
ok: false,
|
|
267
|
+
values: ctx.input,
|
|
268
|
+
error: { code: 'USER_EMAIL_TAKEN', message: 'Email is already in use' }
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const html = renderForm(formProjection, {
|
|
272
|
+
action: '/users',
|
|
273
|
+
state: formState
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`renderForm(...)` returns a plain HTML string. It renders projected fields, preserves safe state values, omits sensitive values, and renders root/field errors. It supports explicit textarea and hidden metadata, finite options as `<select>`, stable `data-potentia-*` styling/testing hooks, and baseline accessibility attributes. Server validation remains authoritative. The renderer is intentionally exported from `@potentiajs/core/forms`, not the package root.
|
|
278
|
+
|
|
279
|
+
`projectForm(...)` is an experimental metadata-only helper. It projects action input contracts into renderer-independent field metadata where safe. SigilJS object contracts can expose field paths, derived labels, conservative input hints, required/optional flags, scalar-array `multiple` hints, and sensitive flags. Generic function/parse/check contracts remain opaque and do not invent fields. Server validation remains authoritative.
|
|
280
|
+
|
|
281
|
+
## Hooks
|
|
282
|
+
|
|
283
|
+
Apps, route collections, and mounts can define lifecycle hooks:
|
|
284
|
+
|
|
285
|
+
```js
|
|
286
|
+
const secureRoutes = createRoutes({
|
|
287
|
+
hooks: {
|
|
288
|
+
beforeRequest: [requireAuth],
|
|
289
|
+
afterResponse: [auditResponse],
|
|
290
|
+
onError: [recover]
|
|
291
|
+
},
|
|
292
|
+
routes: []
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Hook order for matched routes:
|
|
297
|
+
|
|
298
|
+
1. app `beforeRequest`
|
|
299
|
+
2. outer collection `beforeRequest`
|
|
300
|
+
3. inner collection `beforeRequest`
|
|
301
|
+
4. route handler
|
|
302
|
+
5. inner collection `afterResponse`
|
|
303
|
+
6. outer collection `afterResponse`
|
|
304
|
+
7. app `afterResponse`
|
|
305
|
+
|
|
306
|
+
Scoped `onError` recovery is nearest-local first, then outer scopes, then app hooks.
|
|
307
|
+
|
|
308
|
+
## Error/result model
|
|
309
|
+
|
|
310
|
+
Handlers usually return `ok(value)` or `fail(error)`. Response helpers create descriptors that project to native `Response` objects:
|
|
311
|
+
|
|
312
|
+
```js
|
|
313
|
+
return ok(json({ ok: true }));
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Safe contract failure body example:
|
|
317
|
+
|
|
318
|
+
```js
|
|
319
|
+
{
|
|
320
|
+
ok: false,
|
|
321
|
+
error: {
|
|
322
|
+
code: 'POTENTIA_CONTRACT_FAILED',
|
|
323
|
+
message: 'Request body failed contract validation',
|
|
324
|
+
boundary: 'body',
|
|
325
|
+
issues: [{
|
|
326
|
+
code: 'invalid_type',
|
|
327
|
+
message: 'SigilJS contract rejected value',
|
|
328
|
+
path: ['email'],
|
|
329
|
+
field: 'email',
|
|
330
|
+
boundary: 'body',
|
|
331
|
+
source: 'sigil',
|
|
332
|
+
expected: 'string',
|
|
333
|
+
received: 'number',
|
|
334
|
+
meta: null
|
|
335
|
+
}]
|
|
336
|
+
},
|
|
337
|
+
boundary: 'body',
|
|
338
|
+
issues: [/* same issue objects */]
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Contract failures now expose normalized issue metadata across route and action boundaries. SigilJS can provide field-level `path` / `field` issues where structured metadata is available. Generic contracts remain opaque and use root-level issues (`path: []`, `field: null`). Raw input values are not exposed; `received` uses safe type descriptors such as `string`, `number`, `array`, or `object`. These diagnostics support future forms/tooling, but Potentia still does not include a form generator, client SDK, or OpenAPI generator.
|
|
343
|
+
|
|
344
|
+
Unsafe thrown handler errors return `POTENTIA_HANDLER_FAILED` with `Internal server error`.
|
|
345
|
+
|
|
346
|
+
## Examples
|
|
347
|
+
|
|
348
|
+
- [`examples/kernel-basic/`](examples/kernel-basic/) — generic contract kernel smoke app.
|
|
349
|
+
- [`examples/sigiljs-basic/`](examples/sigiljs-basic/) — focused SigilJS contract smoke app.
|
|
350
|
+
- [`examples/composed-basic/`](examples/composed-basic/) — explicit route composition smoke app.
|
|
351
|
+
- [`examples/action-basic/`](examples/action-basic/) — experimental JSON and URL-encoded action smoke app.
|
|
352
|
+
- [`examples/form-state-basic/`](examples/form-state-basic/) — opt-in safe form state helper smoke app.
|
|
353
|
+
- [`examples/form-rendering-basic/`](examples/form-rendering-basic/) — server-rendered escaped form HTML smoke app.
|
|
354
|
+
- [`examples/file-routing-basic/`](examples/file-routing-basic/) — experimental file-route generation smoke app.
|
|
355
|
+
- [`examples/full-flow-basic/`](examples/full-flow-basic/) — file routing + actions + form projection + server-rendered forms.
|
|
356
|
+
|
|
357
|
+
Each example exports `app` for smoke tests and only starts `Bun.serve()` when run directly.
|
|
358
|
+
|
|
359
|
+
## Commands
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
bun run test # test suite
|
|
363
|
+
bun run check # current local check
|
|
364
|
+
bun run check:preview # tests + check + npm pack dry-run
|
|
365
|
+
bun run check:release # preview release check alias
|
|
366
|
+
bun run check:file-routing # internal file-routing projection tests
|
|
367
|
+
potentia routes generate # experimental file-route generation CLI after package install/link
|
|
368
|
+
potentia routes check # verify generated file-route output is current without writing
|
|
369
|
+
bun run generate:file-routes -- --root routes --out .potentia/routes.generated.js # internal/dev-only generation wrapper
|
|
370
|
+
bun run pack:dry # npm pack --dry-run --json
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Current limitations
|
|
374
|
+
|
|
375
|
+
Deferred intentionally:
|
|
376
|
+
|
|
377
|
+
- stable public APIs
|
|
378
|
+
- registry visibility may lag or require release verification
|
|
379
|
+
- stable public file-based routing API and route auto-discovery beyond the experimental `@potentiajs/core/file-routing` generation subpath
|
|
380
|
+
- nested layout routing
|
|
381
|
+
- frontend rendering and hydration
|
|
382
|
+
- client router
|
|
383
|
+
- stable server actions/RPC
|
|
384
|
+
- full middleware ecosystem
|
|
385
|
+
- large plugin ecosystem
|
|
386
|
+
- async plugin loading/discovery
|
|
387
|
+
- advanced effect workflows/concurrency/retries/cancellation
|
|
388
|
+
- field-level verbose contract diagnostics
|
|
389
|
+
- OpenAPI/JSON Schema/route docs/client/forms generators
|
|
390
|
+
- multipart/file uploads and form generation
|
|
391
|
+
- session/flash form state persistence
|
|
392
|
+
- route manifest file writing/loading
|
|
393
|
+
- streaming/file responses/content negotiation/cookies API
|
|
394
|
+
- CLI expansion
|
|
395
|
+
- `.view` compiler changes
|
|
396
|
+
- database integration
|
|
397
|
+
- auth
|
|
398
|
+
- package splitting
|
|
399
|
+
- automatic publish on push
|
|
400
|
+
|
|
401
|
+
## Public API status
|
|
402
|
+
|
|
403
|
+
All exports are experimental:
|
|
404
|
+
|
|
405
|
+
- app/routes/actions/forms: `createApp`, `route`, `action`, `createFormState`, `createRoutes`, `mount`, `composeRoutes`
|
|
406
|
+
- results/responses: `ok`, `fail`, `json`, `text`, `redirect`
|
|
407
|
+
- effects: `effect`, `call`, `value`, `context`
|
|
408
|
+
- errors: `createFrameworkError`
|
|
409
|
+
- contracts/projection/manifest/plugins: `projectContract`, `projectAction`, `projectForm`, `projectRoute`, `projectRoutes`, `createRouteManifest`, `createPlugin`
|
|
410
|
+
|
|
411
|
+
Lower-level implementation helpers such as request-context construction, effect execution, framework error normalization, and response projection remain internal and may be reshaped before any stable API commitment.
|
|
412
|
+
|
|
413
|
+
Experimental package subpaths:
|
|
414
|
+
|
|
415
|
+
- file routing generation: `@potentiajs/core/file-routing` exports `generateFileRoutes`
|
|
416
|
+
|
|
417
|
+
## Release / publish status
|
|
418
|
+
|
|
419
|
+
Prepared for public preview dry-run verification:
|
|
420
|
+
|
|
421
|
+
- package metadata targets `@potentiajs/core@0.1.0-preview.0`
|
|
422
|
+
- license is MIT
|
|
423
|
+
- repository metadata targets `https://github.com/antistructured/potentiajs`
|
|
424
|
+
- package is configured as public
|
|
425
|
+
- `CHANGELOG.md` and conservative declarations are included
|
|
426
|
+
- no public API is stable
|
|
427
|
+
- registry visibility is verified during post-release checks
|
|
428
|
+
|
|
429
|
+
Post-release verification should confirm the registry package is visible before sharing install instructions widely.
|
|
430
|
+
|
|
431
|
+
## License
|
|
432
|
+
|
|
433
|
+
MIT © Daniel Weipert
|
|
434
|
+
|
|
435
|
+
See [`LICENSE`](LICENSE).
|
|
436
|
+
|
|
437
|
+
## Changelog
|
|
438
|
+
|
|
439
|
+
See [`CHANGELOG.md`](CHANGELOG.md).
|
|
440
|
+
|
|
441
|
+
|
package/bin/potentia.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { main } from '../src/cli.js';
|
|
4
|
+
|
|
5
|
+
main(process.argv.slice(2), {
|
|
6
|
+
cwd: process.cwd(),
|
|
7
|
+
stdout: process.stdout,
|
|
8
|
+
stderr: process.stderr
|
|
9
|
+
}).then((code) => {
|
|
10
|
+
process.exitCode = code;
|
|
11
|
+
}).catch((error) => {
|
|
12
|
+
process.stderr.write(`Unexpected Potentia CLI failure: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# File Routing Basic Example
|
|
2
|
+
|
|
3
|
+
This example shows PotentiaJS file routing as explicit generated route composition.
|
|
4
|
+
|
|
5
|
+
```txt
|
|
6
|
+
route files
|
|
7
|
+
→ potentia routes generate
|
|
8
|
+
→ .potentia/routes.generated.js
|
|
9
|
+
→ createApp(...)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
File routing is optional and experimental. The CLI is a thin generation wrapper; it is not watch mode, not a dev server, and not runtime request-time filesystem scanning.
|
|
13
|
+
|
|
14
|
+
## Route tree
|
|
15
|
+
|
|
16
|
+
```txt
|
|
17
|
+
examples/file-routing-basic/
|
|
18
|
+
generate.js
|
|
19
|
+
app.js
|
|
20
|
+
routes/
|
|
21
|
+
index.js
|
|
22
|
+
health.js
|
|
23
|
+
users/
|
|
24
|
+
_routes.js
|
|
25
|
+
[id].js
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Projected routes:
|
|
29
|
+
|
|
30
|
+
- `routes/index.js` → `/`
|
|
31
|
+
- `routes/health.js` → `/health`
|
|
32
|
+
- `routes/users/[id].js` → `/users/:id`
|
|
33
|
+
- `routes/users/_routes.js` → scoped metadata/hooks for the `users` folder
|
|
34
|
+
|
|
35
|
+
## Generate routes
|
|
36
|
+
|
|
37
|
+
Use the CLI:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
potentia routes generate \
|
|
41
|
+
--root examples/file-routing-basic/routes \
|
|
42
|
+
--out examples/file-routing-basic/.potentia/routes.generated.js
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Verify generated output without writing files:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
potentia routes check \
|
|
49
|
+
--root examples/file-routing-basic/routes \
|
|
50
|
+
--out examples/file-routing-basic/.potentia/routes.generated.js
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`generate` writes the route module. `check` only verifies the generated output is current, making it useful for CI.
|
|
54
|
+
|
|
55
|
+
For scripts and CI tooling, both commands can emit JSON:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
potentia routes generate --json \
|
|
59
|
+
--root examples/file-routing-basic/routes \
|
|
60
|
+
--out examples/file-routing-basic/.potentia/routes.generated.js
|
|
61
|
+
potentia routes check --json \
|
|
62
|
+
--root examples/file-routing-basic/routes \
|
|
63
|
+
--out examples/file-routing-basic/.potentia/routes.generated.js
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
JSON changes output format only: `generate --json` still writes files, `check --json` remains non-mutating, JSON envelopes go to stdout, and exit codes remain the same.
|
|
67
|
+
|
|
68
|
+
Or run the programmatic example script:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
bun examples/file-routing-basic/generate.js
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The programmatic script uses the public subpath:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
import { generateFileRoutes } from '@potentiajs/core/file-routing';
|
|
78
|
+
|
|
79
|
+
await generateFileRoutes({
|
|
80
|
+
rootDir: new URL('./routes', import.meta.url).pathname,
|
|
81
|
+
outputFile: new URL('./.potentia/routes.generated.js', import.meta.url).pathname
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This writes:
|
|
86
|
+
|
|
87
|
+
```txt
|
|
88
|
+
examples/file-routing-basic/.potentia/routes.generated.js
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The generated file is inspectable JavaScript. It imports route modules and exports an explicit `createRoutes(...)` collection.
|
|
92
|
+
|
|
93
|
+
The start of the file looks like this:
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
// Generated by PotentiaJS. Do not edit by hand.
|
|
97
|
+
import { createRoutes, mount } from '@potentiajs/core';
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
That generated module is normal JavaScript: it imports your route files and composes them. It does not scan the filesystem when a request arrives.
|
|
101
|
+
|
|
102
|
+
## Use generated routes
|
|
103
|
+
|
|
104
|
+
`app.js` consumes the generated module explicitly:
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
import { createApp } from '@potentiajs/core';
|
|
108
|
+
import routes from './.potentia/routes.generated.js';
|
|
109
|
+
|
|
110
|
+
export const app = createApp({
|
|
111
|
+
routes: [routes]
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Run locally
|
|
116
|
+
|
|
117
|
+
Generate first, then run the app:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
potentia routes generate \
|
|
121
|
+
--root examples/file-routing-basic/routes \
|
|
122
|
+
--out examples/file-routing-basic/.potentia/routes.generated.js
|
|
123
|
+
potentia routes check \
|
|
124
|
+
--root examples/file-routing-basic/routes \
|
|
125
|
+
--out examples/file-routing-basic/.potentia/routes.generated.js
|
|
126
|
+
bun examples/file-routing-basic/app.js
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Try:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
curl http://localhost:3000/
|
|
133
|
+
curl http://localhost:3000/health
|
|
134
|
+
curl http://localhost:3000/users/ada
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Generated output policy
|
|
138
|
+
|
|
139
|
+
- `.potentia/routes.generated.js` is generated output.
|
|
140
|
+
- `.potentia/` is ignored by default in this repository.
|
|
141
|
+
- Generation runs during a dev/build step, not during request handling.
|
|
142
|
+
- The generated module must not scan the filesystem at runtime.
|
|
143
|
+
- `potentia routes check` verifies generated output without writing or rewriting files.
|
|
144
|
+
- Add `--json` to `generate` or `check` for machine-readable stdout envelopes; exit codes remain unchanged.
|
|
145
|
+
- Normal app projects usually should not commit `.potentia/`; package authors may choose differently if they intentionally ship generated code.
|
|
146
|
+
- If generation fails, Potentia reports diagnostics and avoids replacing the previous valid output.
|
|
147
|
+
|
|
148
|
+
## Deferred
|
|
149
|
+
|
|
150
|
+
- watch mode
|
|
151
|
+
- config file support
|
|
152
|
+
- compiler integration
|
|
153
|
+
- dev server
|
|
154
|
+
- TypeScript route files
|
|
155
|
+
- named method exports such as `GET` / `POST`
|
|
156
|
+
- catch-all, optional, and route-group conventions
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createApp } from '@potentiajs/core';
|
|
2
|
+
|
|
3
|
+
import routes from './.potentia/routes.generated.js';
|
|
4
|
+
|
|
5
|
+
export const app = createApp({
|
|
6
|
+
routes: [routes]
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
if (import.meta.main) {
|
|
10
|
+
Bun.serve({
|
|
11
|
+
port: 3000,
|
|
12
|
+
fetch: app.fetch
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
console.log('Potentia file routing example running at http://localhost:3000');
|
|
16
|
+
}
|