@eminuckan/spine 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/CODE_OF_CONDUCT.md +39 -0
- package/CONTRIBUTING.md +78 -0
- package/MAINTAINERS.md +43 -0
- package/README.md +111 -55
- package/ROADMAP.md +58 -0
- package/SECURITY.md +37 -0
- package/dist/access/evaluator.d.ts +3 -0
- package/dist/access/evaluator.d.ts.map +1 -0
- package/dist/access/index.d.ts +3 -0
- package/dist/access/index.d.ts.map +1 -0
- package/dist/access/index.js +4 -0
- package/dist/access/types.d.ts +38 -0
- package/dist/access/types.d.ts.map +1 -0
- package/dist/access-CGrsDMwu.js +61 -0
- package/dist/access-CGrsDMwu.js.map +1 -0
- package/dist/api-client/api-config.server.d.ts +27 -0
- package/dist/api-client/api-config.server.d.ts.map +1 -1
- package/dist/api-client/error-codes.d.ts +6 -31
- package/dist/api-client/error-codes.d.ts.map +1 -1
- package/dist/api-client/index.js +3 -3
- package/dist/api-client/server.d.ts +1 -1
- package/dist/api-client/server.d.ts.map +1 -1
- package/dist/api-client/server.js +4 -4
- package/dist/{api-client-DsrxK05Z.js → api-client-CgZUMukO.js} +9 -26
- package/dist/api-client-CgZUMukO.js.map +1 -0
- package/dist/auth/auth.server.d.ts +21 -16
- package/dist/auth/auth.server.d.ts.map +1 -1
- package/dist/auth/authorization-parameters.d.ts +5 -0
- package/dist/auth/authorization-parameters.d.ts.map +1 -0
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/redis-session-storage.server.d.ts +18 -1
- package/dist/auth/redis-session-storage.server.d.ts.map +1 -1
- package/dist/auth/route-protection.server.d.ts +20 -4
- package/dist/auth/route-protection.server.d.ts.map +1 -1
- package/dist/auth/server.d.ts +1 -1
- package/dist/auth/server.d.ts.map +1 -1
- package/dist/auth/server.js +5 -5
- package/dist/auth/types.d.ts +88 -21
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth.server-D5oiz9aF.js +1066 -0
- package/dist/auth.server-D5oiz9aF.js.map +1 -0
- package/dist/auth.server-lC94lmlx.js +5 -0
- package/dist/identity/identity-context.server.d.ts +2 -0
- package/dist/identity/identity-context.server.d.ts.map +1 -1
- package/dist/identity/identity-store.d.ts +19 -2
- package/dist/identity/identity-store.d.ts.map +1 -1
- package/dist/identity/index.js +2 -2
- package/dist/identity/server.js +3 -3
- package/dist/identity/types.d.ts +3 -1
- package/dist/identity/types.d.ts.map +1 -1
- package/dist/{identity-CFa1txoj.js → identity-C5un-xAs.js} +104 -60
- package/dist/identity-C5un-xAs.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -10
- package/dist/logging/index.js +1 -1
- package/dist/{logging-_h2mi3L9.js → logging-DviiAkLx.js} +2 -2
- package/dist/{logging-_h2mi3L9.js.map → logging-DviiAkLx.js.map} +1 -1
- package/dist/permission-service-CvGpwI0d.js +95 -0
- package/dist/permission-service-CvGpwI0d.js.map +1 -0
- package/dist/permissions/constants.d.ts +3 -0
- package/dist/permissions/constants.d.ts.map +1 -1
- package/dist/permissions/index.js +2 -1
- package/dist/permissions/permission-store.d.ts +1 -1
- package/dist/permissions/types.d.ts +1 -1
- package/dist/permissions/types.d.ts.map +1 -1
- package/dist/{permissions-D5ScGGnd.js → permissions-_n1hIG7Q.js} +8 -106
- package/dist/permissions-_n1hIG7Q.js.map +1 -0
- package/dist/query-client/index.js +1 -1
- package/dist/query-client/types.d.ts +9 -9
- package/dist/query-client/types.d.ts.map +1 -1
- package/dist/{query-client-Bpe08pk9.js → query-client-Bh3TyB8W.js} +2 -2
- package/dist/query-client-Bh3TyB8W.js.map +1 -0
- package/dist/react-router/index.js +12 -10
- package/dist/react-router/server.js +19 -17
- package/dist/redis-session-storage.server-v2VcpFsQ.js +500 -0
- package/dist/redis-session-storage.server-v2VcpFsQ.js.map +1 -0
- package/dist/{server-BHfKHKF_.js → server-B6PCxc-T.js} +39 -87
- package/dist/server-B6PCxc-T.js.map +1 -0
- package/dist/{server-DZmcd2Lr.js → server-C96NMFBy.js} +18 -10
- package/dist/server-C96NMFBy.js.map +1 -0
- package/dist/{server-BxvRxjCK.js → server-Ca-3DJf2.js} +44 -11
- package/dist/server-Ca-3DJf2.js.map +1 -0
- package/dist/{server-C3L8J7iE.js → server-Dxsww9v9.js} +6 -5
- package/dist/server-Dxsww9v9.js.map +1 -0
- package/dist/{server-CouYoZiw.js → server-hGrElJMG.js} +2 -2
- package/dist/{server-CouYoZiw.js.map → server-hGrElJMG.js.map} +1 -1
- package/dist/server.js +19 -17
- package/dist/signalr/index.js +1 -1
- package/dist/signalr/types.d.ts +1 -1
- package/dist/{signalr-SJ-1sIxw.js → signalr-k-70VHKo.js} +3 -2
- package/dist/signalr-k-70VHKo.js.map +1 -0
- package/dist/tenant/index.js +2 -2
- package/dist/tenant/server.js +4 -4
- package/dist/tenant/tenant-context.d.ts +17 -1
- package/dist/tenant/tenant-context.d.ts.map +1 -1
- package/dist/tenant/tenant-cookie.server.d.ts.map +1 -1
- package/dist/tenant/tenant-store.d.ts +54 -9
- package/dist/tenant/tenant-store.d.ts.map +1 -1
- package/dist/tenant/tenant.server.d.ts +20 -1
- package/dist/tenant/tenant.server.d.ts.map +1 -1
- package/dist/tenant/types.d.ts +29 -9
- package/dist/tenant/types.d.ts.map +1 -1
- package/dist/tenant-DfLemb6n.js +384 -0
- package/dist/tenant-DfLemb6n.js.map +1 -0
- package/dist/{types-BnKaWdfe.js → types-BaaE6wjY.js} +1 -1
- package/dist/{types-BnKaWdfe.js.map → types-BaaE6wjY.js.map} +1 -1
- package/docs/adapters.md +87 -0
- package/docs/architecture.md +193 -0
- package/docs/backend-adaptation.md +249 -0
- package/docs/codex-for-oss-application.md +31 -0
- package/docs/codex-maintenance.md +47 -0
- package/docs/installation.md +213 -0
- package/docs/module-reference.md +207 -0
- package/docs/releasing.md +92 -0
- package/package.json +27 -4
- package/dist/api-client-DsrxK05Z.js.map +0 -1
- package/dist/auth.server-BPiCtlJE.js +0 -5
- package/dist/auth.server-Cx2h2qu3.js +0 -724
- package/dist/auth.server-Cx2h2qu3.js.map +0 -1
- package/dist/identity-CFa1txoj.js.map +0 -1
- package/dist/permissions-D5ScGGnd.js.map +0 -1
- package/dist/query-client-Bpe08pk9.js.map +0 -1
- package/dist/redis-session-storage.server-BR_0y-QO.js +0 -250
- package/dist/redis-session-storage.server-BR_0y-QO.js.map +0 -1
- package/dist/server-BHfKHKF_.js.map +0 -1
- package/dist/server-BxvRxjCK.js.map +0 -1
- package/dist/server-C3L8J7iE.js.map +0 -1
- package/dist/server-DZmcd2Lr.js.map +0 -1
- package/dist/signalr-SJ-1sIxw.js.map +0 -1
- package/dist/tenant-C6KPaSCX.js +0 -256
- package/dist/tenant-C6KPaSCX.js.map +0 -1
- /package/dist/{src-CJ2RLi94.js → src-B9rDUgSb.js} +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
This guide gets a new app from zero to a working Spine integration shape.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js 20 or newer
|
|
8
|
+
- pnpm 9 or newer
|
|
9
|
+
- React 18 or newer when using client-side provider/hooks
|
|
10
|
+
- A Redis-compatible store for server sessions and OAuth state
|
|
11
|
+
- An OpenID Connect provider for the built-in auth flow
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @eminuckan/spine
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
For React apps, install the peer dependencies used by the provider and query modules:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add react @tanstack/react-query
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Environment
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
OIDC_AUTHORITY=https://identity.example.com/realms/demo
|
|
29
|
+
OIDC_CLIENT_ID=your-client
|
|
30
|
+
OIDC_REDIRECT_URI=http://localhost:5173/auth/callback
|
|
31
|
+
OIDC_POST_LOGOUT_REDIRECT_URI=http://localhost:5173/
|
|
32
|
+
OIDC_CLIENT_AUTH_METHOD=none
|
|
33
|
+
OIDC_SCOPE="openid profile email api"
|
|
34
|
+
REDIS_URL=redis://localhost:6379
|
|
35
|
+
API_BASE_URL=http://localhost:8080
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Use `OIDC_CLIENT_SECRET` with `OIDC_CLIENT_AUTH_METHOD=client_secret_post` or
|
|
39
|
+
`client_secret_basic` for confidential server-side clients.
|
|
40
|
+
|
|
41
|
+
## React Router Routes
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
// app/routes/auth.login.tsx
|
|
45
|
+
import { login } from '@eminuckan/spine/react-router/server';
|
|
46
|
+
|
|
47
|
+
export async function loader({ request }: { request: Request }) {
|
|
48
|
+
throw await login(request, { returnUrl: '/dashboard' });
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// app/routes/auth.callback.tsx
|
|
54
|
+
import { handleCallback } from '@eminuckan/spine/react-router/server';
|
|
55
|
+
|
|
56
|
+
export async function loader({ request }: { request: Request }) {
|
|
57
|
+
return handleCallback(request);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
// app/routes/auth.logout.tsx
|
|
63
|
+
import { logout } from '@eminuckan/spine/react-router/server';
|
|
64
|
+
|
|
65
|
+
export async function loader({ request }: { request: Request }) {
|
|
66
|
+
return logout(request);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
// app/routes/dashboard.tsx
|
|
72
|
+
import { authRoute } from '@eminuckan/spine/react-router/server';
|
|
73
|
+
|
|
74
|
+
export async function loader({ request }: { request: Request }) {
|
|
75
|
+
return authRoute(request, async (user) => ({ user }));
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Tenant Data
|
|
80
|
+
|
|
81
|
+
Configure cookie conventions in the app layer:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { configureTenantCookie } from '@eminuckan/spine/tenant/server';
|
|
85
|
+
|
|
86
|
+
configureTenantCookie({
|
|
87
|
+
name: '__spine_tenant',
|
|
88
|
+
sameSite: 'Lax',
|
|
89
|
+
httpOnly: false,
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Simple apps can use the default tenant client by configuring endpoint names:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { configureTenantClient } from '@eminuckan/spine/tenant';
|
|
97
|
+
|
|
98
|
+
configureTenantClient({
|
|
99
|
+
tenantDataEndpoint: '/api/tenant/data',
|
|
100
|
+
tenantSwitchEndpoint: '/api/tenant/switch',
|
|
101
|
+
tenantCookieName: '__spine_tenant',
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Apps with different multi-tenancy contracts should provide their own client functions:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { configureTenantClient } from '@eminuckan/spine/tenant';
|
|
109
|
+
|
|
110
|
+
configureTenantClient({
|
|
111
|
+
fetchTenantData: async ({ tenantId }) => {
|
|
112
|
+
const response = await fetch(`/api/workspaces/${tenantId}`);
|
|
113
|
+
const payload = await response.json();
|
|
114
|
+
return {
|
|
115
|
+
id: payload.workspace.id,
|
|
116
|
+
name: payload.workspace.displayName,
|
|
117
|
+
plan: payload.workspace.plan,
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
switchTenant: async ({ tenantId }) => {
|
|
121
|
+
const response = await fetch('/api/session/workspace', {
|
|
122
|
+
method: 'PUT',
|
|
123
|
+
headers: { 'Content-Type': 'application/json' },
|
|
124
|
+
body: JSON.stringify({ workspaceId: tenantId }),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return { success: response.ok, reload: false };
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Use the generic client hooks for new code:
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { useCurrentTenant, useCurrentTenantData, useTenantData } from '@eminuckan/spine/tenant';
|
|
136
|
+
|
|
137
|
+
export function TenantBadge() {
|
|
138
|
+
const tenantId = useCurrentTenant();
|
|
139
|
+
const tenantData = useCurrentTenantData();
|
|
140
|
+
const { refreshTenantData } = useTenantData();
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<button type="button" onClick={refreshTenantData}>
|
|
144
|
+
{tenantData?.name ?? tenantId ?? 'No tenant'}
|
|
145
|
+
</button>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The older organization-named hooks remain as deprecated compatibility aliases.
|
|
151
|
+
|
|
152
|
+
## Identity Client Contracts
|
|
153
|
+
|
|
154
|
+
Simple apps can configure browser-side identity endpoints:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { configureIdentityStore } from '@eminuckan/spine/identity';
|
|
158
|
+
|
|
159
|
+
configureIdentityStore({
|
|
160
|
+
contextEndpoint: '/api/identity/context',
|
|
161
|
+
permissionsEndpoint: '/api/identity/permissions',
|
|
162
|
+
logoutPath: '/auth/logout',
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Apps with different response shapes can provide functions instead:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
configureIdentityStore({
|
|
170
|
+
fetchContext: async () => {
|
|
171
|
+
const response = await fetch('/session/me');
|
|
172
|
+
const payload = await response.json();
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
userId: payload.user.id,
|
|
176
|
+
email: payload.user.email,
|
|
177
|
+
memberships: payload.accounts.map((account) => ({
|
|
178
|
+
tenantId: account.id,
|
|
179
|
+
tenantName: account.name,
|
|
180
|
+
})),
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
fetchPermissions: async () => {
|
|
184
|
+
const response = await fetch('/session/capabilities');
|
|
185
|
+
const payload = await response.json();
|
|
186
|
+
return payload.capabilities;
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Try the Example
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
cd examples/react-router-saas
|
|
195
|
+
pnpm install
|
|
196
|
+
cp .env.example .env
|
|
197
|
+
pnpm dev
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The example uses React Router framework mode and includes auth routes, a protected
|
|
201
|
+
dashboard loader, provider wiring, and local app adapters for identity and tenant
|
|
202
|
+
fetching.
|
|
203
|
+
|
|
204
|
+
## Validation
|
|
205
|
+
|
|
206
|
+
For library development, run:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
pnpm check
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
`pnpm check` runs the public-surface guard, TypeScript, ESLint, Vitest, and the
|
|
213
|
+
package build.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Module Reference
|
|
2
|
+
|
|
3
|
+
This is a high-level reference for Spine's public surfaces.
|
|
4
|
+
|
|
5
|
+
## Auth
|
|
6
|
+
|
|
7
|
+
Server entry point:
|
|
8
|
+
|
|
9
|
+
- `@eminuckan/spine/auth/server`
|
|
10
|
+
- `@eminuckan/spine/server`
|
|
11
|
+
- `@eminuckan/spine/react-router/server`
|
|
12
|
+
|
|
13
|
+
Key exports:
|
|
14
|
+
|
|
15
|
+
- `login`
|
|
16
|
+
- `handleCallback`
|
|
17
|
+
- `logout`
|
|
18
|
+
- `getUser`
|
|
19
|
+
- `requireAuth`
|
|
20
|
+
- `getAccessToken`
|
|
21
|
+
- `refreshTokens`
|
|
22
|
+
- `getAuthSession`
|
|
23
|
+
- `updateAuthSession`
|
|
24
|
+
- `destroyAuthSession`
|
|
25
|
+
- `configureAuthClaimMapping`
|
|
26
|
+
- `resetAuthClaimMapping`
|
|
27
|
+
- `configureRouteProtection`
|
|
28
|
+
- `authRoute`
|
|
29
|
+
- `protectedRoute`
|
|
30
|
+
- `policyRoute`
|
|
31
|
+
- `publicRoute`
|
|
32
|
+
|
|
33
|
+
Deprecated compatibility aliases:
|
|
34
|
+
|
|
35
|
+
- `onboardingRoute`
|
|
36
|
+
- `subscriptionRoute`
|
|
37
|
+
|
|
38
|
+
## Tenant
|
|
39
|
+
|
|
40
|
+
Client entry point:
|
|
41
|
+
|
|
42
|
+
- `@eminuckan/spine/tenant`
|
|
43
|
+
|
|
44
|
+
Server entry point:
|
|
45
|
+
|
|
46
|
+
- `@eminuckan/spine/tenant/server`
|
|
47
|
+
|
|
48
|
+
Key client exports:
|
|
49
|
+
|
|
50
|
+
- `TenantProvider`
|
|
51
|
+
- `useTenant`
|
|
52
|
+
- `useCurrentTenant`
|
|
53
|
+
- `useCurrentTenantData`
|
|
54
|
+
- `useTenantData`
|
|
55
|
+
- `useTenantStore`
|
|
56
|
+
- `initializeTenantStore`
|
|
57
|
+
- `configureTenantClient`
|
|
58
|
+
- `configureTenantEndpoints`
|
|
59
|
+
- `resetTenantClientConfig`
|
|
60
|
+
- `normalizeTenantDataPayload`
|
|
61
|
+
- `normalizeTenantSwitchPayload`
|
|
62
|
+
- `TenantData`
|
|
63
|
+
- `TenantBranding`
|
|
64
|
+
|
|
65
|
+
Deprecated compatibility aliases:
|
|
66
|
+
|
|
67
|
+
- `useCurrentOrganization`
|
|
68
|
+
- `useOrganization`
|
|
69
|
+
- `OrganizationData`
|
|
70
|
+
- `OrganizationBranding`
|
|
71
|
+
|
|
72
|
+
Key server exports:
|
|
73
|
+
|
|
74
|
+
- `configureTenantCookie`
|
|
75
|
+
- `resetTenantCookieConfig`
|
|
76
|
+
- `getActiveTenant`
|
|
77
|
+
- `setActiveTenant`
|
|
78
|
+
- `clearActiveTenant`
|
|
79
|
+
- `configureTenantResolution`
|
|
80
|
+
- `resetTenantResolutionConfig`
|
|
81
|
+
- `configureIdentityContextFetcher`
|
|
82
|
+
- `getCurrentTenant`
|
|
83
|
+
- `getAvailableTenants`
|
|
84
|
+
- `initializeTenant`
|
|
85
|
+
- `setCurrentTenant`
|
|
86
|
+
- `clearCurrentTenant`
|
|
87
|
+
|
|
88
|
+
## Identity
|
|
89
|
+
|
|
90
|
+
Client entry point:
|
|
91
|
+
|
|
92
|
+
- `@eminuckan/spine/identity`
|
|
93
|
+
|
|
94
|
+
Server entry point:
|
|
95
|
+
|
|
96
|
+
- `@eminuckan/spine/identity/server`
|
|
97
|
+
|
|
98
|
+
Key client exports:
|
|
99
|
+
|
|
100
|
+
- `IdentityContextProvider`
|
|
101
|
+
- `useIdentityStore`
|
|
102
|
+
- `useIdentityContext`
|
|
103
|
+
- `useIsOnboarded`
|
|
104
|
+
- `useHasSubscription`
|
|
105
|
+
- `useUserTenants`
|
|
106
|
+
- `useUserPermissions`
|
|
107
|
+
- `configureIdentityStore`
|
|
108
|
+
- `configureIdentityEndpoints`
|
|
109
|
+
- `resetIdentityStoreConfig`
|
|
110
|
+
- `normalizeIdentityContextPayload`
|
|
111
|
+
- `normalizeIdentityPermissionsPayload`
|
|
112
|
+
|
|
113
|
+
Key server exports:
|
|
114
|
+
|
|
115
|
+
- `configureIdentityAPIFetcher`
|
|
116
|
+
- `configurePermissionFetcher`
|
|
117
|
+
- `fetchIdentityContext`
|
|
118
|
+
- `getIdentityContext`
|
|
119
|
+
- `fetchUserPermissions`
|
|
120
|
+
- `contextToUserInfo`
|
|
121
|
+
- `clearIdentityContextCache`
|
|
122
|
+
- `hasContextVersionChanged`
|
|
123
|
+
- `updateContextVersion`
|
|
124
|
+
|
|
125
|
+
## Permissions
|
|
126
|
+
|
|
127
|
+
Client entry point:
|
|
128
|
+
|
|
129
|
+
- `@eminuckan/spine/permissions`
|
|
130
|
+
|
|
131
|
+
Server entry point:
|
|
132
|
+
|
|
133
|
+
- `@eminuckan/spine/server`
|
|
134
|
+
|
|
135
|
+
Key client exports:
|
|
136
|
+
|
|
137
|
+
- `usePermissionStore`
|
|
138
|
+
- `initializePermissions`
|
|
139
|
+
- `getPermissionChecker`
|
|
140
|
+
- `getPermissions`
|
|
141
|
+
- `usePermission`
|
|
142
|
+
- `useHasPermission`
|
|
143
|
+
- `usePermissions`
|
|
144
|
+
- `useHasPermissions`
|
|
145
|
+
- `useModulePermission`
|
|
146
|
+
- `useHasModulePermission`
|
|
147
|
+
- `usePermissionChecker`
|
|
148
|
+
- `useAllPermissions`
|
|
149
|
+
- `ProtectedButton`
|
|
150
|
+
- `ProtectedLink`
|
|
151
|
+
- `ProtectedSection`
|
|
152
|
+
- `ProtectedAction`
|
|
153
|
+
|
|
154
|
+
Key server exports:
|
|
155
|
+
|
|
156
|
+
- `configurePermissionRouteProtection`
|
|
157
|
+
- `resetPermissionRouteProtection`
|
|
158
|
+
- `requirePermission`
|
|
159
|
+
- `checkPermission`
|
|
160
|
+
- `withPermission`
|
|
161
|
+
|
|
162
|
+
## API Client
|
|
163
|
+
|
|
164
|
+
Client entry point:
|
|
165
|
+
|
|
166
|
+
- `@eminuckan/spine/api-client`
|
|
167
|
+
|
|
168
|
+
Server entry point:
|
|
169
|
+
|
|
170
|
+
- `@eminuckan/spine/api-client/server`
|
|
171
|
+
|
|
172
|
+
Key server exports:
|
|
173
|
+
|
|
174
|
+
- `createAPIConfigFactory`
|
|
175
|
+
- `createFetchMiddleware`
|
|
176
|
+
- `createApiClient`
|
|
177
|
+
- `APIConfigFactoryOptions`
|
|
178
|
+
- `APIHeaderStrategyContext`
|
|
179
|
+
|
|
180
|
+
## Query Client
|
|
181
|
+
|
|
182
|
+
Entry point:
|
|
183
|
+
|
|
184
|
+
- `@eminuckan/spine/query-client`
|
|
185
|
+
|
|
186
|
+
Key exports:
|
|
187
|
+
|
|
188
|
+
- `createQueryClient`
|
|
189
|
+
- `cachePresets`
|
|
190
|
+
- `createQueryKeyFactory`
|
|
191
|
+
- `invalidationHelpers`
|
|
192
|
+
|
|
193
|
+
## Logging
|
|
194
|
+
|
|
195
|
+
Entry point:
|
|
196
|
+
|
|
197
|
+
- `@eminuckan/spine/logging`
|
|
198
|
+
|
|
199
|
+
The logging module provides typed logger configuration and a default service-level logger.
|
|
200
|
+
|
|
201
|
+
## SignalR
|
|
202
|
+
|
|
203
|
+
Entry point:
|
|
204
|
+
|
|
205
|
+
- `@eminuckan/spine/signalr`
|
|
206
|
+
|
|
207
|
+
Use this module for generic realtime client helpers and types.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Releasing Spine
|
|
2
|
+
|
|
3
|
+
This guide covers local validation, GitHub release setup, and npm publishing.
|
|
4
|
+
|
|
5
|
+
## Before Releasing
|
|
6
|
+
|
|
7
|
+
Run:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm install
|
|
11
|
+
pnpm check
|
|
12
|
+
npm pack --dry-run
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Review:
|
|
16
|
+
|
|
17
|
+
- `README.md`
|
|
18
|
+
- `package.json`
|
|
19
|
+
- `CHANGELOG.md`
|
|
20
|
+
- `README.md`
|
|
21
|
+
- `ROADMAP.md`
|
|
22
|
+
|
|
23
|
+
## Versioning
|
|
24
|
+
|
|
25
|
+
Update the version in `package.json` and add a changelog entry.
|
|
26
|
+
|
|
27
|
+
## GitHub Repository
|
|
28
|
+
|
|
29
|
+
If the repository does not exist yet:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
gh repo create eminuckan/spine --public --source=. --remote=origin --push
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Useful follow-up commands:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
gh repo edit eminuckan/spine --description "Framework-agnostic SaaS primitives for auth, identity, multi-tenancy, permissions, and API access."
|
|
39
|
+
gh repo edit eminuckan/spine --homepage "https://github.com/eminuckan/spine#readme"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## npm Publishing
|
|
43
|
+
|
|
44
|
+
You need npm authentication on the machine for manual publishing, or npm trusted publishing configured for GitHub Actions.
|
|
45
|
+
|
|
46
|
+
Manual publish:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm publish --access public
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Dry run:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm publish --dry-run
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## GitHub Actions Release Workflow
|
|
59
|
+
|
|
60
|
+
The repository includes a release workflow that expects npm trusted publishing to be configured for the `npm-publish` GitHub environment.
|
|
61
|
+
|
|
62
|
+
Recommended tag format:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
git tag v0.2.0
|
|
66
|
+
git push origin v0.2.0
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Release Checklist
|
|
70
|
+
|
|
71
|
+
- Version updated
|
|
72
|
+
- Changelog updated
|
|
73
|
+
- Docs updated
|
|
74
|
+
- `pnpm check` passes
|
|
75
|
+
- `npm pack --dry-run` includes only intended files
|
|
76
|
+
- GitHub repo is public
|
|
77
|
+
- npm auth is available
|
|
78
|
+
- Publish succeeds
|
|
79
|
+
|
|
80
|
+
## Troubleshooting
|
|
81
|
+
|
|
82
|
+
### `npm whoami` fails
|
|
83
|
+
|
|
84
|
+
You are not authenticated with npm on the current machine.
|
|
85
|
+
|
|
86
|
+
### `npm view @eminuckan/spine version` returns 404
|
|
87
|
+
|
|
88
|
+
The package is not currently published or the scope/package name is unavailable to the current user.
|
|
89
|
+
|
|
90
|
+
### GitHub push fails because the repo does not exist
|
|
91
|
+
|
|
92
|
+
Create the repository first with `gh repo create`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eminuckan/spine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Framework-agnostic SaaS primitives for auth, identity, multi-tenancy, permissions, and API access.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
"types": "./dist/auth/server.d.ts",
|
|
34
34
|
"import": "./dist/auth/server.js"
|
|
35
35
|
},
|
|
36
|
+
"./access": {
|
|
37
|
+
"types": "./dist/access/index.d.ts",
|
|
38
|
+
"import": "./dist/access/index.js"
|
|
39
|
+
},
|
|
36
40
|
"./permissions": {
|
|
37
41
|
"types": "./dist/permissions/index.d.ts",
|
|
38
42
|
"import": "./dist/permissions/index.js"
|
|
@@ -76,8 +80,14 @@
|
|
|
76
80
|
},
|
|
77
81
|
"files": [
|
|
78
82
|
"dist",
|
|
83
|
+
"docs",
|
|
79
84
|
"CHANGELOG.md",
|
|
85
|
+
"CODE_OF_CONDUCT.md",
|
|
86
|
+
"CONTRIBUTING.md",
|
|
80
87
|
"README.md",
|
|
88
|
+
"MAINTAINERS.md",
|
|
89
|
+
"ROADMAP.md",
|
|
90
|
+
"SECURITY.md",
|
|
81
91
|
"LICENSE"
|
|
82
92
|
],
|
|
83
93
|
"scripts": {
|
|
@@ -85,16 +95,28 @@
|
|
|
85
95
|
"dev": "tsdown --watch",
|
|
86
96
|
"typecheck": "tsc --noEmit",
|
|
87
97
|
"lint": "eslint src --max-warnings 0",
|
|
88
|
-
"
|
|
98
|
+
"test": "vitest run",
|
|
99
|
+
"check:public-surface": "node scripts/check-public-surface.mjs",
|
|
100
|
+
"check": "pnpm run check:public-surface && pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run build",
|
|
101
|
+
"prepublishOnly": "pnpm run check"
|
|
89
102
|
},
|
|
90
103
|
"peerDependencies": {
|
|
91
104
|
"@tanstack/react-query": ">=5.0.0",
|
|
92
105
|
"react": ">=18.0.0"
|
|
93
106
|
},
|
|
107
|
+
"peerDependenciesMeta": {
|
|
108
|
+
"@tanstack/react-query": {
|
|
109
|
+
"optional": true
|
|
110
|
+
},
|
|
111
|
+
"react": {
|
|
112
|
+
"optional": true
|
|
113
|
+
}
|
|
114
|
+
},
|
|
94
115
|
"dependencies": {
|
|
95
116
|
"@microsoft/signalr": "^9.0.0",
|
|
96
117
|
"ioredis": "^5.4.1",
|
|
97
|
-
"
|
|
118
|
+
"jose": "^6.2.2",
|
|
119
|
+
"openid-client": "^6.8.3",
|
|
98
120
|
"zustand": "^5.0.2"
|
|
99
121
|
},
|
|
100
122
|
"devDependencies": {
|
|
@@ -105,7 +127,8 @@
|
|
|
105
127
|
"globals": "^17.4.0",
|
|
106
128
|
"tsdown": "^0.9.0",
|
|
107
129
|
"typescript": "^5.7.0",
|
|
108
|
-
"typescript-eslint": "^8.57.0"
|
|
130
|
+
"typescript-eslint": "^8.57.0",
|
|
131
|
+
"vitest": "^4.1.8"
|
|
109
132
|
},
|
|
110
133
|
"engines": {
|
|
111
134
|
"node": ">=20.0.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api-client-DsrxK05Z.js","names":["problemDetails: ProblemDetails","response: Response","config: ErrorHandlerConfig","error: ApiError","message: string","type: 'error' | 'success' | 'info'","DEFAULT_RETRY_CONFIG: Required<RetryConfig>","error: unknown","attempt: number","baseDelay: number","maxDelay: number","fn: () => Promise<T>","config?: RetryConfig","lastError: unknown","input: RequestInfo | URL","init?: RequestInit & { timeout?: number }","init?: RequestInit"],"sources":["../src/api-client/error-codes.ts","../src/api-client/error-handler.ts","../src/api-client/retry-handler.ts"],"sourcesContent":["/**\n * Error Codes\n * \n * Standard error codes from backend.\n */\n\nexport const ErrorCodes = {\n // Validation\n Validation: {\n Failed: 'ValidationFailed',\n },\n\n // Identity\n Identity: {\n InvalidCredentials: 'InvalidCredentials',\n UserInactive: 'UserInactive',\n TenantContextMissing: 'TenantContextMissing',\n InvalidCurrentPassword: 'InvalidCurrentPassword',\n OwnerCannotDisableMembership: 'OwnerCannotDisableMembership',\n CurrentUserMissing: 'CurrentUserMissing',\n UserNotFound: 'UserNotFound',\n EmailAlreadyExists: 'EmailAlreadyExists',\n },\n\n // Accounting\n Accounting: {\n OpeningBalanceLockedReconciliation: 'OpeningBalanceLockedReconciliation',\n OpeningBalanceLockedJournal: 'OpeningBalanceLockedJournal',\n SystemLocked: 'SystemLocked',\n AccountsDuplicate: 'AccountsDuplicate',\n AccountNotFound: 'AccountNotFound',\n },\n\n // Subscription\n Subscription: {\n SubscriptionRequired: 'SubscriptionRequired',\n SubscriptionExpired: 'SubscriptionExpired',\n FeatureNotAvailable: 'FeatureNotAvailable',\n },\n\n // Property Management\n PropertyManagement: {\n PropertyNotFound: 'PropertyNotFound',\n UnitNotFound: 'UnitNotFound',\n DuplicateProperty: 'DuplicateProperty',\n DuplicateUnit: 'DuplicateUnit',\n },\n\n // Leasing\n Leasing: {\n LeaseNotFound: 'LeaseNotFound',\n LeaseConflict: 'LeaseConflict',\n TenantNotFound: 'TenantNotFound',\n LateFeeValidationError: 'LateFeeValidationError',\n },\n\n // General\n NotFound: 'NotFound',\n Unauthorized: 'Unauthorized',\n Forbidden: 'Forbidden',\n InternalError: 'InternalError',\n} as const;\n\n/**\n * Check if error code is from PropertyManagement module\n */\nexport function isPropertyManagementError(code: string): boolean {\n return Object.values(ErrorCodes.PropertyManagement).includes(code as any);\n}\n\n/**\n * Check if error code is from Leasing module\n */\nexport function isLeasingError(code: string): boolean {\n return Object.values(ErrorCodes.Leasing).includes(code as any);\n}\n","/**\n * Error Handler\n * \n * Centralized error handling with ProblemDetails parsing.\n */\n\nimport type { ProblemDetails, ValidationError } from './types';\nimport { ErrorCodes } from './error-codes';\nimport { logger } from '../logging';\n\n/**\n * API Error Class\n */\nexport class ApiError extends Error {\n constructor(\n public problemDetails: ProblemDetails,\n public response: Response\n ) {\n super(problemDetails.detail);\n this.name = 'ApiError';\n }\n\n get code(): string {\n return this.problemDetails.title;\n }\n\n get status(): number {\n return this.problemDetails.status;\n }\n\n get isValidationError(): boolean {\n return this.code === ErrorCodes.Validation.Failed;\n }\n\n get validationErrors(): ValidationError[] {\n return this.problemDetails.meta?.errors ?? [];\n }\n\n get traceId(): string {\n return this.problemDetails.traceId;\n }\n}\n\n/**\n * Parse API error response\n */\nexport async function handleApiError(response: Response): Promise<never> {\n let problemDetails: ProblemDetails;\n\n try {\n problemDetails = await response.json();\n } catch {\n problemDetails = {\n type: `https://httpstatuses.com/${response.status}`,\n title: 'UnknownError',\n status: response.status,\n detail: response.statusText || 'An unknown error occurred',\n traceId: response.headers.get('x-trace-id') ?? 'unknown',\n };\n }\n\n logger.error('API request failed', undefined, {\n code: problemDetails.title,\n status: problemDetails.status,\n detail: problemDetails.detail,\n traceId: problemDetails.traceId,\n });\n\n throw new ApiError(problemDetails, response);\n}\n\n/**\n * Error Handler Configuration\n */\nexport interface ErrorHandlerConfig {\n handlers?: Partial<Record<string, (error: ApiError) => void>>;\n showToast?: boolean;\n toastMessages?: Partial<Record<string, string>>;\n onError?: (error: ApiError) => void;\n toast?: (message: string, type: 'error' | 'success' | 'info') => void;\n}\n\n/**\n * Error Handler Class\n */\nexport class ErrorHandler {\n private config: ErrorHandlerConfig;\n\n constructor(config: ErrorHandlerConfig = {}) {\n this.config = {\n showToast: true,\n ...config,\n };\n }\n\n handle(error: ApiError): void {\n const code = error.code;\n\n logger.error('Handling API Error', undefined, {\n code,\n status: error.status,\n traceId: error.traceId,\n });\n\n // Try custom handler first\n if (this.config.handlers?.[code]) {\n this.config.handlers[code]!(error);\n return;\n }\n\n // Fallback\n if (this.config.onError) {\n this.config.onError(error);\n } else {\n this.showDefaultToast(error);\n }\n }\n\n private showToast(message: string, type: 'error' | 'success' | 'info'): void {\n if (this.config.showToast && this.config.toast) {\n this.config.toast(message, type);\n }\n }\n\n private showDefaultToast(error: ApiError): void {\n const message =\n this.config.toastMessages?.[error.code] ||\n error.problemDetails.detail ||\n 'An error occurred';\n this.showToast(message, 'error');\n }\n}\n\n/**\n * Create global error handler\n */\nexport function createErrorHandler(config?: ErrorHandlerConfig): ErrorHandler {\n return new ErrorHandler(config);\n}\n\n/**\n * Map validation errors to form fields\n */\nexport function mapValidationErrors(errors: ValidationError[]): Record<string, string> {\n const mapped: Record<string, string> = {};\n for (const error of errors) {\n const fieldName = error.field.charAt(0).toLowerCase() + error.field.slice(1);\n mapped[fieldName] = error.message;\n }\n return mapped;\n}\n","/**\n * Retry Handler\n * \n * Retry logic for API requests.\n */\n\nimport type { RetryConfig } from './types';\n\nconst DEFAULT_RETRY_CONFIG: Required<RetryConfig> = {\n maxRetries: 3,\n baseDelay: 1000,\n maxDelay: 10000,\n retryCondition: (error: unknown) => {\n if (error instanceof Response) {\n return error.status >= 500 || error.status === 429;\n }\n return false;\n },\n};\n\n/**\n * Calculate backoff delay\n */\nexport function calculateBackoffDelay(\n attempt: number,\n baseDelay: number,\n maxDelay: number\n): number {\n const delay = baseDelay * Math.pow(2, attempt);\n const jitter = Math.random() * 0.3 * delay;\n return Math.min(delay + jitter, maxDelay);\n}\n\n/**\n * Check if error is retryable\n */\nexport function isRetryableError(error: unknown): boolean {\n if (error instanceof Response) {\n return error.status >= 500 || error.status === 429;\n }\n if (error instanceof TypeError) {\n // Network errors\n return true;\n }\n return false;\n}\n\n/**\n * Execute function with retry logic\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config?: RetryConfig\n): Promise<T> {\n const { maxRetries, baseDelay, maxDelay, retryCondition } = {\n ...DEFAULT_RETRY_CONFIG,\n ...config,\n };\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n if (attempt === maxRetries || !retryCondition(error)) {\n throw error;\n }\n\n const delay = calculateBackoffDelay(attempt, baseDelay, maxDelay);\n console.log(`Retry attempt ${attempt + 1}/${maxRetries} after ${delay}ms`);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError;\n}\n\n/**\n * Fetch with timeout\n */\nexport async function fetchWithTimeout(\n input: RequestInfo | URL,\n init?: RequestInit & { timeout?: number }\n): Promise<Response> {\n const { timeout = 30000, ...fetchInit } = init || {};\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(input, {\n ...fetchInit,\n signal: controller.signal,\n });\n return response;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Create fetch function with retry\n */\nexport function createRetryFetch(config?: RetryConfig) {\n return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n return withRetry(() => fetch(input, init), config);\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import "./logging-_h2mi3L9.js";
|
|
2
|
-
import { isSessionValid } from "./redis-session-storage.server-BR_0y-QO.js";
|
|
3
|
-
import { clearAuthErrorHeaders, clearAuthServerCache, configureAuthClaimMapping, extractUserInfo, getAccessToken, getAuthError, getUser, handleCallback, hasAuthError, login, logout, refreshTokens, requireAuth, resetAuthClaimMapping } from "./auth.server-Cx2h2qu3.js";
|
|
4
|
-
|
|
5
|
-
export { logout };
|