@voxgig/build 4.4.0 → 4.12.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 +51 -2
- package/build.ts +20 -0
- package/dist/api/api_gen.d.ts +7 -0
- package/dist/api/api_gen.js +370 -0
- package/dist/api/api_gen.js.map +1 -0
- package/dist/build.d.ts +16 -1
- package/dist/build.js +17 -1
- package/dist/build.js.map +1 -1
- package/dist/doc/doc_gen.d.ts +8 -0
- package/dist/doc/doc_gen.js +397 -0
- package/dist/doc/doc_gen.js.map +1 -0
- package/dist/env/web/web_gen.js +197 -11
- package/dist/env/web/web_gen.js.map +1 -1
- package/dist/shape/ent.js +4 -2
- package/dist/shape/ent.js.map +1 -1
- package/env/web/web_gen.ts +219 -13
- package/package.json +6 -9
- package/tm/web/backend/env/shared/seed.ts.frag +23 -0
- package/tm/web/backend/env/web/api.ts.frag +157 -0
- package/tm/web/backend/env/web/web.ts.frag +43 -8
- package/tm/web/backend/srv/api/api-srv.ts.frag +4 -0
- package/tm/web/backend/srv/api/expose.ts.frag +33 -0
- package/tm/web/backend/srv/api/get_info.ts.frag +6 -0
- package/tm/web/backend/srv/api/on_ent.ts.frag +124 -0
- package/tm/web/backend/srv/auth/apikey_util.ts.frag +10 -0
- package/tm/web/backend/srv/auth/auth-srv.ts.frag +1 -0
- package/tm/web/backend/srv/auth/change_pass.ts.frag +12 -0
- package/tm/web/backend/srv/auth/create_apikey.ts.frag +33 -0
- package/tm/web/backend/srv/auth/get_info.ts.frag +1 -0
- package/tm/web/backend/srv/auth/list_apikey.ts.frag +15 -0
- package/tm/web/backend/srv/auth/load_auth.ts.frag +10 -1
- package/tm/web/backend/srv/auth/remind_pass.ts.frag +35 -0
- package/tm/web/backend/srv/auth/revoke_apikey.ts.frag +23 -0
- package/tm/web/backend/srv/auth/signin_user.ts.frag +12 -2
- package/tm/web/backend/srv/auth/signout_user.ts.frag +11 -1
- package/tm/web/backend/srv/auth/update_user.ts.frag +15 -0
- package/tm/web/backend/srv/auth/user_util.ts.frag +21 -0
- package/tm/web/backend/srv/auth/web_change_pass.ts.frag +16 -0
- package/tm/web/backend/srv/auth/web_create_apikey.ts.frag +15 -0
- package/tm/web/backend/srv/auth/web_list_apikey.ts.frag +11 -0
- package/tm/web/backend/srv/auth/web_load_auth.ts.frag +1 -0
- package/tm/web/backend/srv/auth/web_remind_pass.ts.frag +7 -0
- package/tm/web/backend/srv/auth/web_revoke_apikey.ts.frag +14 -0
- package/tm/web/backend/srv/auth/web_signin_user.ts.frag +1 -0
- package/tm/web/backend/srv/auth/web_signout_user.ts.frag +11 -3
- package/tm/web/backend/srv/auth/web_update_user.ts.frag +11 -0
- package/tm/web/backend/srv/ent/access.ts.frag +179 -0
- package/tm/web/backend/srv/ent/cmd_list.ts.frag +86 -0
- package/tm/web/backend/srv/ent/cmd_load.ts.frag +56 -0
- package/tm/web/backend/srv/ent/cmd_remove.ts.frag +60 -0
- package/tm/web/backend/srv/ent/cmd_save.ts.frag +141 -0
- package/tm/web/backend/srv/ent/ent-srv.ts.frag +4 -0
- package/tm/web/backend/srv/ent/get_info.ts.frag +6 -0
- package/tm/web/backend/srv/ent/web_cmd_list.ts.frag +17 -0
- package/tm/web/backend/srv/ent/web_cmd_load.ts.frag +17 -0
- package/tm/web/backend/srv/ent/web_cmd_remove.ts.frag +17 -0
- package/tm/web/backend/srv/ent/web_cmd_save.ts.frag +17 -0
- package/tm/web/backend/test/unit/env/web/api-router.test.ts.frag +315 -0
- package/tm/web/backend/test/unit/env/web/surface.test.ts.frag +109 -0
- package/tm/web/backend/test/unit/srv/api/api.setup.ts.frag +162 -0
- package/tm/web/backend/test/unit/srv/api/api.test.ts.frag +225 -0
- package/tm/web/backend/test/unit/srv/auth/apikey.test.ts.frag +152 -0
- package/tm/web/backend/test/unit/srv/auth/auth.setup.ts.frag +40 -0
- package/tm/web/backend/test/unit/srv/auth/session.test.ts.frag +189 -0
- package/tm/web/backend/test/unit/srv/ent/ent.setup.ts.frag +53 -0
- package/tm/web/backend/test/unit/srv/ent/proxy.test.ts.frag +109 -0
- package/tm/web/docs/explanation/web-architecture.md.frag +50 -0
- package/tm/web/docs/how-to/add-a-custom-entity-view.md.frag +53 -0
- package/tm/web/docs/how-to/change-the-theme.md.frag +63 -0
- package/tm/web/docs/how-to/customise-the-web-app.md.frag +68 -0
- package/tm/web/docs/how-to/use-the-api.md.frag +53 -0
- package/tm/web/docs/reference/web-app.md.frag +98 -0
- package/tm/web/web/AGENTS.md.frag +52 -0
- package/tm/web/web/e2e/smoke.spec.js.frag +13 -11
- package/tm/web/web/index.html.frag +1 -0
- package/tm/web/web/package.json.frag +1 -0
- package/tm/web/web/playwright.config.js.frag +2 -1
- package/tm/web/web/src/api.js.frag +92 -0
- package/tm/web/web/src/bus.js.frag +14 -8
- package/tm/web/web/src/cmp/admin.js.frag +293 -212
- package/tm/web/web/src/cmp/admin.md.frag +51 -0
- package/tm/web/web/src/cmp/app.js.frag +12 -15
- package/tm/web/web/src/cmp/app.md.frag +28 -0
- package/tm/web/web/src/cmp/auth.js.frag +69 -41
- package/tm/web/web/src/cmp/auth.md.frag +36 -0
- package/tm/web/web/src/cmp/public.js.frag +58 -0
- package/tm/web/web/src/cmp/public.md.frag +26 -0
- package/tm/web/web/src/cmp/settings.js.frag +178 -0
- package/tm/web/web/src/cmp/settings.md.frag +30 -0
- package/tm/web/web/src/cmp/shell.js.frag +231 -0
- package/tm/web/web/src/cmp/shell.md.frag +49 -0
- package/tm/web/web/src/cmp/view/custom-view.js.frag +47 -0
- package/tm/web/web/src/cmp/view/custom-view.md.frag +31 -0
- package/tm/web/web/src/custom.css.frag +8 -0
- package/tm/web/web/src/customise.js.frag +30 -0
- package/tm/web/web/src/hooks.js.frag +79 -0
- package/tm/web/web/src/main.js.frag +17 -4
- package/tm/web/web/src/model.js.frag +162 -0
- package/tm/web/web/src/style.css.frag +135 -21
- package/tm/web/web/src/theme.js.frag +82 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Reference: the web app
|
|
2
|
+
|
|
3
|
+
*Diátaxis: reference — the generated SPA's structure, messages, hook
|
|
4
|
+
points, and theme contract.*
|
|
5
|
+
|
|
6
|
+
## Files ($$fe$$/src/)
|
|
7
|
+
|
|
8
|
+
| File | Role | Regenerated? |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| `main.js` | Entry: imports css, bus, components, views, customise | no |
|
|
11
|
+
| `bus.js` | The browser Seneca bus (transport to the backend gateway) | no |
|
|
12
|
+
| `model.js` | Runtime model: fetches `/model.json`; entities, refs graph, labels, project scoping, custom-view tags | no |
|
|
13
|
+
| `api.js` | Client for `aim:ent` CRUD + `aim:auth` | no |
|
|
14
|
+
| `hooks.js` | Customisation hook registry | no |
|
|
15
|
+
| `theme.js` | Theme mode controller (persisted; applies `data-theme-mode`) | no |
|
|
16
|
+
| `style.css` | App styles (consume `--vg-*` variables) | no |
|
|
17
|
+
| `theme.css` | CSS variables per theme mode | **yes — from the model** |
|
|
18
|
+
| `views.js` | Index importing custom entity views | **yes — from the model** |
|
|
19
|
+
| `customise.js` / `custom.css` | Project customisations | no (yours) |
|
|
20
|
+
| `cmp/app.js` | Router: signed-out → public, signed-in → shell | no |
|
|
21
|
+
| `cmp/public.js` | Public site + login mount | no |
|
|
22
|
+
| `cmp/auth.js` | Login / forgot-password form | no |
|
|
23
|
+
| `cmp/shell.js` | App shell: topbar, project selector, user menu, entity nav | no |
|
|
24
|
+
| `cmp/admin.js` | Generic entity admin (list/detail/form, relationship drill) | no |
|
|
25
|
+
| `cmp/settings.js` | Profile + change password | no |
|
|
26
|
+
| `cmp/view/*.js` | Custom entity views (one per `ux:{view:'custom'}` entity) | no (starters) |
|
|
27
|
+
|
|
28
|
+
## Backend messages used
|
|
29
|
+
|
|
30
|
+
**The browser may only send `aim:web` messages.** The gateway allow-list
|
|
31
|
+
names that one namespace, and every message the SPA sends is declared in
|
|
32
|
+
the model as an `aim:web` PROXY that forwards to the real service message
|
|
33
|
+
(`web_*` action files). Service namespaces (`aim:auth`, `aim:ent`,
|
|
34
|
+
`aim:api`, ...) are internal: posting one from a browser is rejected with
|
|
35
|
+
`not-allowed`. API-key clients have the same shape - the REST router
|
|
36
|
+
posts `aim:api`, which is likewise not browser-reachable.
|
|
37
|
+
|
|
38
|
+
| Browser message | Proxies to | Purpose |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `aim:web,on:ent,cmd:list,ent:<canon>` (+`q`) | `aim:ent,cmd:list` | List (membership/project-scoped) |
|
|
41
|
+
| `aim:web,on:ent,cmd:load,ent:<canon>` (+`id`) | `aim:ent,cmd:load` | Load one |
|
|
42
|
+
| `aim:web,on:ent,cmd:save,ent:<canon>` (+`item`) | `aim:ent,cmd:save` | Create/update |
|
|
43
|
+
| `aim:web,on:ent,cmd:remove,ent:<canon>` (+`id`) | `aim:ent,cmd:remove` | Delete |
|
|
44
|
+
| `aim:web,on:auth,load:auth` | `aim:auth,load:auth` | Current principal |
|
|
45
|
+
| `aim:web,on:auth,signin:user` / `signout:user` | `aim:auth,*` | Session (sets/clears the cookie) |
|
|
46
|
+
| `aim:web,on:auth,change:pass` / `update:user` | `aim:auth,*` | Settings & security |
|
|
47
|
+
| `aim:web,on:auth,remind:pass` | `aim:auth,remind:pass` | Password reminder (server stub) |
|
|
48
|
+
| `aim:web,on:auth,create:apikey` / `list:apikey` / `revoke:apikey` | `aim:auth,*` | API keys |
|
|
49
|
+
|
|
50
|
+
To expose a new operation to the browser, declare an `aim:web` proxy for
|
|
51
|
+
it in the model and implement the `web_*` action - never widen the
|
|
52
|
+
allow-list.
|
|
53
|
+
|
|
54
|
+
## Hook points
|
|
55
|
+
|
|
56
|
+
Register in `customise.js`; kinds: `html` (inject markup), `filter`
|
|
57
|
+
(transform a value), `action` (side effect). All synchronous,
|
|
58
|
+
failure-isolated.
|
|
59
|
+
|
|
60
|
+
| Point | Kind | Context / value |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `shell:topbar:right` | html | `{ user }` |
|
|
63
|
+
| `shell:sidebar:top` | html | `{ user }` |
|
|
64
|
+
| `shell:nav:items` | filter | entity list for the nav menu |
|
|
65
|
+
| `admin:list:toolbar` | html | list toolbar region |
|
|
66
|
+
| `admin:list:items` | filter | rows before render |
|
|
67
|
+
| `admin:list:columns` | filter | column defs |
|
|
68
|
+
| `admin:row:actions` | html | per-row action cell |
|
|
69
|
+
| `admin:list:after` | action | `{ root, canon, items }` |
|
|
70
|
+
| `admin:form:fields` | filter | form field defs |
|
|
71
|
+
| `admin:form:extra` | html | extra form markup |
|
|
72
|
+
| `admin:form:after` | action | after form render |
|
|
73
|
+
| `admin:save:data` | filter | payload before save |
|
|
74
|
+
| `admin:save:after` | action | after successful save |
|
|
75
|
+
| `public:sections` | html | extra public-site sections |
|
|
76
|
+
| `auth:form:footer` | html | login form footer |
|
|
77
|
+
| `settings:sections` | html | extra settings sections |
|
|
78
|
+
| `theme:modes` | filter | available theme mode list |
|
|
79
|
+
|
|
80
|
+
## Theme contract
|
|
81
|
+
|
|
82
|
+
- Model: `main.theme` = `{ mode, modes: { <name>: { <token>: <value> } } }`
|
|
83
|
+
(source: `backend/model/theme.aontu`).
|
|
84
|
+
- Generated CSS: each token → `--vg-<token>` under
|
|
85
|
+
`:root[data-theme-mode="<name>"]`; default mode also on `:root`.
|
|
86
|
+
- Runtime: `theme.js` sets `data-theme-mode` on `<html>`; choice persists
|
|
87
|
+
in localStorage `vg-theme-mode`; shell user menu toggles when more than
|
|
88
|
+
one mode exists.
|
|
89
|
+
- Standard tokens: `primary`, `primary-dark`, `bg`, `surface`, `text`,
|
|
90
|
+
`muted`, `border`, `topbar-bg`, `topbar-fg`, `accent-bg`, `font`,
|
|
91
|
+
`radius`, `shadow-card`.
|
|
92
|
+
|
|
93
|
+
## Custom entity view contract
|
|
94
|
+
|
|
95
|
+
Tag `vg-view-<zone>-<name>`; properties `canon`, `projectId`, `detailId`,
|
|
96
|
+
`onNavigate(canon, id)`; method `reload()`. Declared by
|
|
97
|
+
`ux: { view: 'custom' }` on the model entity.
|
|
98
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Agent guide: $$name$$ web app
|
|
2
|
+
|
|
3
|
+
The model-driven SPA for $$name$$: vanilla web components on a browser
|
|
4
|
+
Seneca message bus (no framework). Generated once by @voxgig/build EnvWeb;
|
|
5
|
+
now developer-owned. Backend guide: ../AGENTS.md. Concept docs:
|
|
6
|
+
../docs/ (see reference/web-app.md and explanation/web-architecture.md).
|
|
7
|
+
|
|
8
|
+
## Commands (run in web/)
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install
|
|
12
|
+
npm run dev # vite dev server (backend must be running: npm run web in backend/)
|
|
13
|
+
npm run e2e # playwright e2e (headless; starts its own backend)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
E2e tests run headless Chromium with workers=1 against a shared backend —
|
|
17
|
+
keep new specs independent of execution order but tolerant of shared seed
|
|
18
|
+
data.
|
|
19
|
+
|
|
20
|
+
## Architecture in one paragraph
|
|
21
|
+
|
|
22
|
+
`main.js` imports theme/css, the bus, all components, `views.js`
|
|
23
|
+
(generated index of custom views), and `customise.js` (project hooks).
|
|
24
|
+
All data flows are Seneca messages on `bus.js`. **Only `aim:web`
|
|
25
|
+
messages leave the browser** - that is the sole namespace the gateway
|
|
26
|
+
accepts, and each one is a declared proxy to a real service message;
|
|
27
|
+
`cmp:*` and events stay local. The transport pin is `aim:web` too, so a
|
|
28
|
+
stray service-message post fails locally instead of travelling. The UI is
|
|
29
|
+
model-driven at runtime — `model.js` fetches `/model.json` and derives
|
|
30
|
+
the entity menu, forms, and relationship navigation; `api.js` wraps the
|
|
31
|
+
`aim:web,on:ent,cmd:*` CRUD proxies and the `aim:web,on:auth,*` proxies.
|
|
32
|
+
|
|
33
|
+
## Editing rules
|
|
34
|
+
|
|
35
|
+
- Everything here is create-once EXCEPT `src/views.js` and
|
|
36
|
+
`src/theme.css` — those are REGENERATED from the model
|
|
37
|
+
(`npm run model-build` in backend/); never hand-edit them. Theme tokens
|
|
38
|
+
live in `backend/model/theme.aontu`; per-project overrides in
|
|
39
|
+
`src/custom.css`.
|
|
40
|
+
- Prefer customisation over editing generated components: hooks in
|
|
41
|
+
`src/customise.js` (see `src/hooks.js` for points), CSS variables in
|
|
42
|
+
`src/custom.css`, or a custom entity view (`ux:{view:'custom'}` in the
|
|
43
|
+
model). Edit components directly only when those layers can't express
|
|
44
|
+
the change.
|
|
45
|
+
- Components re-render by setting `innerHTML`; async renders must guard
|
|
46
|
+
against stale overwrites (see the render-token pattern in
|
|
47
|
+
`cmp/admin.js`: capture a token at method start, bail before writing if
|
|
48
|
+
superseded).
|
|
49
|
+
- `bus.sub` has no auto-unsubscribe: long-lived subscriptions in
|
|
50
|
+
components must guard callbacks with `this.isConnected`.
|
|
51
|
+
- Escape all user data with the local `esc()` helpers when building HTML.
|
|
52
|
+
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { test, expect } from '@playwright/test'
|
|
2
2
|
|
|
3
|
-
// Generic smoke test: the
|
|
4
|
-
// model-driven
|
|
5
|
-
//
|
|
3
|
+
// Generic smoke test: the public site loads, a seeded user can sign in, and
|
|
4
|
+
// the model-driven app shell renders with an entity menu. Add project-specific
|
|
5
|
+
// flows (CRUD, relationship drill, settings) in your own spec files.
|
|
6
6
|
|
|
7
|
-
test('sign in and
|
|
7
|
+
test('public site, sign in, and the app shell', async ({ page }) => {
|
|
8
8
|
await page.goto('/')
|
|
9
9
|
|
|
10
|
+
// Public content site with a login form.
|
|
10
11
|
await expect(page.locator('vg-auth h2')).toHaveText('Sign in')
|
|
11
12
|
|
|
12
|
-
await page.fill('input[name=email]', '$$seedEmail$$')
|
|
13
|
-
await page.fill('input[name=password]', '$$seedPassword$$')
|
|
13
|
+
await page.fill('vg-auth input[name=email]', '$$seedEmail$$')
|
|
14
|
+
await page.fill('vg-auth input[name=password]', '$$seedPassword$$')
|
|
14
15
|
await page.click('vg-auth button[type=submit]')
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
await expect(page.locator('
|
|
17
|
+
// The enterprise shell: top bar with the signed-in user, and a
|
|
18
|
+
// model-driven entity menu with at least one entity.
|
|
19
|
+
await expect(page.locator('.vg-shell')).toBeVisible()
|
|
20
|
+
await expect(page.locator('.vg-user-btn')).toContainText('$$seedEmail$$')
|
|
21
|
+
await expect(page.locator('.vg-navlink')).not.toHaveCount(0)
|
|
21
22
|
})
|
|
23
|
+
|
|
@@ -30,7 +30,7 @@ export default defineConfig({
|
|
|
30
30
|
},
|
|
31
31
|
],
|
|
32
32
|
webServer: {
|
|
33
|
-
command: `npm run build && cd ../backend && PORT=${PORT} node dist/env/web/web.js`,
|
|
33
|
+
command: `npm run build && cd ../backend && PORT=${PORT} REPL=false node dist/env/web/web.js`,
|
|
34
34
|
url: `http://localhost:${PORT}/`,
|
|
35
35
|
timeout: 60000,
|
|
36
36
|
reuseExistingServer: false,
|
|
@@ -38,3 +38,4 @@ export default defineConfig({
|
|
|
38
38
|
stderr: 'pipe',
|
|
39
39
|
},
|
|
40
40
|
})
|
|
41
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Thin client over the Seneca bus.
|
|
2
|
+
//
|
|
3
|
+
// EVERY message the browser sends is an aim:web message: that is the only
|
|
4
|
+
// namespace the backend gateway accepts (see backend/src/env/web/web.ts),
|
|
5
|
+
// and each one is a declared proxy that forwards to the real service
|
|
6
|
+
// message. Entity CRUD still lands on the ONE generic backend service, so
|
|
7
|
+
// the same four calls serve every entity in the model.
|
|
8
|
+
|
|
9
|
+
import { bus } from './bus.js'
|
|
10
|
+
|
|
11
|
+
// ---- generic entity CRUD ------------------------------------------------
|
|
12
|
+
|
|
13
|
+
async function list(ent, q) {
|
|
14
|
+
const r = await bus.post({ aim: 'web', on: 'ent', cmd: 'list', ent, q: q || {} })
|
|
15
|
+
return (r && r.ok && r.list) || []
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function load(ent, id) {
|
|
19
|
+
const r = await bus.post({ aim: 'web', on: 'ent', cmd: 'load', ent, id })
|
|
20
|
+
return r && r.ok ? r.item : null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function save(ent, item) {
|
|
24
|
+
return bus.post({ aim: 'web', on: 'ent', cmd: 'save', ent, item })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function remove(ent, id) {
|
|
28
|
+
return bus.post({ aim: 'web', on: 'ent', cmd: 'remove', ent, id })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Users, for reference pickers (read-only, public fields).
|
|
32
|
+
async function users() {
|
|
33
|
+
return list('sys/user')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ---- auth / settings ----------------------------------------------------
|
|
37
|
+
|
|
38
|
+
async function loadAuth() {
|
|
39
|
+
return bus.post('aim:web,on:auth,load:auth')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function signin(email, password) {
|
|
43
|
+
return bus.post('aim:web,on:auth,signin:user', { email, password })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function signout() {
|
|
47
|
+
return bus.post('aim:web,on:auth,signout:user')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function changePass(password) {
|
|
51
|
+
return bus.post({ aim: 'web', on: 'auth', change: 'pass', password })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function updateUser(data) {
|
|
55
|
+
return bus.post({ aim: 'web', on: 'auth', update: 'user', data })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function remindPass(email) {
|
|
59
|
+
return bus.post({ aim: 'web', on: 'auth', remind: 'pass', email })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ---- API access keys (REST API auth; Settings & security) ---------------
|
|
63
|
+
|
|
64
|
+
async function createApikey(name) {
|
|
65
|
+
return bus.post({ aim: 'web', on: 'auth', create: 'apikey', name })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function listApikeys() {
|
|
69
|
+
return bus.post({ aim: 'web', on: 'auth', list: 'apikey' })
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function revokeApikey(id) {
|
|
73
|
+
return bus.post({ aim: 'web', on: 'auth', revoke: 'apikey', id })
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export {
|
|
77
|
+
list,
|
|
78
|
+
load,
|
|
79
|
+
save,
|
|
80
|
+
remove,
|
|
81
|
+
users,
|
|
82
|
+
loadAuth,
|
|
83
|
+
signin,
|
|
84
|
+
signout,
|
|
85
|
+
changePass,
|
|
86
|
+
updateUser,
|
|
87
|
+
remindPass,
|
|
88
|
+
createApikey,
|
|
89
|
+
listApikeys,
|
|
90
|
+
revokeApikey,
|
|
91
|
+
}
|
|
92
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The browser-side Seneca service bus. All component state and all
|
|
2
2
|
// backend communication flow through messages on this bus:
|
|
3
3
|
//
|
|
4
|
-
// aim
|
|
4
|
+
// aim:web,* -> the backend gateway (/seneca) via the
|
|
5
5
|
// seneca-browser fetch transport (cookie auth)
|
|
6
6
|
// cmp:* -> local component plugins (state)
|
|
7
7
|
//
|
|
@@ -23,22 +23,27 @@ const bus = Seneca({
|
|
|
23
23
|
},
|
|
24
24
|
})
|
|
25
25
|
.test()
|
|
26
|
-
.client({ type: 'browser', pin: 'aim
|
|
26
|
+
.client({ type: 'browser', pin: 'aim:web' })
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
// Transparent state cache (Redux-like): caches aim
|
|
29
|
+
// Transparent state cache (Redux-like): caches aim:web query results and
|
|
30
30
|
// invalidates on writes, so repeated reads are served without a round-trip.
|
|
31
|
-
// Registered AFTER .client() so the aim
|
|
31
|
+
// Registered AFTER .client() so the aim:web pin actions exist to be wrapped.
|
|
32
32
|
if ('undefined' !== typeof SenecaBrowserStore) {
|
|
33
|
-
bus.use(SenecaBrowserStore, {
|
|
33
|
+
bus.use(SenecaBrowserStore, {
|
|
34
|
+
pin: 'aim:web',
|
|
35
|
+
// Defaults plus 'revoke': aim:web,on:auth,revoke:apikey must
|
|
36
|
+
// invalidate the cached list:apikey reads.
|
|
37
|
+
write: ['save', 'remove', 'update', 'delete', 'create', 'done', 'set', 'add', 'revoke'],
|
|
38
|
+
})
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
// In-window devtools: a resizable/hideable pop-up showing live Seneca
|
|
37
42
|
// message flows, config, and (via the store plugin above) the cache tree.
|
|
38
|
-
// aim
|
|
43
|
+
// aim:web messages are the remote (backend) calls. Loaded as a global script
|
|
39
44
|
// in index.html; guarded so a missing bundle never breaks the app.
|
|
40
45
|
if ('undefined' !== typeof SenecaBrowserDebug) {
|
|
41
|
-
bus.use(SenecaBrowserDebug, { remotePins: 'aim
|
|
46
|
+
bus.use(SenecaBrowserDebug, { remotePins: 'aim:web' })
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
|
|
@@ -75,4 +80,5 @@ export {
|
|
|
75
80
|
bus,
|
|
76
81
|
onEvent,
|
|
77
82
|
emit,
|
|
78
|
-
}
|
|
83
|
+
}
|
|
84
|
+
|