@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.
Files changed (135) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/CODE_OF_CONDUCT.md +39 -0
  3. package/CONTRIBUTING.md +78 -0
  4. package/MAINTAINERS.md +43 -0
  5. package/README.md +111 -55
  6. package/ROADMAP.md +58 -0
  7. package/SECURITY.md +37 -0
  8. package/dist/access/evaluator.d.ts +3 -0
  9. package/dist/access/evaluator.d.ts.map +1 -0
  10. package/dist/access/index.d.ts +3 -0
  11. package/dist/access/index.d.ts.map +1 -0
  12. package/dist/access/index.js +4 -0
  13. package/dist/access/types.d.ts +38 -0
  14. package/dist/access/types.d.ts.map +1 -0
  15. package/dist/access-CGrsDMwu.js +61 -0
  16. package/dist/access-CGrsDMwu.js.map +1 -0
  17. package/dist/api-client/api-config.server.d.ts +27 -0
  18. package/dist/api-client/api-config.server.d.ts.map +1 -1
  19. package/dist/api-client/error-codes.d.ts +6 -31
  20. package/dist/api-client/error-codes.d.ts.map +1 -1
  21. package/dist/api-client/index.js +3 -3
  22. package/dist/api-client/server.d.ts +1 -1
  23. package/dist/api-client/server.d.ts.map +1 -1
  24. package/dist/api-client/server.js +4 -4
  25. package/dist/{api-client-DsrxK05Z.js → api-client-CgZUMukO.js} +9 -26
  26. package/dist/api-client-CgZUMukO.js.map +1 -0
  27. package/dist/auth/auth.server.d.ts +21 -16
  28. package/dist/auth/auth.server.d.ts.map +1 -1
  29. package/dist/auth/authorization-parameters.d.ts +5 -0
  30. package/dist/auth/authorization-parameters.d.ts.map +1 -0
  31. package/dist/auth/index.d.ts +1 -1
  32. package/dist/auth/index.d.ts.map +1 -1
  33. package/dist/auth/redis-session-storage.server.d.ts +18 -1
  34. package/dist/auth/redis-session-storage.server.d.ts.map +1 -1
  35. package/dist/auth/route-protection.server.d.ts +20 -4
  36. package/dist/auth/route-protection.server.d.ts.map +1 -1
  37. package/dist/auth/server.d.ts +1 -1
  38. package/dist/auth/server.d.ts.map +1 -1
  39. package/dist/auth/server.js +5 -5
  40. package/dist/auth/types.d.ts +88 -21
  41. package/dist/auth/types.d.ts.map +1 -1
  42. package/dist/auth.server-D5oiz9aF.js +1066 -0
  43. package/dist/auth.server-D5oiz9aF.js.map +1 -0
  44. package/dist/auth.server-lC94lmlx.js +5 -0
  45. package/dist/identity/identity-context.server.d.ts +2 -0
  46. package/dist/identity/identity-context.server.d.ts.map +1 -1
  47. package/dist/identity/identity-store.d.ts +19 -2
  48. package/dist/identity/identity-store.d.ts.map +1 -1
  49. package/dist/identity/index.js +2 -2
  50. package/dist/identity/server.js +3 -3
  51. package/dist/identity/types.d.ts +3 -1
  52. package/dist/identity/types.d.ts.map +1 -1
  53. package/dist/{identity-CFa1txoj.js → identity-C5un-xAs.js} +104 -60
  54. package/dist/identity-C5un-xAs.js.map +1 -0
  55. package/dist/index.d.ts +1 -0
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +12 -10
  58. package/dist/logging/index.js +1 -1
  59. package/dist/{logging-_h2mi3L9.js → logging-DviiAkLx.js} +2 -2
  60. package/dist/{logging-_h2mi3L9.js.map → logging-DviiAkLx.js.map} +1 -1
  61. package/dist/permission-service-CvGpwI0d.js +95 -0
  62. package/dist/permission-service-CvGpwI0d.js.map +1 -0
  63. package/dist/permissions/constants.d.ts +3 -0
  64. package/dist/permissions/constants.d.ts.map +1 -1
  65. package/dist/permissions/index.js +2 -1
  66. package/dist/permissions/permission-store.d.ts +1 -1
  67. package/dist/permissions/types.d.ts +1 -1
  68. package/dist/permissions/types.d.ts.map +1 -1
  69. package/dist/{permissions-D5ScGGnd.js → permissions-_n1hIG7Q.js} +8 -106
  70. package/dist/permissions-_n1hIG7Q.js.map +1 -0
  71. package/dist/query-client/index.js +1 -1
  72. package/dist/query-client/types.d.ts +9 -9
  73. package/dist/query-client/types.d.ts.map +1 -1
  74. package/dist/{query-client-Bpe08pk9.js → query-client-Bh3TyB8W.js} +2 -2
  75. package/dist/query-client-Bh3TyB8W.js.map +1 -0
  76. package/dist/react-router/index.js +12 -10
  77. package/dist/react-router/server.js +19 -17
  78. package/dist/redis-session-storage.server-v2VcpFsQ.js +500 -0
  79. package/dist/redis-session-storage.server-v2VcpFsQ.js.map +1 -0
  80. package/dist/{server-BHfKHKF_.js → server-B6PCxc-T.js} +39 -87
  81. package/dist/server-B6PCxc-T.js.map +1 -0
  82. package/dist/{server-DZmcd2Lr.js → server-C96NMFBy.js} +18 -10
  83. package/dist/server-C96NMFBy.js.map +1 -0
  84. package/dist/{server-BxvRxjCK.js → server-Ca-3DJf2.js} +44 -11
  85. package/dist/server-Ca-3DJf2.js.map +1 -0
  86. package/dist/{server-C3L8J7iE.js → server-Dxsww9v9.js} +6 -5
  87. package/dist/server-Dxsww9v9.js.map +1 -0
  88. package/dist/{server-CouYoZiw.js → server-hGrElJMG.js} +2 -2
  89. package/dist/{server-CouYoZiw.js.map → server-hGrElJMG.js.map} +1 -1
  90. package/dist/server.js +19 -17
  91. package/dist/signalr/index.js +1 -1
  92. package/dist/signalr/types.d.ts +1 -1
  93. package/dist/{signalr-SJ-1sIxw.js → signalr-k-70VHKo.js} +3 -2
  94. package/dist/signalr-k-70VHKo.js.map +1 -0
  95. package/dist/tenant/index.js +2 -2
  96. package/dist/tenant/server.js +4 -4
  97. package/dist/tenant/tenant-context.d.ts +17 -1
  98. package/dist/tenant/tenant-context.d.ts.map +1 -1
  99. package/dist/tenant/tenant-cookie.server.d.ts.map +1 -1
  100. package/dist/tenant/tenant-store.d.ts +54 -9
  101. package/dist/tenant/tenant-store.d.ts.map +1 -1
  102. package/dist/tenant/tenant.server.d.ts +20 -1
  103. package/dist/tenant/tenant.server.d.ts.map +1 -1
  104. package/dist/tenant/types.d.ts +29 -9
  105. package/dist/tenant/types.d.ts.map +1 -1
  106. package/dist/tenant-DfLemb6n.js +384 -0
  107. package/dist/tenant-DfLemb6n.js.map +1 -0
  108. package/dist/{types-BnKaWdfe.js → types-BaaE6wjY.js} +1 -1
  109. package/dist/{types-BnKaWdfe.js.map → types-BaaE6wjY.js.map} +1 -1
  110. package/docs/adapters.md +87 -0
  111. package/docs/architecture.md +193 -0
  112. package/docs/backend-adaptation.md +249 -0
  113. package/docs/codex-for-oss-application.md +31 -0
  114. package/docs/codex-maintenance.md +47 -0
  115. package/docs/installation.md +213 -0
  116. package/docs/module-reference.md +207 -0
  117. package/docs/releasing.md +92 -0
  118. package/package.json +27 -4
  119. package/dist/api-client-DsrxK05Z.js.map +0 -1
  120. package/dist/auth.server-BPiCtlJE.js +0 -5
  121. package/dist/auth.server-Cx2h2qu3.js +0 -724
  122. package/dist/auth.server-Cx2h2qu3.js.map +0 -1
  123. package/dist/identity-CFa1txoj.js.map +0 -1
  124. package/dist/permissions-D5ScGGnd.js.map +0 -1
  125. package/dist/query-client-Bpe08pk9.js.map +0 -1
  126. package/dist/redis-session-storage.server-BR_0y-QO.js +0 -250
  127. package/dist/redis-session-storage.server-BR_0y-QO.js.map +0 -1
  128. package/dist/server-BHfKHKF_.js.map +0 -1
  129. package/dist/server-BxvRxjCK.js.map +0 -1
  130. package/dist/server-C3L8J7iE.js.map +0 -1
  131. package/dist/server-DZmcd2Lr.js.map +0 -1
  132. package/dist/signalr-SJ-1sIxw.js.map +0 -1
  133. package/dist/tenant-C6KPaSCX.js +0 -256
  134. package/dist/tenant-C6KPaSCX.js.map +0 -1
  135. /package/dist/{src-CJ2RLi94.js → src-B9rDUgSb.js} +0 -0
@@ -0,0 +1,193 @@
1
+ # Architecture
2
+
3
+ This document describes how Spine is structured and where each kind of logic should live.
4
+
5
+ ## Goals
6
+
7
+ Spine tries to solve a common problem in internal platform code:
8
+
9
+ - product A builds infrastructure
10
+ - product B copies it
11
+ - product C forks the copy
12
+ - shared behavior starts drifting
13
+
14
+ Spine centralizes the reusable layer while keeping enough extension points for app-specific behavior.
15
+
16
+ ## Layer Model
17
+
18
+ Spine is organized into three layers:
19
+
20
+ 1. Core primitives
21
+ 2. Framework adapters
22
+ 3. Application adapters
23
+
24
+ ### 1. Core Primitives
25
+
26
+ Core primitives should:
27
+
28
+ - avoid framework-specific response helpers
29
+ - use standard Web `Request` and `Response` APIs
30
+ - expose configuration points for backend conventions
31
+ - stay reusable across multiple apps
32
+
33
+ Examples:
34
+
35
+ - token refresh
36
+ - session handling
37
+ - tenant cookie helpers
38
+ - identity cache
39
+ - permission checking
40
+ - API client configuration
41
+
42
+ ### 2. Framework Adapters
43
+
44
+ Framework adapters make core primitives ergonomic in a specific framework.
45
+
46
+ Examples:
47
+
48
+ - `@eminuckan/spine/react-router`
49
+ - `@eminuckan/spine/react-router/server`
50
+
51
+ Today the React Router adapter is intentionally thin. That is a feature, not a gap: it keeps the core honest and leaves room for future adapters such as Next.js.
52
+
53
+ ### 3. Application Adapters
54
+
55
+ Application adapters belong in the consuming app and translate Spine into product policy.
56
+
57
+ Examples:
58
+
59
+ - setup route redirects
60
+ - entitlement gating
61
+ - domain-specific permission constants
62
+ - generated backend API clients
63
+ - UI wrappers around generic hooks
64
+
65
+ ## Module Responsibilities
66
+
67
+ ### Auth
68
+
69
+ Owns:
70
+
71
+ - login
72
+ - callback handling
73
+ - logout
74
+ - token refresh
75
+ - Redis-backed session and OAuth state
76
+ - generic route protection primitives
77
+
78
+ Does not own:
79
+
80
+ - product-specific post-login routing
81
+ - setup flow decisions
82
+
83
+ ### Tenant
84
+
85
+ Owns:
86
+
87
+ - tenant cookie primitives
88
+ - current tenant resolution
89
+ - available tenant resolution
90
+ - tenant client store and provider
91
+ - membership-aware tenant state
92
+
93
+ Does not own:
94
+
95
+ - product-specific no-tenant redirects
96
+ - tenant provisioning flows
97
+
98
+ ### Identity
99
+
100
+ Owns:
101
+
102
+ - identity context caching
103
+ - permission fetch orchestration
104
+ - transformation from identity context to tenant-aware user info
105
+ - client identity store and provider
106
+
107
+ Does not own:
108
+
109
+ - backend DTO generation
110
+ - product-specific setup data semantics beyond generic fields
111
+
112
+ ### Permissions
113
+
114
+ Owns:
115
+
116
+ - client permission store
117
+ - permission hooks
118
+ - generic permission components
119
+ - generic server-side permission route protection
120
+
121
+ Does not own:
122
+
123
+ - your product's permission vocabulary
124
+ - domain-specific permission constants
125
+
126
+ ### API Client
127
+
128
+ Owns:
129
+
130
+ - base URL and header composition
131
+ - auth header injection
132
+ - tenant header injection
133
+ - fetch middleware
134
+ - retry and error handling
135
+
136
+ Does not own:
137
+
138
+ - generated SDK clients
139
+ - product-specific endpoint wrappers
140
+
141
+ ## Composition Pattern
142
+
143
+ Spine prefers configuration and composition over inheritance or deep factories.
144
+
145
+ The pattern is:
146
+
147
+ 1. Core exposes a generic primitive
148
+ 2. App provides fetchers, endpoint conventions, or redirect policies
149
+ 3. App re-exports a thin local adapter if it wants ergonomic imports
150
+
151
+ This keeps the public API reusable without forcing all apps into the same workflow.
152
+
153
+ ## Where New Code Should Go
154
+
155
+ Use this checklist before adding a new primitive.
156
+
157
+ ### Put it in Core if
158
+
159
+ - it solves a repeated platform problem
160
+ - it can be configured instead of hardcoded
161
+ - it does not depend on a single app's domain
162
+ - it can be explained without referencing a product name
163
+
164
+ ### Put it in a Framework Adapter if
165
+
166
+ - it depends on framework routing or request conventions
167
+ - it is still reusable across many apps using that framework
168
+
169
+ ### Keep it in the App if
170
+
171
+ - it encodes product policy
172
+ - it references app-only routes
173
+ - it assumes app-only claims, plans, or workflows
174
+ - it is mostly a domain-specific convenience layer
175
+
176
+ ## Current Tradeoffs
177
+
178
+ Spine is already framework-agnostic at the request/response level, but not every module is maximally abstract yet. Some modules still use environment-driven configuration where factory-based configuration may eventually be better.
179
+
180
+ That is acceptable as long as:
181
+
182
+ - the surface remains reusable
183
+ - product logic stays outside core
184
+ - adapters remain thin
185
+
186
+ ## Future Adapter Direction
187
+
188
+ The intended path is:
189
+
190
+ - keep root package framework-agnostic
191
+ - keep adapter packages shallow
192
+ - let apps build local adapters for product policy
193
+ - eventually add first-class Next.js support without changing the core mental model
@@ -0,0 +1,249 @@
1
+ # Backend Adaptation
2
+
3
+ Spine should adapt to your backend, not force your backend to look like the original app it came from.
4
+
5
+ This guide covers the main extension points.
6
+
7
+ ## Adaptation Categories
8
+
9
+ There are four major kinds of backend adaptation:
10
+
11
+ 1. Auth claims
12
+ 2. Identity endpoints
13
+ 3. Permission resolution
14
+ 4. Tenant conventions
15
+
16
+ ## Auth Claim Mapping
17
+
18
+ Use `configureAuthClaimMapping` when your identity provider uses different claim names.
19
+
20
+ ```ts
21
+ import { configureAuthClaimMapping } from '@eminuckan/spine/server';
22
+
23
+ configureAuthClaimMapping({
24
+ subject: ['sub', 'user_id'],
25
+ email: ['email', 'preferred_username'],
26
+ givenName: ['given_name', 'first_name'],
27
+ familyName: ['family_name', 'last_name'],
28
+ permissions: ['permissions', 'scope'],
29
+ tenantIds: ['tenant_ids'],
30
+ tenantRoles: ['tenant_roles'],
31
+ isOnboarded: ['is_onboarded', 'profile_complete'],
32
+ });
33
+ ```
34
+
35
+ ## Identity API Fetching
36
+
37
+ Use `configureIdentityAPIFetcher` to tell Spine how to fetch the current identity context.
38
+
39
+ ```ts
40
+ import { configureIdentityAPIFetcher } from '@eminuckan/spine/identity/server';
41
+
42
+ configureIdentityAPIFetcher(async (request) => {
43
+ const response = await fetch('https://api.example.com/me/context', {
44
+ headers: {
45
+ Authorization: request.headers.get('Authorization') ?? '',
46
+ },
47
+ });
48
+
49
+ const payload = await response.json();
50
+
51
+ return {
52
+ userId: payload.user.id,
53
+ email: payload.user.email,
54
+ memberships: payload.memberships,
55
+ hasAnyMembership: payload.memberships.length > 0,
56
+ isOnboarded: payload.flags.isOnboarded,
57
+ hasEntitlement: payload.flags.hasEntitlement,
58
+ contextVersion: payload.version,
59
+ };
60
+ });
61
+ ```
62
+
63
+ ## Permission Fetching
64
+
65
+ Use `configurePermissionFetcher` when permissions are loaded separately from identity context.
66
+
67
+ ```ts
68
+ import { configurePermissionFetcher } from '@eminuckan/spine/identity/server';
69
+
70
+ configurePermissionFetcher(async (request, tenantId) => {
71
+ const response = await fetch(`https://api.example.com/tenants/${tenantId}/permissions`, {
72
+ headers: {
73
+ Authorization: request.headers.get('Authorization') ?? '',
74
+ },
75
+ });
76
+
77
+ const payload = await response.json();
78
+ return payload.permissions;
79
+ });
80
+ ```
81
+
82
+ ## Client Identity Store
83
+
84
+ Simple apps can configure endpoint paths for browser-side identity refresh.
85
+
86
+ ```ts
87
+ import { configureIdentityStore } from '@eminuckan/spine/identity';
88
+
89
+ configureIdentityStore({
90
+ contextEndpoint: '/api/me/context',
91
+ permissionsEndpoint: '/api/me/permissions',
92
+ logoutPath: '/session/logout',
93
+ });
94
+ ```
95
+
96
+ Apps with different response contracts can own the fetch and mapping logic.
97
+
98
+ ```ts
99
+ configureIdentityStore({
100
+ fetchContext: async () => {
101
+ const response = await fetch('/session/me');
102
+ const payload = await response.json();
103
+
104
+ return {
105
+ userId: payload.account.id,
106
+ email: payload.account.email,
107
+ memberships: payload.workspaces.map((workspace) => ({
108
+ tenantId: workspace.id,
109
+ tenantName: workspace.name,
110
+ })),
111
+ };
112
+ },
113
+ fetchPermissions: async () => {
114
+ const response = await fetch('/session/capabilities');
115
+ const payload = await response.json();
116
+ return payload.capabilities;
117
+ },
118
+ onUnauthorized: ({ logoutPath }) => {
119
+ window.location.assign(`${logoutPath}?reason=expired`);
120
+ },
121
+ });
122
+ ```
123
+
124
+ ## Tenant Cookie Policy
125
+
126
+ Use `configureTenantCookie` to match your app's cookie requirements.
127
+
128
+ ```ts
129
+ import { configureTenantCookie } from '@eminuckan/spine/tenant/server';
130
+
131
+ configureTenantCookie({
132
+ name: '__app_tenant',
133
+ httpOnly: false,
134
+ sameSite: 'Lax',
135
+ path: '/',
136
+ secure: process.env.NODE_ENV === 'production',
137
+ });
138
+ ```
139
+
140
+ This is especially useful when:
141
+
142
+ - the tenant cookie must be client-readable
143
+ - the cookie name differs across products
144
+ - secure/samesite behavior varies by deployment model
145
+
146
+ ## Tenant Resolution
147
+
148
+ Use `configureTenantResolution` so tenant helpers can derive tenant state from any identity context shape.
149
+
150
+ ```ts
151
+ import { configureTenantResolution } from '@eminuckan/spine/tenant/server';
152
+ import { fetchIdentityContext } from './identity.server';
153
+
154
+ configureTenantResolution({
155
+ identityContextFetcher: fetchIdentityContext,
156
+ resolveInitialTenant: ({ identityContext }) => {
157
+ return identityContext?.workspaces?.[0]?.workspaceId ?? null;
158
+ },
159
+ resolveAvailableTenants: ({ identityContext }) => {
160
+ return identityContext?.workspaces?.map((workspace) => workspace.workspaceId) ?? [];
161
+ },
162
+ });
163
+ ```
164
+
165
+ The older `configureIdentityContextFetcher` helper remains available for simple
166
+ identity contexts that already expose `memberships[].tenantId`.
167
+
168
+ ## Route Protection Policy
169
+
170
+ Use `configureRouteProtection` for product-specific route policy that still belongs close to auth.
171
+
172
+ ```ts
173
+ import { configureRouteProtection } from '@eminuckan/spine/server';
174
+
175
+ configureRouteProtection({
176
+ getLoginReturnUrl: ({ request }) => new URL(request.url).pathname,
177
+ resolveRoute: async ({ protection, getIdentityContext }) => {
178
+ const identityContext = await getIdentityContext();
179
+ if (protection === 'auth' && identityContext?.isOnboarded === false) {
180
+ return { location: '/setup' };
181
+ }
182
+
183
+ return null;
184
+ },
185
+ });
186
+ ```
187
+
188
+ Use this for framework/app coordination, not domain constants.
189
+
190
+ ## Permission Route Protection
191
+
192
+ Use `configurePermissionRouteProtection` to tell Spine how to get the current session and permission context.
193
+
194
+ ```ts
195
+ import { configurePermissionRouteProtection } from '@eminuckan/spine/server';
196
+
197
+ configurePermissionRouteProtection({
198
+ getSession: async (request) => {
199
+ return {
200
+ user: { sub: '123' },
201
+ accessToken: 'token',
202
+ };
203
+ },
204
+ resolveContext: async () => {
205
+ return {
206
+ permissions: ['Identity.Users.View'],
207
+ currentTenant: 'tenant-1',
208
+ };
209
+ },
210
+ });
211
+ ```
212
+
213
+ ## API Client Adaptation
214
+
215
+ Use `createAPIConfigFactory` to keep auth and tenant resolution centralized.
216
+
217
+ ```ts
218
+ import { createAPIConfigFactory } from '@eminuckan/spine/api-client/server';
219
+
220
+ const { createAPIConfig, getAPIBaseURL } = createAPIConfigFactory(
221
+ getAccessToken,
222
+ getCurrentTenant,
223
+ logger,
224
+ {
225
+ baseURL: process.env.API_BASE_URL,
226
+ userAgent: 'MyApp/1.0',
227
+ tenantHeaderName: 'X-Workspace-Id',
228
+ }
229
+ );
230
+ ```
231
+
232
+ If your backend does not use bearer auth or tenant headers, set `authHeaderName`
233
+ or `tenantHeaderName` to `null` and provide `buildHeaders`.
234
+
235
+ ## Practical Rule
236
+
237
+ If the value could change from one backend to another, it should usually be configured.
238
+
239
+ Examples:
240
+
241
+ - claim names
242
+ - endpoint paths and response shapes
243
+ - logout URLs
244
+ - cookie policies
245
+ - redirect decisions
246
+ - auth and tenant header names
247
+ - permission loading
248
+
249
+ If the logic is universal infrastructure, it should usually stay in core.
@@ -0,0 +1,31 @@
1
+ # Codex For Open Source Application Notes
2
+
3
+ This is the maintainer-facing application packet for the Codex for Open Source program. Do not add secrets, private customer details, or non-public product information here.
4
+
5
+ ## Repository
6
+
7
+ - GitHub: `https://github.com/eminuckan/spine`
8
+ - npm: `https://www.npmjs.com/package/@eminuckan/spine`
9
+ - Package: `@eminuckan/spine`
10
+ - License: MIT
11
+ - Role: primary maintainer
12
+
13
+ ## Qualification Draft
14
+
15
+ Spine is an MIT SaaS frontend infrastructure library for auth, identity, tenancy, permissions, API access, realtime clients, and framework adapters. It is extracted from repeated production-facing SaaS frontend needs, has a published npm package, docs, tests, CI, and an example app, and is being maintained as a reusable OSS foundation.
16
+
17
+ ## API Credit Use Draft
18
+
19
+ Use Codex for core OSS maintenance: PR review, issue triage, release checklist automation, security review of auth/session code, docs drift checks, adapter scaffolding for Next.js/Clerk/Supabase, and regression repair. All changes remain maintainer-reviewed and must pass `pnpm check`.
20
+
21
+ ## Anything Else Draft
22
+
23
+ Spine is early-stage but solves a common repeated SaaS problem: teams copy auth, tenant, permission, API, query, and realtime infrastructure between apps. The project has been rebooted as independent OSS with generic contracts, public docs, tests, issue templates, roadmap, and a maintenance plan for Codex-assisted review and release work.
24
+
25
+ ## Evidence To Refresh Before Submission
26
+
27
+ - GitHub stars, forks, issues, pull requests, and latest push date.
28
+ - npm version and last-month download count.
29
+ - CI status on the default branch.
30
+ - Latest published changelog entry.
31
+ - Public production-facing usage links, if the maintainer wants to include them in the private form.
@@ -0,0 +1,47 @@
1
+ # Codex Maintenance Plan
2
+
3
+ This document explains how Spine intends to use Codex for open source maintenance work.
4
+
5
+ ## Goal
6
+
7
+ Spine is a compact infrastructure library, but the maintenance surface is broad: auth, identity, tenancy, permissions, API access, realtime clients, framework adapters, examples, tests, docs, and release workflows. Codex should reduce maintainer load while keeping human review and ownership explicit.
8
+
9
+ ## Planned Workflows
10
+
11
+ - Pull request review: summarize changes, identify risk areas, suggest missing tests, and check public API impact.
12
+ - Issue triage: classify bug reports, adapter requests, backend-contract questions, and documentation gaps.
13
+ - Release workflow: verify changelog entries, package contents, CI status, and npm dry runs before publishing.
14
+ - Documentation maintenance: keep installation, backend adaptation, adapter, and module reference docs aligned with code.
15
+ - Adapter scaffolding: draft Next.js, Clerk, Supabase, Vue, and Svelte adapter experiments behind issues and review gates.
16
+ - Security hardening: review session, OIDC, redirect, cookie, and token-handling changes with focused checklists.
17
+ - Regression repair: investigate failing tests or consuming-app compatibility issues without leaking app-specific policy into core.
18
+
19
+ ## Guardrails
20
+
21
+ - The maintainer makes final merge and release decisions.
22
+ - All code changes must pass `pnpm check`.
23
+ - Package contents should be checked with `npm pack --dry-run` before publishing.
24
+ - Private application code, credentials, tokens, customer data, and non-public business context should not be included in public issues or documentation.
25
+ - Codex should not scan or review repositories unless the maintainer owns them or has authorization.
26
+ - Product-specific behavior should stay in consuming apps unless it has been generalized and documented.
27
+
28
+ ## API Credit Use
29
+
30
+ API credits would be used for:
31
+
32
+ - automated pull request review experiments
33
+ - issue triage and duplicate detection
34
+ - release checklist automation
35
+ - security-focused reviews for auth and session code
36
+ - adapter proof-of-concept generation
37
+ - documentation drift checks between source and public docs
38
+
39
+ These are core OSS maintenance tasks, not product-specific development work.
40
+
41
+ ## Success Signals
42
+
43
+ - Faster response time on issues and pull requests.
44
+ - More complete adapter examples without expanding core policy.
45
+ - Fewer release mistakes.
46
+ - Better test coverage for security-sensitive code.
47
+ - Clearer docs for teams integrating Spine into different SaaS backends.