@jini-ai/user-management 0.2.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 +77 -0
- package/LICENSE +201 -0
- package/README.md +104 -0
- package/dist/admin/adapters/http.d.ts +29 -0
- package/dist/admin/adapters/http.js +69 -0
- package/dist/admin/adapters/memory.d.ts +61 -0
- package/dist/admin/adapters/memory.js +217 -0
- package/dist/admin/auth.module.d.ts +30 -0
- package/dist/admin/auth.module.js +12 -0
- package/dist/admin/conformance/index.d.ts +56 -0
- package/dist/admin/conformance/index.js +156 -0
- package/dist/admin/controllers/auth.controller.d.ts +23 -0
- package/dist/admin/controllers/auth.controller.js +33 -0
- package/dist/admin/controllers/controller-store.d.ts +18 -0
- package/dist/admin/controllers/controller-store.js +23 -0
- package/dist/admin/controllers/members.controller.d.ts +33 -0
- package/dist/admin/controllers/members.controller.js +119 -0
- package/dist/admin/controllers/roles.controller.d.ts +47 -0
- package/dist/admin/controllers/roles.controller.js +282 -0
- package/dist/admin/controllers/users.controller.d.ts +53 -0
- package/dist/admin/controllers/users.controller.js +220 -0
- package/dist/admin/index.d.ts +21 -0
- package/dist/admin/index.js +15 -0
- package/dist/admin/members.module.d.ts +34 -0
- package/dist/admin/members.module.js +13 -0
- package/dist/admin/messages.en.d.ts +37 -0
- package/dist/admin/messages.en.js +21 -0
- package/dist/admin/models.d.ts +133 -0
- package/dist/admin/models.js +2 -0
- package/dist/admin/people.rules.d.ts +29 -0
- package/dist/admin/people.rules.js +51 -0
- package/dist/admin/ports.d.ts +187 -0
- package/dist/admin/ports.js +17 -0
- package/dist/admin/react/components/DestructiveDialog.d.ts +5 -0
- package/dist/admin/react/components/DestructiveDialog.js +6 -0
- package/dist/admin/react/components/MemberRow.d.ts +5 -0
- package/dist/admin/react/components/MemberRow.js +8 -0
- package/dist/admin/react/components/MembersConfirmation.d.ts +2 -0
- package/dist/admin/react/components/MembersConfirmation.js +8 -0
- package/dist/admin/react/components/PermissionPanel.d.ts +5 -0
- package/dist/admin/react/components/PermissionPanel.js +9 -0
- package/dist/admin/react/components/PolicyRow.d.ts +5 -0
- package/dist/admin/react/components/PolicyRow.js +11 -0
- package/dist/admin/react/components/RoleRow.d.ts +5 -0
- package/dist/admin/react/components/RoleRow.js +10 -0
- package/dist/admin/react/components/UserRow.d.ts +5 -0
- package/dist/admin/react/components/UserRow.js +8 -0
- package/dist/admin/react/components/UsersConfirmation.d.ts +3 -0
- package/dist/admin/react/components/UsersConfirmation.js +9 -0
- package/dist/admin/react/hooks/AuthBinding.hooks.d.ts +39 -0
- package/dist/admin/react/hooks/AuthBinding.hooks.js +21 -0
- package/dist/admin/react/hooks/LoginPage.hooks.d.ts +54 -0
- package/dist/admin/react/hooks/LoginPage.hooks.js +23 -0
- package/dist/admin/react/hooks/MembersBinding.hooks.d.ts +35 -0
- package/dist/admin/react/hooks/MembersBinding.hooks.js +21 -0
- package/dist/admin/react/hooks/MembersPage.hooks.d.ts +99 -0
- package/dist/admin/react/hooks/MembersPage.hooks.js +38 -0
- package/dist/admin/react/hooks/PeopleController.hooks.d.ts +18 -0
- package/dist/admin/react/hooks/PeopleController.hooks.js +20 -0
- package/dist/admin/react/hooks/PermissionPanel.hooks.d.ts +30 -0
- package/dist/admin/react/hooks/PermissionPanel.hooks.js +15 -0
- package/dist/admin/react/hooks/PoliciesTab.hooks.d.ts +47 -0
- package/dist/admin/react/hooks/PoliciesTab.hooks.js +26 -0
- package/dist/admin/react/hooks/RolesBinding.hooks.d.ts +39 -0
- package/dist/admin/react/hooks/RolesBinding.hooks.js +23 -0
- package/dist/admin/react/hooks/RolesController.hooks.d.ts +43 -0
- package/dist/admin/react/hooks/RolesController.hooks.js +25 -0
- package/dist/admin/react/hooks/RolesPage.hooks.d.ts +105 -0
- package/dist/admin/react/hooks/RolesPage.hooks.js +33 -0
- package/dist/admin/react/hooks/RolesTab.hooks.d.ts +35 -0
- package/dist/admin/react/hooks/RolesTab.hooks.js +19 -0
- package/dist/admin/react/hooks/UsersBinding.hooks.d.ts +36 -0
- package/dist/admin/react/hooks/UsersBinding.hooks.js +21 -0
- package/dist/admin/react/hooks/UsersPage.hooks.d.ts +106 -0
- package/dist/admin/react/hooks/UsersPage.hooks.js +22 -0
- package/dist/admin/react/hooks/UsersTab.hooks.d.ts +101 -0
- package/dist/admin/react/hooks/UsersTab.hooks.js +53 -0
- package/dist/admin/react/index.d.ts +203 -0
- package/dist/admin/react/index.js +32 -0
- package/dist/admin/react/pages/LoginPage.d.ts +3 -0
- package/dist/admin/react/pages/LoginPage.js +13 -0
- package/dist/admin/react/pages/MembersPage.d.ts +3 -0
- package/dist/admin/react/pages/MembersPage.js +19 -0
- package/dist/admin/react/pages/RolesPage.d.ts +3 -0
- package/dist/admin/react/pages/RolesPage.js +19 -0
- package/dist/admin/react/pages/UsersPage.d.ts +3 -0
- package/dist/admin/react/pages/UsersPage.js +19 -0
- package/dist/admin/react/tabs/AuthTab.d.ts +4 -0
- package/dist/admin/react/tabs/AuthTab.js +8 -0
- package/dist/admin/react/tabs/MembersTab.d.ts +4 -0
- package/dist/admin/react/tabs/MembersTab.js +9 -0
- package/dist/admin/react/tabs/PoliciesTab.d.ts +4 -0
- package/dist/admin/react/tabs/PoliciesTab.js +9 -0
- package/dist/admin/react/tabs/RolesTab.d.ts +4 -0
- package/dist/admin/react/tabs/RolesTab.js +9 -0
- package/dist/admin/react/tabs/UsersTab.d.ts +4 -0
- package/dist/admin/react/tabs/UsersTab.js +9 -0
- package/dist/admin/roles.module.d.ts +48 -0
- package/dist/admin/roles.module.js +15 -0
- package/dist/admin/rules.d.ts +32 -0
- package/dist/admin/rules.js +32 -0
- package/dist/admin/users.module.d.ts +39 -0
- package/dist/admin/users.module.js +14 -0
- package/dist/core/builtin-permissions.d.ts +10 -0
- package/dist/core/builtin-permissions.js +402 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +9 -0
- package/dist/core/permissions.d.ts +40 -0
- package/dist/core/permissions.js +52 -0
- package/dist/core/ports.d.ts +237 -0
- package/dist/core/ports.js +2 -0
- package/dist/core/runtime-ports.d.ts +8 -0
- package/dist/core/runtime-ports.js +2 -0
- package/dist/core/types.d.ts +224 -0
- package/dist/core/types.js +93 -0
- package/dist/react/auth/Login.d.ts +9 -0
- package/dist/react/auth/Login.js +8 -0
- package/dist/react/auth/hooks/use-login.hooks.d.ts +22 -0
- package/dist/react/auth/hooks/use-login.hooks.js +24 -0
- package/dist/react/errors.d.ts +10 -0
- package/dist/react/errors.js +13 -0
- package/dist/react/index.d.ts +18 -0
- package/dist/react/index.js +9 -0
- package/dist/react/members/Members.d.ts +6 -0
- package/dist/react/members/Members.js +38 -0
- package/dist/react/members/hooks/use-members.hooks.d.ts +32 -0
- package/dist/react/members/hooks/use-members.hooks.js +120 -0
- package/dist/react/members/rules.d.ts +26 -0
- package/dist/react/members/rules.js +35 -0
- package/dist/react/models.d.ts +50 -0
- package/dist/react/models.js +2 -0
- package/dist/react/ports.d.ts +157 -0
- package/dist/react/ports.js +2 -0
- package/dist/react/roles/Roles.d.ts +100 -0
- package/dist/react/roles/Roles.hooks.d.ts +17 -0
- package/dist/react/roles/Roles.hooks.js +40 -0
- package/dist/react/roles/Roles.js +150 -0
- package/dist/react/roles/hooks/use-roles.hooks.d.ts +80 -0
- package/dist/react/roles/hooks/use-roles.hooks.js +303 -0
- package/dist/react/roles/rules.d.ts +35 -0
- package/dist/react/roles/rules.js +31 -0
- package/dist/react/roles/text.d.ts +27 -0
- package/dist/react/roles/text.js +13 -0
- package/dist/react/testing.d.ts +51 -0
- package/dist/react/testing.js +275 -0
- package/dist/react/users/Users.d.ts +72 -0
- package/dist/react/users/Users.js +129 -0
- package/dist/react/users/hooks/use-reset-password-fields.hooks.d.ts +16 -0
- package/dist/react/users/hooks/use-reset-password-fields.hooks.js +21 -0
- package/dist/react/users/hooks/use-users.hooks.d.ts +77 -0
- package/dist/react/users/hooks/use-users.hooks.js +286 -0
- package/dist/react/users/rules.d.ts +33 -0
- package/dist/react/users/rules.js +61 -0
- package/dist/server/admin-crud-service.d.ts +295 -0
- package/dist/server/admin-crud-service.js +561 -0
- package/dist/server/agent-tool-input.d.ts +58 -0
- package/dist/server/agent-tool-input.js +158 -0
- package/dist/server/agent-tool-registrations.d.ts +51 -0
- package/dist/server/agent-tool-registrations.js +417 -0
- package/dist/server/agent-tools.d.ts +88 -0
- package/dist/server/agent-tools.js +229 -0
- package/dist/server/auth-service.d.ts +107 -0
- package/dist/server/auth-service.js +170 -0
- package/dist/server/authorize.d.ts +92 -0
- package/dist/server/authorize.js +96 -0
- package/dist/server/grant-service.d.ts +219 -0
- package/dist/server/grant-service.js +419 -0
- package/dist/server/hasher.d.ts +64 -0
- package/dist/server/hasher.js +53 -0
- package/dist/server/identity-transaction.d.ts +9 -0
- package/dist/server/identity-transaction.js +8 -0
- package/dist/server/index.d.ts +18 -0
- package/dist/server/index.js +20 -0
- package/dist/server/messages.d.ts +7 -0
- package/dist/server/messages.js +5 -0
- package/dist/server/password-policy.d.ts +59 -0
- package/dist/server/password-policy.js +66 -0
- package/dist/server/permission-migrations.d.ts +84 -0
- package/dist/server/permission-migrations.js +77 -0
- package/dist/server/repo.memory-transactions.d.ts +16 -0
- package/dist/server/repo.memory-transactions.js +79 -0
- package/dist/server/repo.memory.d.ts +186 -0
- package/dist/server/repo.memory.js +230 -0
- package/dist/server/seed.d.ts +56 -0
- package/dist/server/seed.js +435 -0
- package/dist/server/session-tokens.d.ts +10 -0
- package/dist/server/session-tokens.js +12 -0
- package/dist/server/username.d.ts +14 -0
- package/dist/server/username.js +14 -0
- package/package.json +144 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import { nowIso as kernelNowIso } from "@jini-ai/core/primitives";
|
|
2
|
+
import { normalizeUsername } from "./username.js";
|
|
3
|
+
/**
|
|
4
|
+
* @file First-boot identity seed (state.spec `SEED_FIRST_BOOT`).
|
|
5
|
+
*
|
|
6
|
+
* Purpose:
|
|
7
|
+
* Seeds, in one idempotent pass: a `system` principal; a disabled legacy
|
|
8
|
+
* `user-local` principal ( — so any pre-existing `change_sets`
|
|
9
|
+
* stamped `actorId='user-local'` satisfy the composite FK without rewriting
|
|
10
|
+
* history); four built-in roles (`owner`/`admin`/`editor`/
|
|
11
|
+
* `viewer`) each 1:1 with a built-in policy; and the initial `owner` user.
|
|
12
|
+
*
|
|
13
|
+
* How it relates to the project:
|
|
14
|
+
* - Called once by each composition root (`server/app.ts` in-memory,
|
|
15
|
+
* `server/deps.ts` SQLite-content-with-in-memory-identity) before the
|
|
16
|
+
* server accepts requests. `RouteDeps.identityReady` is this call's
|
|
17
|
+
* promise — auth-adjacent middleware awaits it (see `middleware/dev-auth.ts`).
|
|
18
|
+
*
|
|
19
|
+
* Architectural role:
|
|
20
|
+
* Seed data lives in one place so both composition roots stay identical
|
|
21
|
+
* (mirrors `server/seed.ts`'s reasoning for workspace/post/presentation seed
|
|
22
|
+
* data). Idempotent: re-seeding an already-seeded workspace is a no-op
|
|
23
|
+
* (checked via the owner username).
|
|
24
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
25
|
+
*/
|
|
26
|
+
/** : the literal id the legacy Article VI actor stamp resolves to. See docs/decisions/DR-001-identity-and-session-boundary.md. */
|
|
27
|
+
const LEGACY_USER_LOCAL_PRINCIPAL_ID = "user-local";
|
|
28
|
+
/**
|
|
29
|
+
* The built-in role the seeded owner user holds, and its authority. Named constants because two
|
|
30
|
+
* call sites now depend on them agreeing exactly — the fresh seed below and `ensureOwnerRoleBinding`,
|
|
31
|
+
* which repairs an interrupted one. Drift between the two would be a silent authority bug.
|
|
32
|
+
*
|
|
33
|
+
* the owner policy holds the wildcard `*`, not an enumerated list, so it
|
|
34
|
+
* automatically covers permissions features register later.
|
|
35
|
+
* See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
36
|
+
*/
|
|
37
|
+
const OWNER_ROLE_NAME = "owner";
|
|
38
|
+
const OWNER_ROLE_PERMISSIONS = ["*"];
|
|
39
|
+
const BUILTIN_ADMIN_PERMISSIONS = [
|
|
40
|
+
"content.read",
|
|
41
|
+
"content.write",
|
|
42
|
+
"content.publish",
|
|
43
|
+
"content.delete",
|
|
44
|
+
"theme.set",
|
|
45
|
+
// Editing a theme's source files is an admin capability, deliberately NOT in the editor set
|
|
46
|
+
// below (which keeps theme.set): an editor may switch the site between validated themes, but
|
|
47
|
+
// authoring template/CSS source is a build-time-shaped capability whose failure mode is a broken
|
|
48
|
+
// theme, not a different-looking one.
|
|
49
|
+
"theme.edit",
|
|
50
|
+
"plugin.read",
|
|
51
|
+
"plugin.enable",
|
|
52
|
+
"plugin.disable",
|
|
53
|
+
"changeset.read",
|
|
54
|
+
"changeset.revert",
|
|
55
|
+
"member.manage",
|
|
56
|
+
"settings.write",
|
|
57
|
+
"apikey.manage",
|
|
58
|
+
// Migration clause (mirrors the admin.menus.*/admin.integrations.manage
|
|
59
|
+
// precedent above): every freshly-seeded workspace's admin role gets the concrete media.* working
|
|
60
|
+
// actions directly, so it never depends on migrateDeprecatedPermissionGrants(). The umbrella
|
|
61
|
+
// "media.manage" is deliberately not included here (mirrors admin.menus.manage's exclusion
|
|
62
|
+
// above). "media.write" is deliberately dropped from THIS seed list only — the string stays
|
|
63
|
+
// registered (deprecated) in identity/permissions.ts.
|
|
64
|
+
"media.read",
|
|
65
|
+
"media.upload",
|
|
66
|
+
"media.update",
|
|
67
|
+
"media.delete",
|
|
68
|
+
"media.delete.force",
|
|
69
|
+
"media.download_original",
|
|
70
|
+
"media.upload_svg",
|
|
71
|
+
// Menus remediation migration clause: every freshly-seeded
|
|
72
|
+
// workspace gets the 6 new admin.menus.* CRUD strings directly, so it never depends on
|
|
73
|
+
// migrateDeprecatedPermissionGrants() for its own built-in role grants. The now-legacy
|
|
74
|
+
// "navigation.manage" is deliberately dropped from THIS seed list only — the string itself
|
|
75
|
+
// stays registered (deprecated) in identity/permissions.ts. migrateDeprecatedPermissionGrants()
|
|
76
|
+
// now IS wired into live boot (identity/wiring.ts, chained right after seedIdentity resolves)
|
|
77
|
+
// so any pre-existing policy still holding navigation.manage/integration.manage also gains
|
|
78
|
+
// the new string(s) — this fresh-seed list just doesn't need to depend on that fan-out for its
|
|
79
|
+
// own built-in role grants.
|
|
80
|
+
"admin.menus.read",
|
|
81
|
+
"admin.menus.create",
|
|
82
|
+
"admin.menus.update",
|
|
83
|
+
"admin.menus.delete",
|
|
84
|
+
"admin.menus.delete.force",
|
|
85
|
+
"admin.menus.assign",
|
|
86
|
+
// Migration clause (mirrors the admin.menus.* precedent immediately
|
|
87
|
+
// above): every freshly-seeded workspace gets admin.integrations.manage directly, so it never
|
|
88
|
+
// depends on migrateDeprecatedPermissionGrants(). "integration.manage" is deliberately dropped
|
|
89
|
+
// from THIS seed list only — the string stays registered (deprecated) in permissions.ts.
|
|
90
|
+
"admin.integrations.manage",
|
|
91
|
+
// Migration clause: every settings.write holder also
|
|
92
|
+
// gets settings.definitions.manage, so the admin role isn't left
|
|
93
|
+
// fail-closed-locked-out of definition-lifecycle operations it previously
|
|
94
|
+
// reached through the coarse settings.write grant. No separate migration
|
|
95
|
+
// script is needed pre-launch — this seed function is the sole source of
|
|
96
|
+
// built-in role grants (no existing installation's data to migrate yet).
|
|
97
|
+
"settings.definitions.manage",
|
|
98
|
+
"settings.global.write",
|
|
99
|
+
"settings.workspace.write",
|
|
100
|
+
"settings.user.self.write",
|
|
101
|
+
"settings.user.write",
|
|
102
|
+
// Internal audit F2 remediation (2026-07-29): granted directly here so a freshly-seeded
|
|
103
|
+
// workspace never depends on the `settings.user.write` -> `settings.user.read` fan-out in
|
|
104
|
+
// permissions.ts (same "don't depend on migrateDeprecatedPermissionGrants for our own built-in
|
|
105
|
+
// role grants" discipline as the admin.menus.*/admin.integrations.manage clauses above). The
|
|
106
|
+
// fan-out exists for ALREADY-seeded installations, which this list cannot reach because
|
|
107
|
+
// seedIdentity early-returns once an owner user exists.
|
|
108
|
+
"settings.user.read",
|
|
109
|
+
"settings.reset.global",
|
|
110
|
+
"settings.reset.workspace",
|
|
111
|
+
"settings.reset.user",
|
|
112
|
+
"settings.read",
|
|
113
|
+
"settings.read.raw",
|
|
114
|
+
"settings.read.revisions",
|
|
115
|
+
"settings.read.definitions",
|
|
116
|
+
// Every freshly-seeded workspace's built-in admin role
|
|
117
|
+
// gets the one SEO umbrella permission directly, mirroring the admin.menus.* precedent above.
|
|
118
|
+
"admin.seo.manage",
|
|
119
|
+
// The AI Assistant section's umbrella permission, granted directly here for the same reason
|
|
120
|
+
// `admin.seo.manage` above is: a freshly-seeded workspace's built-in admin must be able to reach
|
|
121
|
+
// the public assistant's master switch without depending on any deprecated-grant fan-out. Turning
|
|
122
|
+
// the public assistant ON is still a deliberate act — the SETTING defaults to off
|
|
123
|
+
// (`assistant/public-assistant-settings.ts`); this only grants the ability to flip it.
|
|
124
|
+
"admin.assistant.manage",
|
|
125
|
+
// Workspace Administration: admin gets workspace.manage
|
|
126
|
+
// directly at seed — a distinct grant from settings.write, not owner-only (unlike user.manage/
|
|
127
|
+
// role.manage below).
|
|
128
|
+
"workspace.manage",
|
|
129
|
+
// Owner-only per : "user.manage", "role.manage" are deliberately absent. See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
130
|
+
];
|
|
131
|
+
const BUILTIN_EDITOR_PERMISSIONS = [
|
|
132
|
+
"content.read",
|
|
133
|
+
"content.write",
|
|
134
|
+
"content.publish",
|
|
135
|
+
"content.delete",
|
|
136
|
+
// Migration clause: editor gets the ordinary media working actions
|
|
137
|
+
// (read/upload/update/trash) but, unlike admin above, NOT media.delete.force (destructive
|
|
138
|
+
// hard-purge), media.download_original (mint-only access to sensitive originals), or
|
|
139
|
+
// media.upload_svg (XSS-risk-gated capability) — mirrors the Forms admin.forms.manage vs
|
|
140
|
+
// admin.forms.submissions.* PII-split precedent rather than granting editor
|
|
141
|
+
// everything admin.write's single flat string previously implied. "media.write" is deliberately
|
|
142
|
+
// dropped from THIS seed list only — the string stays registered (deprecated) in
|
|
143
|
+
// identity/permissions.ts. Still a subset of admin's media.* set above ( owner ⊇ admin ⊇. See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
144
|
+
// editor subset property).
|
|
145
|
+
"media.read",
|
|
146
|
+
"media.upload",
|
|
147
|
+
"media.update",
|
|
148
|
+
"media.delete",
|
|
149
|
+
"theme.set",
|
|
150
|
+
];
|
|
151
|
+
const BUILTIN_VIEWER_PERMISSIONS = ["content.read", "changeset.read", "plugin.read"];
|
|
152
|
+
/**
|
|
153
|
+
* Why every step below is "find, then save only what is missing" rather than a straight insert.
|
|
154
|
+
*
|
|
155
|
+
* This function's writes are NOT transactional and its caller is not awaited by its hosts (one host
|
|
156
|
+
* hands `seedIdentity`'s promise back as `RouteDeps.identityReady` and returns synchronously), so
|
|
157
|
+
* a process that exits before the seed finishes — a fast-failing CLI command, a Ctrl-C, a crash —
|
|
158
|
+
* leaves the workspace half-seeded. `seedIdentity`'s own early-return guard keys on the OWNER USER,
|
|
159
|
+
* which is written last, so the next boot correctly decides the seed is unfinished and replays it.
|
|
160
|
+
* A replay that blindly re-inserted would hit `UNIQUE(workspace_id, name)` on `roles`/`policies`
|
|
161
|
+
* (crashing every subsequent boot, permanently), and would silently duplicate `role_policies` /
|
|
162
|
+
* `policy_permissions` rows, which carry no unique index at all. So the replay must converge onto
|
|
163
|
+
* the rows already there instead of re-creating them.
|
|
164
|
+
*
|
|
165
|
+
* What this deliberately does NOT do: adopt a role or policy an operator created themselves. A
|
|
166
|
+
* same-named non-built-in row is a genuine conflict, not a partial seed, and silently promoting it
|
|
167
|
+
* would hand its holders the built-in policy's permissions. Those cases throw.
|
|
168
|
+
*/
|
|
169
|
+
/** The built-in role for `name`, reusing a prior (possibly interrupted) seed's row if present. */
|
|
170
|
+
async function ensureBuiltinRole(required) {
|
|
171
|
+
const { deps, workspaceId, name } = required;
|
|
172
|
+
const existing = await deps.repos.roles.findByName({ workspaceId, name });
|
|
173
|
+
if (!existing) {
|
|
174
|
+
const roleId = deps.idGen.newId();
|
|
175
|
+
await deps.repos.roles.save({ id: roleId, workspaceId, name, isBuiltin: true });
|
|
176
|
+
return roleId;
|
|
177
|
+
}
|
|
178
|
+
if (!existing.isBuiltin) {
|
|
179
|
+
throw new Error(`seedIdentity: a non-built-in role named '${name}' already exists in workspace ${workspaceId}. ` +
|
|
180
|
+
`Refusing to adopt it as the built-in '${name}' role — rename or remove it first.`);
|
|
181
|
+
}
|
|
182
|
+
return existing.id;
|
|
183
|
+
}
|
|
184
|
+
/** The built-in policy for `name`, reusing a prior (possibly interrupted) seed's row if present. */
|
|
185
|
+
async function ensureBuiltinPolicy(required) {
|
|
186
|
+
const { deps, workspaceId, name } = required;
|
|
187
|
+
const policyName = `${name}-builtin-policy`;
|
|
188
|
+
const existing = await deps.repos.policies.findByName({ workspaceId, name: policyName });
|
|
189
|
+
if (!existing) {
|
|
190
|
+
const policyId = deps.idGen.newId();
|
|
191
|
+
await deps.repos.policies.save({
|
|
192
|
+
id: policyId,
|
|
193
|
+
workspaceId,
|
|
194
|
+
name: policyName,
|
|
195
|
+
description: `Built-in policy for the seeded '${name}' role.`,
|
|
196
|
+
isBuiltin: true,
|
|
197
|
+
isFrozen: false,
|
|
198
|
+
});
|
|
199
|
+
return policyId;
|
|
200
|
+
}
|
|
201
|
+
if (!existing.isBuiltin) {
|
|
202
|
+
throw new Error(`seedIdentity: a non-built-in policy named '${policyName}' already exists in workspace ${workspaceId}. ` +
|
|
203
|
+
`Refusing to adopt it as the built-in '${name}' policy — rename or remove it first.`);
|
|
204
|
+
}
|
|
205
|
+
return existing.id;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Grant `permissions` to `policyId`, skipping any the policy already holds.
|
|
209
|
+
*
|
|
210
|
+
* Additive by design, and the reason a resumed seed still CONVERGES rather than merely staying
|
|
211
|
+
* quiet: a built-in policy whose permission rows never landed (the interrupt fell between the
|
|
212
|
+
* policy insert and this loop) gains them on the next boot. Compares only unscoped rows because
|
|
213
|
+
* that is the only shape this seed writes — a `resourceType`-scoped row of the same name is a
|
|
214
|
+
* different grant and must not satisfy an unscoped one. A constrained row likewise cannot
|
|
215
|
+
* satisfy this seed because authorization rejects every non-null constraint, including an empty
|
|
216
|
+
* string. Null and absent scope/constraint have the same unconstrained meaning in the evaluator.
|
|
217
|
+
*/
|
|
218
|
+
async function ensurePolicyPermissions(required) {
|
|
219
|
+
const { deps, workspaceId, policyId, permissions } = required;
|
|
220
|
+
const existing = await deps.repos.policyPermissions.listByPolicyId({ workspaceId, policyId });
|
|
221
|
+
const held = new Set(existing.filter((row) => row.resourceType == null && row.constraintJson == null).map((row) => row.permission));
|
|
222
|
+
for (const permission of permissions) {
|
|
223
|
+
if (held.has(permission))
|
|
224
|
+
continue;
|
|
225
|
+
await deps.repos.policyPermissions.save({
|
|
226
|
+
id: deps.idGen.newId(),
|
|
227
|
+
workspaceId,
|
|
228
|
+
policyId,
|
|
229
|
+
permission,
|
|
230
|
+
resourceType: null,
|
|
231
|
+
constraintJson: null,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/** Builds one built-in role + its 1:1 policy + policy_permissions rows, converging on what exists. */
|
|
236
|
+
async function seedBuiltinRoleWithPolicy(required) {
|
|
237
|
+
const { deps, workspaceId, name, permissions, nowIso } = required;
|
|
238
|
+
const roleId = await ensureBuiltinRole({ deps, workspaceId, name });
|
|
239
|
+
const policyId = await ensureBuiltinPolicy({ deps, workspaceId, name });
|
|
240
|
+
// `role_policies` has no unique index, so a replay would otherwise stack a second identical link.
|
|
241
|
+
const links = await deps.repos.rolePolicies.listByRoleId({ workspaceId, roleId });
|
|
242
|
+
if (!links.some((link) => link.policyId === policyId)) {
|
|
243
|
+
await deps.repos.rolePolicies.save({ id: deps.idGen.newId(), workspaceId, roleId, policyId });
|
|
244
|
+
}
|
|
245
|
+
await ensurePolicyPermissions({ deps, workspaceId, policyId, permissions });
|
|
246
|
+
void nowIso; // reserved for a future createdAt column on roles/policies
|
|
247
|
+
return { roleId, policyId };
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Repair the one completion step `seedIdentity`'s owner-user guard cannot see.
|
|
251
|
+
*
|
|
252
|
+
* The guard keys on the owner USER, and the owner's `principal_roles` link is written after it. A
|
|
253
|
+
* process that exits between those two adjacent writes leaves a workspace whose owner exists, can
|
|
254
|
+
* still log in, and holds no role — and `resolveEffectivePermissions` (`authorize.ts`) starts from
|
|
255
|
+
* `principal_roles` + `principal_policies`, so with neither present EVERY permission evaluates to
|
|
256
|
+
* `no_grant`. Without this step the guard early-returns on the user it finds forever and the site
|
|
257
|
+
* stays bricked for its owner, silently. Neither host-side reconciler closes it either: the host's
|
|
258
|
+
* `migrateDeprecatedPermissionGrants` and `applyBuiltinRoleGrants` are handed policy and role repos
|
|
259
|
+
* only, never `principalRoles`, so neither can write a principal->role link at all.
|
|
260
|
+
*
|
|
261
|
+
* Why the gate is "holds NO role" rather than "lacks the owner role". Those two conditions differ
|
|
262
|
+
* on exactly one workspace shape, and it matters: an operator who moves the seeded owner principal
|
|
263
|
+
* to a lesser role leaves it lacking the owner link on purpose. Repairing on that broader signal
|
|
264
|
+
* would re-escalate the principal to wildcard authority on the next restart — a privilege
|
|
265
|
+
* escalation shipped as a bug fix. A principal holding no role at all is never a deliberate state;
|
|
266
|
+
* it is only ever the interrupted seed, so that is what this repairs.
|
|
267
|
+
*
|
|
268
|
+
* Converges rather than assuming: it goes through `seedBuiltinRoleWithPolicy`, so a workspace whose
|
|
269
|
+
* owner ROLE or wildcard grant also never landed gains those here instead of being linked to a role
|
|
270
|
+
* that grants nothing. `principal_roles` carries no unique index, hence the read-before-write.
|
|
271
|
+
*
|
|
272
|
+
* @complexity O(1) — a handful of point reads on a healthy boot (one `listByPrincipalId`, which
|
|
273
|
+
* returns early), plus the fixed converge pass for the owner role only when the repair fires.
|
|
274
|
+
*/
|
|
275
|
+
async function ensureOwnerRoleBinding(required) {
|
|
276
|
+
const { deps, workspaceId, ownerPrincipalId, nowIso } = required;
|
|
277
|
+
const heldRoles = await deps.repos.principalRoles.listByPrincipalId({
|
|
278
|
+
workspaceId,
|
|
279
|
+
principalId: ownerPrincipalId,
|
|
280
|
+
});
|
|
281
|
+
if (heldRoles.length > 0)
|
|
282
|
+
return;
|
|
283
|
+
const { roleId } = await seedBuiltinRoleWithPolicy({
|
|
284
|
+
deps,
|
|
285
|
+
workspaceId,
|
|
286
|
+
name: OWNER_ROLE_NAME,
|
|
287
|
+
permissions: OWNER_ROLE_PERMISSIONS,
|
|
288
|
+
nowIso,
|
|
289
|
+
});
|
|
290
|
+
await deps.repos.principalRoles.save({
|
|
291
|
+
id: deps.idGen.newId(),
|
|
292
|
+
workspaceId,
|
|
293
|
+
principalId: ownerPrincipalId,
|
|
294
|
+
roleId,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Seed first-boot identity data.
|
|
299
|
+
*
|
|
300
|
+
* Idempotent in two distinct senses, and both are load-bearing:
|
|
301
|
+
* - A seed that RAN TO COMPLETION is a no-op — the owner-username lookup below early-returns after
|
|
302
|
+
* a single confirming read (`ensureOwnerRoleBinding`) and writes nothing. (This path does not
|
|
303
|
+
* reconcile a built-in policy whose permission list has since grown; that is the host's job — see
|
|
304
|
+
* the host's `applyBuiltinRoleGrants` / `migrateDeprecatedPermissionGrants`, chained onto
|
|
305
|
+
* `identityReady`.)
|
|
306
|
+
* - "Ran to completion" is judged on the owner user AND its role link, not the user alone. The link
|
|
307
|
+
* is the seed's last write, so the user is not on its own evidence the seed finished — see
|
|
308
|
+
* `ensureOwnerRoleBinding` for what an interrupt between those two writes costs.
|
|
309
|
+
* - A seed that was INTERRUPTED is resumed, not replayed: every step converges onto the rows a
|
|
310
|
+
* previous attempt already wrote and fills in only what is missing. See the block comment above
|
|
311
|
+
* `ensureBuiltinRole` for why this function must survive being killed halfway.
|
|
312
|
+
*
|
|
313
|
+
* @complexity O(1) — fixed small number of inserts (4 roles/policies, ~2
|
|
314
|
+
* dozen policy_permissions, 3 principals, 1 user, 1 role assignment).
|
|
315
|
+
* @overallScore 100
|
|
316
|
+
*/
|
|
317
|
+
export async function seedIdentity(required, optional = {}) {
|
|
318
|
+
const { deps } = required;
|
|
319
|
+
const input = { ...required.input, ...optional };
|
|
320
|
+
const workspaceId = input.workspaceId;
|
|
321
|
+
const ownerUsername = normalizeUsername({ raw: input.ownerUsername });
|
|
322
|
+
const ownerPassword = input.ownerPassword;
|
|
323
|
+
const nowIso = kernelNowIso({ clock: deps.clock });
|
|
324
|
+
const existingOwnerUser = await deps.repos.users.findByUsername({
|
|
325
|
+
workspaceId,
|
|
326
|
+
username: ownerUsername,
|
|
327
|
+
});
|
|
328
|
+
if (existingOwnerUser) {
|
|
329
|
+
// The owner user alone is NOT proof the seed finished — its role link is written after it. See
|
|
330
|
+
// `ensureOwnerRoleBinding`: on a healthy workspace this is one read and no write.
|
|
331
|
+
await ensureOwnerRoleBinding({
|
|
332
|
+
deps,
|
|
333
|
+
workspaceId,
|
|
334
|
+
ownerPrincipalId: existingOwnerUser.principalId,
|
|
335
|
+
nowIso,
|
|
336
|
+
});
|
|
337
|
+
const existingSystem = (await deps.repos.principals.list({ workspaceId })).find((row) => row.kind === "system" && row.id !== LEGACY_USER_LOCAL_PRINCIPAL_ID);
|
|
338
|
+
return {
|
|
339
|
+
ownerPrincipalId: existingOwnerUser.principalId,
|
|
340
|
+
systemPrincipalId: existingSystem?.id ?? LEGACY_USER_LOCAL_PRINCIPAL_ID,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
// Reuse the system principal a prior interrupted seed may already have written: `principals` has
|
|
344
|
+
// no unique index on kind, so a blind re-insert would leave TWO non-legacy system principals and
|
|
345
|
+
// the early-return branch above picking between them by list order.
|
|
346
|
+
const existingSystem = (await deps.repos.principals.list({ workspaceId })).find((row) => row.kind === "system" && row.id !== LEGACY_USER_LOCAL_PRINCIPAL_ID);
|
|
347
|
+
const systemPrincipalId = existingSystem?.id ?? deps.idGen.newId();
|
|
348
|
+
if (!existingSystem) {
|
|
349
|
+
await deps.repos.principals.save({
|
|
350
|
+
id: systemPrincipalId,
|
|
351
|
+
workspaceId,
|
|
352
|
+
kind: "system",
|
|
353
|
+
displayName: "System",
|
|
354
|
+
status: "active",
|
|
355
|
+
createdAt: nowIso,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
// disabled legacy actor so historical `actorId='user-local'`. See docs/decisions/DR-001-identity-and-session-boundary.md.
|
|
359
|
+
// change-sets resolve without rewriting history.
|
|
360
|
+
await deps.repos.principals.save({
|
|
361
|
+
id: LEGACY_USER_LOCAL_PRINCIPAL_ID,
|
|
362
|
+
workspaceId,
|
|
363
|
+
kind: "system",
|
|
364
|
+
displayName: "Legacy actor (pre-identity)",
|
|
365
|
+
status: "disabled",
|
|
366
|
+
disabledAt: nowIso,
|
|
367
|
+
createdAt: nowIso,
|
|
368
|
+
});
|
|
369
|
+
const { roleId: ownerRoleId } = await seedBuiltinRoleWithPolicy({
|
|
370
|
+
deps,
|
|
371
|
+
workspaceId,
|
|
372
|
+
name: OWNER_ROLE_NAME,
|
|
373
|
+
permissions: OWNER_ROLE_PERMISSIONS,
|
|
374
|
+
nowIso,
|
|
375
|
+
});
|
|
376
|
+
await seedBuiltinRoleWithPolicy({
|
|
377
|
+
deps,
|
|
378
|
+
workspaceId,
|
|
379
|
+
name: "admin",
|
|
380
|
+
permissions: BUILTIN_ADMIN_PERMISSIONS,
|
|
381
|
+
nowIso,
|
|
382
|
+
});
|
|
383
|
+
await seedBuiltinRoleWithPolicy({
|
|
384
|
+
deps,
|
|
385
|
+
workspaceId,
|
|
386
|
+
name: "editor",
|
|
387
|
+
permissions: BUILTIN_EDITOR_PERMISSIONS,
|
|
388
|
+
nowIso,
|
|
389
|
+
});
|
|
390
|
+
await seedBuiltinRoleWithPolicy({
|
|
391
|
+
deps,
|
|
392
|
+
workspaceId,
|
|
393
|
+
name: "viewer",
|
|
394
|
+
permissions: BUILTIN_VIEWER_PERMISSIONS,
|
|
395
|
+
nowIso,
|
|
396
|
+
});
|
|
397
|
+
// Hashed BEFORE the owner principal row is written, not inline in the `users.save` below.
|
|
398
|
+
// argon2id is deliberately slow, which made it by far the widest window in this un-awaited,
|
|
399
|
+
// non-transactional sequence for a process to exit between the principal insert and the user
|
|
400
|
+
// insert that gives it meaning — leaving an orphan `kind: "user"` principal behind on every
|
|
401
|
+
// interrupted boot. Hashing first shrinks that window to a single adjacent await.
|
|
402
|
+
const ownerPasswordHash = await deps.hasher.hash({ password: ownerPassword });
|
|
403
|
+
const ownerPrincipalId = deps.idGen.newId();
|
|
404
|
+
await deps.repos.principals.save({
|
|
405
|
+
id: ownerPrincipalId,
|
|
406
|
+
workspaceId,
|
|
407
|
+
kind: "user",
|
|
408
|
+
displayName: "Owner",
|
|
409
|
+
status: "active",
|
|
410
|
+
createdAt: nowIso,
|
|
411
|
+
});
|
|
412
|
+
await deps.repos.users.save({
|
|
413
|
+
principalId: ownerPrincipalId,
|
|
414
|
+
workspaceId,
|
|
415
|
+
username: ownerUsername,
|
|
416
|
+
email: input.ownerEmail,
|
|
417
|
+
passwordHash: ownerPasswordHash,
|
|
418
|
+
});
|
|
419
|
+
// `principal_roles` carries no unique index either — same converge-don't-duplicate rule as the
|
|
420
|
+
// `role_policies` link in `seedBuiltinRoleWithPolicy`.
|
|
421
|
+
const ownerRoleLinks = await deps.repos.principalRoles.listByPrincipalId({
|
|
422
|
+
workspaceId,
|
|
423
|
+
principalId: ownerPrincipalId,
|
|
424
|
+
});
|
|
425
|
+
if (!ownerRoleLinks.some((link) => link.roleId === ownerRoleId)) {
|
|
426
|
+
await deps.repos.principalRoles.save({
|
|
427
|
+
id: deps.idGen.newId(),
|
|
428
|
+
workspaceId,
|
|
429
|
+
principalId: ownerPrincipalId,
|
|
430
|
+
roleId: ownerRoleId,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
return { ownerPrincipalId, systemPrincipalId };
|
|
434
|
+
}
|
|
435
|
+
//# sourceMappingURL=seed.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SessionTokenPort } from "../core/runtime-ports.js";
|
|
2
|
+
/** Node adapter retaining the existing 32-byte hex bearer and SHA-256 storage digest. */
|
|
3
|
+
export declare class NodeSessionTokens implements SessionTokenPort {
|
|
4
|
+
constructor(_required: Record<string, never>);
|
|
5
|
+
newToken(_required: Record<string, never>): string;
|
|
6
|
+
hashToken({ rawToken }: {
|
|
7
|
+
rawToken: string;
|
|
8
|
+
}): string;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=session-tokens.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
/** Node adapter retaining the existing 32-byte hex bearer and SHA-256 storage digest. */
|
|
3
|
+
export class NodeSessionTokens {
|
|
4
|
+
constructor(_required) { }
|
|
5
|
+
newToken(_required) {
|
|
6
|
+
return randomBytes(32).toString("hex");
|
|
7
|
+
}
|
|
8
|
+
hashToken({ rawToken }) {
|
|
9
|
+
return createHash("sha256").update(rawToken).digest("hex");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=session-tokens.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Username normalization (behavior.spec §5.1).
|
|
3
|
+
*
|
|
4
|
+
* A `users` row is a duplicate within a workspace iff its `username` equals an
|
|
5
|
+
* existing user's after case-insensitive comparison and Unicode NFC
|
|
6
|
+
* normalization. Normalizing once at every write/lookup site (seed, login,
|
|
7
|
+
* future CREATE_USER) makes that comparison a direct equality check rather
|
|
8
|
+
* than a repeated ad hoc string transform.
|
|
9
|
+
*/
|
|
10
|
+
/** NFC-normalize, trim, and lowercase a username for storage/lookup. */
|
|
11
|
+
export declare function normalizeUsername({ raw }: {
|
|
12
|
+
raw: string;
|
|
13
|
+
}): string;
|
|
14
|
+
//# sourceMappingURL=username.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Username normalization (behavior.spec §5.1).
|
|
3
|
+
*
|
|
4
|
+
* A `users` row is a duplicate within a workspace iff its `username` equals an
|
|
5
|
+
* existing user's after case-insensitive comparison and Unicode NFC
|
|
6
|
+
* normalization. Normalizing once at every write/lookup site (seed, login,
|
|
7
|
+
* future CREATE_USER) makes that comparison a direct equality check rather
|
|
8
|
+
* than a repeated ad hoc string transform.
|
|
9
|
+
*/
|
|
10
|
+
/** NFC-normalize, trim, and lowercase a username for storage/lookup. */
|
|
11
|
+
export function normalizeUsername({ raw }) {
|
|
12
|
+
return raw.normalize("NFC").trim().toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=username.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jini-ai/user-management",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Runtime-separated user management contracts and Node authentication, authorization, and administration services.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/AINSEP/Jini.git",
|
|
9
|
+
"directory": "packages/user-management"
|
|
10
|
+
},
|
|
11
|
+
"jini": {
|
|
12
|
+
"domain": "capability",
|
|
13
|
+
"kind": "user-management",
|
|
14
|
+
"runtime": "universal",
|
|
15
|
+
"entries": {
|
|
16
|
+
".": "universal",
|
|
17
|
+
"./server": "node",
|
|
18
|
+
"./react": "browser",
|
|
19
|
+
"./react/testing": "browser",
|
|
20
|
+
"./admin": "universal",
|
|
21
|
+
"./admin/react": "browser",
|
|
22
|
+
"./admin/adapters/http": "universal",
|
|
23
|
+
"./admin/adapters/memory": "universal",
|
|
24
|
+
"./admin/conformance": "universal"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/core/index.d.ts",
|
|
32
|
+
"import": "./dist/core/index.js",
|
|
33
|
+
"default": "./dist/core/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./server": {
|
|
36
|
+
"types": "./dist/server/index.d.ts",
|
|
37
|
+
"import": "./dist/server/index.js",
|
|
38
|
+
"default": "./dist/server/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./react": {
|
|
41
|
+
"types": "./dist/react/index.d.ts",
|
|
42
|
+
"import": "./dist/react/index.js",
|
|
43
|
+
"default": "./dist/react/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./react/testing": {
|
|
46
|
+
"types": "./dist/react/testing.d.ts",
|
|
47
|
+
"import": "./dist/react/testing.js",
|
|
48
|
+
"default": "./dist/react/testing.js"
|
|
49
|
+
},
|
|
50
|
+
"./admin": {
|
|
51
|
+
"types": "./dist/admin/index.d.ts",
|
|
52
|
+
"import": "./dist/admin/index.js",
|
|
53
|
+
"default": "./dist/admin/index.js"
|
|
54
|
+
},
|
|
55
|
+
"./admin/react": {
|
|
56
|
+
"types": "./dist/admin/react/index.d.ts",
|
|
57
|
+
"import": "./dist/admin/react/index.js",
|
|
58
|
+
"default": "./dist/admin/react/index.js"
|
|
59
|
+
},
|
|
60
|
+
"./admin/adapters/http": {
|
|
61
|
+
"types": "./dist/admin/adapters/http.d.ts",
|
|
62
|
+
"import": "./dist/admin/adapters/http.js",
|
|
63
|
+
"default": "./dist/admin/adapters/http.js"
|
|
64
|
+
},
|
|
65
|
+
"./admin/adapters/memory": {
|
|
66
|
+
"types": "./dist/admin/adapters/memory.d.ts",
|
|
67
|
+
"import": "./dist/admin/adapters/memory.js",
|
|
68
|
+
"default": "./dist/admin/adapters/memory.js"
|
|
69
|
+
},
|
|
70
|
+
"./admin/conformance": {
|
|
71
|
+
"types": "./dist/admin/conformance/index.d.ts",
|
|
72
|
+
"import": "./dist/admin/conformance/index.js",
|
|
73
|
+
"default": "./dist/admin/conformance/index.js"
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"files": [
|
|
77
|
+
"dist",
|
|
78
|
+
"README.md",
|
|
79
|
+
"CHANGELOG.md",
|
|
80
|
+
"LICENSE",
|
|
81
|
+
"!dist/**/__tests__/**",
|
|
82
|
+
"!dist/**/*.test.*",
|
|
83
|
+
"!dist/**/*.map"
|
|
84
|
+
],
|
|
85
|
+
"publishConfig": {
|
|
86
|
+
"access": "public",
|
|
87
|
+
"registry": "https://registry.npmjs.org"
|
|
88
|
+
},
|
|
89
|
+
"peerDependencies": {
|
|
90
|
+
"argon2": "^0.44.0",
|
|
91
|
+
"react": "^18.3.0 || ^19.0.0",
|
|
92
|
+
"react-dom": "^18.3.0 || ^19.0.0",
|
|
93
|
+
"@jini-ai/ui": "^0.4.0",
|
|
94
|
+
"@jini-ai/admin": "^0.4.0",
|
|
95
|
+
"@jini-ai/agentic": "^0.4.0"
|
|
96
|
+
},
|
|
97
|
+
"peerDependenciesMeta": {
|
|
98
|
+
"argon2": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"react": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"react-dom": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"@jini-ai/ui": {
|
|
108
|
+
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"@jini-ai/admin": {
|
|
111
|
+
"optional": true
|
|
112
|
+
},
|
|
113
|
+
"@jini-ai/agentic": {
|
|
114
|
+
"optional": true
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"devDependencies": {
|
|
118
|
+
"vitest": "^2.1.0",
|
|
119
|
+
"typescript": "^5.6.0",
|
|
120
|
+
"@types/node": "^24.0.0",
|
|
121
|
+
"argon2": "^0.44.0",
|
|
122
|
+
"react": "^19.2.0",
|
|
123
|
+
"react-dom": "^19.2.0",
|
|
124
|
+
"@types/react": "^19.2.0",
|
|
125
|
+
"@types/react-dom": "^19.2.0",
|
|
126
|
+
"@testing-library/react": "^16.3.0",
|
|
127
|
+
"@testing-library/user-event": "^14.5.0",
|
|
128
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
129
|
+
"jsdom": "^25.0.1",
|
|
130
|
+
"@jini-ai/agentic": "0.4.0",
|
|
131
|
+
"@jini-ai/admin": "0.4.0",
|
|
132
|
+
"@jini-ai/cms": "0.4.0",
|
|
133
|
+
"@jini-ai/ui": "0.4.0"
|
|
134
|
+
},
|
|
135
|
+
"dependencies": {
|
|
136
|
+
"@jini-ai/core": "0.4.0",
|
|
137
|
+
"@jini-ai/ui-kit": "0.1.0"
|
|
138
|
+
},
|
|
139
|
+
"scripts": {
|
|
140
|
+
"build": "tsc -p tsconfig.json",
|
|
141
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
142
|
+
"test": "vitest run"
|
|
143
|
+
}
|
|
144
|
+
}
|