spine-framework 0.3.17 → 0.3.18
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/.framework/cli/commands/create-app.ts +1 -1
- package/.framework/cli/commands/dev.ts +215 -33
- package/.framework/cli/index.ts +37 -75
- package/.framework/functions/_shared/app-manifest.ts +8 -15
- package/.framework/functions/_shared/db.ts +11 -15
- package/.framework/functions/_shared/index.ts +7 -16
- package/.framework/functions/_shared/principal.ts +4 -62
- package/.framework/functions/_shared/resolve-ids.ts +6 -6
- package/.framework/functions/_shared/webhook-registry.ts +43 -1
- package/.framework/functions/admin-funnel.ts +104 -0
- package/.framework/functions/admin-install-funnel.ts +225 -0
- package/.framework/functions/admin-signals.ts +241 -0
- package/.framework/functions/integration-routes.ts +4 -8
- package/.framework/migrations/001_schema.sql +406 -0
- package/.framework/migrations/002_rls_and_functions.sql +710 -0
- package/.framework/migrations/003_system_data.sql +520 -0
- package/.framework/migrations/004_enhancements.sql +43 -0
- package/.framework/public/_redirects +3 -0
- package/.framework/public/spine-logo.jpg +0 -0
- package/.framework/public/spine-logo.svg +3 -0
- package/{scripts → .framework/scripts}/assemble-frontend.sh +8 -5
- package/{scripts → .framework/scripts}/assemble-functions.sh +4 -47
- package/{scripts → .framework/scripts}/assemble.sh +2 -8
- package/{scripts → .framework/scripts}/boundary-check.sh +5 -9
- package/{scripts → .framework/scripts}/check-core-integrity.sh +0 -0
- package/.framework/seeds/link_types/account_cli_instances.json +9 -0
- package/.framework/seeds/types/funnel_signal_use.json +98 -0
- package/.framework/seeds/types/spine_instance.json +109 -0
- package/.framework/src/App.tsx +11 -21
- package/.framework/src/components/CustomAppLoader.tsx +2 -19
- package/.framework/src/components/ui/accordion.tsx +1 -1
- package/.framework/src/components/ui/alert-dialog.tsx +1 -1
- package/.framework/src/components/ui/aspect-ratio.tsx +1 -1
- package/.framework/src/components/ui/avatar.tsx +1 -1
- package/.framework/src/components/ui/badge.tsx +2 -2
- package/.framework/src/components/ui/breadcrumb.tsx +2 -2
- package/.framework/src/components/ui/button-group.tsx +2 -2
- package/.framework/src/components/ui/button.tsx +2 -2
- package/.framework/src/components/ui/chart.tsx +6 -1
- package/.framework/src/components/ui/checkbox.tsx +1 -1
- package/.framework/src/components/ui/collapsible.tsx +1 -1
- package/.framework/src/components/ui/context-menu.tsx +1 -1
- package/.framework/src/components/ui/dialog.tsx +1 -1
- package/.framework/src/components/ui/direction.tsx +17 -1
- package/.framework/src/components/ui/dropdown-menu.tsx +1 -1
- package/.framework/src/components/ui/hover-card.tsx +1 -1
- package/.framework/src/components/ui/item.tsx +2 -2
- package/.framework/src/components/ui/label.tsx +1 -1
- package/.framework/src/components/ui/menubar.tsx +1 -1
- package/.framework/src/components/ui/navigation-menu.tsx +1 -1
- package/.framework/src/components/ui/popover.tsx +1 -1
- package/.framework/src/components/ui/progress.tsx +1 -1
- package/.framework/src/components/ui/radio-group.tsx +1 -1
- package/.framework/src/components/ui/scroll-area.tsx +1 -1
- package/.framework/src/components/ui/select.tsx +1 -1
- package/.framework/src/components/ui/separator.tsx +1 -1
- package/.framework/src/components/ui/sheet.tsx +1 -1
- package/.framework/src/components/ui/sidebar.tsx +6 -6
- package/.framework/src/components/ui/slider.tsx +1 -1
- package/.framework/src/components/ui/switch.tsx +1 -1
- package/.framework/src/components/ui/tabs.tsx +1 -1
- package/.framework/src/components/ui/toggle-group.tsx +1 -1
- package/.framework/src/components/ui/toggle.tsx +1 -1
- package/.framework/src/components/ui/tooltip.tsx +1 -1
- package/.framework/src/pages/auth/LoginPage.tsx +10 -9
- package/LICENSE.md +63 -93
- package/README.md +254 -129
- package/custom/.devin/skills/install-app/SKILL.md +39 -0
- package/custom/.devin/workflows/app-install.yaml +83 -0
- package/custom/README.md +79 -0
- package/docs/APPS.md +232 -0
- package/docs/ARCHITECTURE.md +246 -0
- package/docs/INSTALL.md +155 -0
- package/docs/README.md +170 -0
- package/docs/design-schema-spec.md +1837 -0
- package/docs/dev-guides/01-create-an-app.md +183 -0
- package/docs/dev-guides/02-create-a-function.md +178 -0
- package/docs/dev-guides/03-create-a-component.md +178 -0
- package/docs/dev-guides/04-webhook-handlers.md +129 -0
- package/docs/dev-guides/05-testing.md +168 -0
- package/docs/dev-guides/README.md +40 -0
- package/docs/npm-packages-plan.md +288 -0
- package/docs/spine-core--api-reference.md +822 -0
- package/package.json +78 -150
- package/.framework/cli/bin.cjs +0 -14
- package/.framework/cli/commands/claim.ts +0 -430
- package/.framework/cli/commands/init.ts +0 -219
- package/.framework/cli/commands/install-app.ts +0 -685
- package/.framework/cli/commands/migrate.ts +0 -178
- package/.framework/cli/commands/publish.ts +0 -191
- package/.framework/cli/commands/status.ts +0 -268
- package/.framework/cli/commands/uninstall-app.ts +0 -166
- package/.framework/cli/commands/update-app.ts +0 -397
- package/.framework/cli/welcome.cjs +0 -45
- package/.framework/config/signal-client.ts +0 -147
- package/.framework/config/signals.ts +0 -70
- package/.framework/docs/STABILITY.md +0 -52
- package/.framework/docs/examples/custom-app/README.md +0 -77
- package/.framework/docs/examples/custom-function/README.md +0 -27
- package/.framework/docs/examples/custom-function/handler.ts +0 -48
- package/.framework/docs/examples/custom-webhook/README.md +0 -68
- package/.framework/functions/handler-registry.ts +0 -22
- package/.framework/functions/signal.ts +0 -168
- package/.framework/migrations/000_foundation.sql +0 -1302
- package/.framework/migrations/001_seed.sql +0 -163
- package/.framework/migrations/002_seed_constraints.sql +0 -19
- package/.framework/migrations/003_auth_user_trigger.sql +0 -67
- package/.framework/migrations/004_app_version_tracking.sql +0 -8
- package/.framework/migrations/005_pipelines_slug_stages.sql +0 -21
- package/.framework/src/pages/auth/RegisterPage.tsx +0 -163
- package/CHANGELOG.md +0 -71
- package/STRUCTURE.md +0 -150
- package/bin/spine-framework.cjs +0 -62
- package/bin/welcome.cjs +0 -58
- package/bin/ws-shim.ts +0 -10
- package/config/components.json +0 -25
- package/config/deno.lock +0 -108
- package/config/package-lock.json +0 -17183
- package/config/postcss.config.cjs +0 -10
- package/config/tailwind.config.cjs +0 -78
- package/config/tsconfig.build.json +0 -32
- package/config/tsconfig.cli.json +0 -18
- package/config/tsconfig.json +0 -41
- package/config/tsconfig.node.json +0 -17
- package/config/tsconfig.node.tsbuildinfo +0 -1
- package/config/tsconfig.tsbuildinfo +0 -1
- package/config/typedoc.json +0 -16
- package/config/vite.config.d.ts +0 -2
- package/config/vite.config.ts +0 -71
- package/dist/cli/commands/agents.d.ts +0 -39
- package/dist/cli/commands/agents.d.ts.map +0 -1
- package/dist/cli/commands/auth.d.ts +0 -36
- package/dist/cli/commands/auth.d.ts.map +0 -1
- package/dist/cli/commands/claim.d.ts +0 -38
- package/dist/cli/commands/claim.d.ts.map +0 -1
- package/dist/cli/commands/create-app.d.ts +0 -23
- package/dist/cli/commands/create-app.d.ts.map +0 -1
- package/dist/cli/commands/dev.d.ts +0 -24
- package/dist/cli/commands/dev.d.ts.map +0 -1
- package/dist/cli/commands/doctor.d.ts +0 -42
- package/dist/cli/commands/doctor.d.ts.map +0 -1
- package/dist/cli/commands/generate.d.ts +0 -36
- package/dist/cli/commands/generate.d.ts.map +0 -1
- package/dist/cli/commands/init.d.ts +0 -20
- package/dist/cli/commands/init.d.ts.map +0 -1
- package/dist/cli/commands/install-app.d.ts +0 -30
- package/dist/cli/commands/install-app.d.ts.map +0 -1
- package/dist/cli/commands/items.d.ts +0 -45
- package/dist/cli/commands/items.d.ts.map +0 -1
- package/dist/cli/commands/migrate.d.ts +0 -21
- package/dist/cli/commands/migrate.d.ts.map +0 -1
- package/dist/cli/commands/migrations.d.ts +0 -41
- package/dist/cli/commands/migrations.d.ts.map +0 -1
- package/dist/cli/commands/pipelines.d.ts +0 -40
- package/dist/cli/commands/pipelines.d.ts.map +0 -1
- package/dist/cli/commands/publish.d.ts +0 -23
- package/dist/cli/commands/publish.d.ts.map +0 -1
- package/dist/cli/commands/status.d.ts +0 -23
- package/dist/cli/commands/status.d.ts.map +0 -1
- package/dist/cli/commands/system.d.ts +0 -29
- package/dist/cli/commands/system.d.ts.map +0 -1
- package/dist/cli/commands/test.d.ts +0 -46
- package/dist/cli/commands/test.d.ts.map +0 -1
- package/dist/cli/commands/uninstall-app.d.ts +0 -23
- package/dist/cli/commands/uninstall-app.d.ts.map +0 -1
- package/dist/cli/commands/update-app.d.ts +0 -24
- package/dist/cli/commands/update-app.d.ts.map +0 -1
- package/dist/cli/context.d.ts +0 -88
- package/dist/cli/context.d.ts.map +0 -1
- package/dist/cli/env-loader.d.ts +0 -14
- package/dist/cli/env-loader.d.ts.map +0 -1
- package/dist/cli/index.d.ts +0 -44
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/config/signals.d.ts +0 -82
- package/dist/config/signals.d.ts.map +0 -1
- package/dist/functions/_shared/agent-runner.d.ts +0 -156
- package/dist/functions/_shared/agent-runner.d.ts.map +0 -1
- package/dist/functions/_shared/app-manifest.d.ts +0 -68
- package/dist/functions/_shared/app-manifest.d.ts.map +0 -1
- package/dist/functions/_shared/audit.d.ts +0 -91
- package/dist/functions/_shared/audit.d.ts.map +0 -1
- package/dist/functions/_shared/db.d.ts +0 -125
- package/dist/functions/_shared/db.d.ts.map +0 -1
- package/dist/functions/_shared/index.d.ts +0 -299
- package/dist/functions/_shared/index.d.ts.map +0 -1
- package/dist/functions/_shared/middleware.d.ts +0 -315
- package/dist/functions/_shared/middleware.d.ts.map +0 -1
- package/dist/functions/_shared/permissions.d.ts +0 -626
- package/dist/functions/_shared/permissions.d.ts.map +0 -1
- package/dist/functions/_shared/pipeline-runner.d.ts +0 -124
- package/dist/functions/_shared/pipeline-runner.d.ts.map +0 -1
- package/dist/functions/_shared/principal.d.ts +0 -284
- package/dist/functions/_shared/principal.d.ts.map +0 -1
- package/dist/functions/_shared/resolve-ids.d.ts +0 -10
- package/dist/functions/_shared/resolve-ids.d.ts.map +0 -1
- package/dist/functions/_shared/schema-utils.d.ts +0 -181
- package/dist/functions/_shared/schema-utils.d.ts.map +0 -1
- package/dist/functions/_shared/testing.d.ts +0 -172
- package/dist/functions/_shared/testing.d.ts.map +0 -1
- package/dist/functions/_shared/trigger-engine.d.ts +0 -140
- package/dist/functions/_shared/trigger-engine.d.ts.map +0 -1
- package/dist/functions/_shared/webhook-registration.d.ts +0 -81
- package/dist/functions/_shared/webhook-registration.d.ts.map +0 -1
- package/dist/functions/_shared/webhook-registry.d.ts +0 -41
- package/dist/functions/_shared/webhook-registry.d.ts.map +0 -1
- package/dist/functions/account-nodes.d.ts +0 -48
- package/dist/functions/account-nodes.d.ts.map +0 -1
- package/dist/functions/admin-data.d.ts +0 -178
- package/dist/functions/admin-data.d.ts.map +0 -1
- package/dist/functions/ai-agents.d.ts +0 -125
- package/dist/functions/ai-agents.d.ts.map +0 -1
- package/dist/functions/api-keys.d.ts +0 -140
- package/dist/functions/api-keys.d.ts.map +0 -1
- package/dist/functions/apps.d.ts +0 -163
- package/dist/functions/apps.d.ts.map +0 -1
- package/dist/functions/auth.d.ts +0 -74
- package/dist/functions/auth.d.ts.map +0 -1
- package/dist/functions/debug-auth.d.ts +0 -33
- package/dist/functions/debug-auth.d.ts.map +0 -1
- package/dist/functions/embeddings.d.ts +0 -205
- package/dist/functions/embeddings.d.ts.map +0 -1
- package/dist/functions/handler-registry.d.ts +0 -22
- package/dist/functions/handler-registry.d.ts.map +0 -1
- package/dist/functions/integration-routes.d.ts +0 -45
- package/dist/functions/integration-routes.d.ts.map +0 -1
- package/dist/functions/integrations.d.ts +0 -124
- package/dist/functions/integrations.d.ts.map +0 -1
- package/dist/functions/item-progress.d.ts +0 -41
- package/dist/functions/item-progress.d.ts.map +0 -1
- package/dist/functions/logs.d.ts +0 -162
- package/dist/functions/logs.d.ts.map +0 -1
- package/dist/functions/observability.d.ts +0 -123
- package/dist/functions/observability.d.ts.map +0 -1
- package/dist/functions/pipeline-executions.d.ts +0 -190
- package/dist/functions/pipeline-executions.d.ts.map +0 -1
- package/dist/functions/pipelines.d.ts +0 -171
- package/dist/functions/pipelines.d.ts.map +0 -1
- package/dist/functions/prompt-configs.d.ts +0 -125
- package/dist/functions/prompt-configs.d.ts.map +0 -1
- package/dist/functions/roles.d.ts +0 -118
- package/dist/functions/roles.d.ts.map +0 -1
- package/dist/functions/signal.d.ts +0 -73
- package/dist/functions/signal.d.ts.map +0 -1
- package/dist/functions/system-cron.d.ts +0 -65
- package/dist/functions/system-cron.d.ts.map +0 -1
- package/dist/functions/system.d.ts +0 -29
- package/dist/functions/system.d.ts.map +0 -1
- package/dist/functions/tests.d.ts +0 -28
- package/dist/functions/tests.d.ts.map +0 -1
- package/dist/functions/timers.d.ts +0 -139
- package/dist/functions/timers.d.ts.map +0 -1
- package/dist/functions/triggers.d.ts +0 -203
- package/dist/functions/triggers.d.ts.map +0 -1
- package/dist/functions/types.d.ts +0 -151
- package/dist/functions/types.d.ts.map +0 -1
- package/dist/src/types/types.d.ts +0 -364
- package/dist/src/types/types.d.ts.map +0 -1
- package/index.html +0 -13
- package/netlify.toml +0 -36
- package/package-project.json +0 -71
- package/scripts/ingest-chunks.cjs +0 -202
- package/scripts/kb-chunk-parser.cjs +0 -312
- package/scripts/kb-chunk-parser.ts +0 -330
- package/scripts/load-test-app-install.ts +0 -484
- package/scripts/netlify-dev-wrapper.sh +0 -22
- package/vitest.config.ts +0 -45
- /package/{scripts → .framework/scripts}/app-install-cli.ts +0 -0
- /package/{scripts → .framework/scripts}/build-manifest.sh +0 -0
- /package/{scripts → .framework/scripts}/verify-integrity.sh +0 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Guide 05: Testing Custom Code
|
|
2
|
+
|
|
3
|
+
## What this guide covers
|
|
4
|
+
|
|
5
|
+
How to test custom functions, components, and apps using the Spine test harness.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Test commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm run test # All tests
|
|
13
|
+
npm run test:unit # Unit tests only (.framework/tests/unit/)
|
|
14
|
+
npm run test:core # Core isolation test (no custom code required)
|
|
15
|
+
npm run test:integration # Integration tests (requires running Supabase)
|
|
16
|
+
npm run test:boundary # Architectural boundary check
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Testing a custom function
|
|
22
|
+
|
|
23
|
+
Use `makeTestContext` from the core testing utilities to create a mock `CoreContext`:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// custom/tests/my-feature.test.ts
|
|
27
|
+
import { describe, it, expect } from 'vitest'
|
|
28
|
+
import { makeTestContext, mockPrincipal } from '@core/_shared/testing'
|
|
29
|
+
|
|
30
|
+
// Import your handler's logic (not the Netlify wrapper)
|
|
31
|
+
import { handler } from '../functions/custom_my-feature'
|
|
32
|
+
|
|
33
|
+
describe('custom_my-feature', () => {
|
|
34
|
+
it('returns items for list action', async () => {
|
|
35
|
+
const ctx = makeTestContext({
|
|
36
|
+
principal: mockPrincipal({ roles: ['member'] }),
|
|
37
|
+
query: { action: 'list' },
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const result = await handler({} as any, ctx)
|
|
41
|
+
expect(result.data).toBeDefined()
|
|
42
|
+
expect(Array.isArray(result.data)).toBe(true)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('rejects unauthenticated requests', async () => {
|
|
46
|
+
const ctx = makeTestContext({ principal: null })
|
|
47
|
+
|
|
48
|
+
await expect(handler({} as any, ctx)).rejects.toThrow()
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## makeTestContext options
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
makeTestContext({
|
|
59
|
+
principal?: Principal | null, // defaults to a mock member principal
|
|
60
|
+
query?: Record<string, string>, // URL query params
|
|
61
|
+
body?: any, // parsed request body
|
|
62
|
+
db?: SupabaseClient, // override with a real or mock DB client
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## mockPrincipal options
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
mockPrincipal({
|
|
72
|
+
id?: string // UUID, defaults to a stable test UUID
|
|
73
|
+
account_id?: string // UUID, defaults to a stable test UUID
|
|
74
|
+
roles?: string[] // defaults to ['member']
|
|
75
|
+
permissions?: string[]
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Testing role-based access
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
it('allows support role to access restricted data', async () => {
|
|
85
|
+
const ctx = makeTestContext({
|
|
86
|
+
principal: mockPrincipal({ roles: ['support'] }),
|
|
87
|
+
query: { action: 'list' },
|
|
88
|
+
})
|
|
89
|
+
const result = await handler({} as any, ctx)
|
|
90
|
+
expect(result.data).toBeDefined()
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('denies member role from restricted endpoint', async () => {
|
|
94
|
+
const ctx = makeTestContext({
|
|
95
|
+
principal: mockPrincipal({ roles: ['member'] }),
|
|
96
|
+
query: { action: 'admin_list' },
|
|
97
|
+
})
|
|
98
|
+
await expect(handler({} as any, ctx)).rejects.toThrow('required')
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Boundary check
|
|
105
|
+
|
|
106
|
+
The boundary check verifies that core never imports from custom, and that custom apps use `@core` aliases (not relative paths to `.framework/`):
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npm run test:boundary
|
|
110
|
+
# or
|
|
111
|
+
bash scripts/boundary-check.sh
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Run this before any PR. The GitHub Actions CI also runs it automatically.
|
|
115
|
+
|
|
116
|
+
**What it checks:**
|
|
117
|
+
1. No `from 'custom/'` or `import @custom` in `.framework/`
|
|
118
|
+
2. No `from 'custom_'` module imports in `.framework/`
|
|
119
|
+
3. No relative paths to `.framework/` from `custom/` (must use `@core`)
|
|
120
|
+
4. No app-specific slugs hardcoded in `.framework/src/`
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Running core tests in isolation
|
|
125
|
+
|
|
126
|
+
Core tests must pass without any custom code present:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npm run test:core
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This verifies the architectural boundary holds — if core tests fail, it means core has leaked a dependency on custom code.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Integration tests
|
|
137
|
+
|
|
138
|
+
Integration tests require a running Supabase instance. Set your `.env` before running:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# .env
|
|
142
|
+
SUPABASE_URL=http://localhost:54321
|
|
143
|
+
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
|
|
144
|
+
|
|
145
|
+
npm run test:integration
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## File locations
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
.framework/tests/
|
|
154
|
+
unit/
|
|
155
|
+
core-isolation.test.ts ← core tests with no custom deps
|
|
156
|
+
permissions.test.ts
|
|
157
|
+
pipeline-runner.test.ts
|
|
158
|
+
integration/
|
|
159
|
+
admin-data-accounts.test.ts
|
|
160
|
+
isolation.test.ts
|
|
161
|
+
...
|
|
162
|
+
fixtures/
|
|
163
|
+
seed.ts ← test data seeding
|
|
164
|
+
teardown.ts
|
|
165
|
+
|
|
166
|
+
custom/tests/ ← your custom tests go here (create this dir)
|
|
167
|
+
my-feature.test.ts
|
|
168
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Spine Developer Guides
|
|
2
|
+
|
|
3
|
+
Guides for building custom apps, functions, and components on Spine Core.
|
|
4
|
+
|
|
5
|
+
## Guides
|
|
6
|
+
|
|
7
|
+
| Guide | What it covers |
|
|
8
|
+
|-------|---------------|
|
|
9
|
+
| [01-create-an-app.md](./01-create-an-app.md) | Scaffold, configure, and register a new custom app |
|
|
10
|
+
| [02-create-a-function.md](./02-create-a-function.md) | Write a custom Netlify function using core utilities |
|
|
11
|
+
| [03-create-a-component.md](./03-create-a-component.md) | Build a React component inside a custom app |
|
|
12
|
+
| [04-webhook-handlers.md](./04-webhook-handlers.md) | Register a webhook handler without touching core |
|
|
13
|
+
| [05-testing.md](./05-testing.md) | Test custom code using the core test harness |
|
|
14
|
+
|
|
15
|
+
## Architecture in One Sentence
|
|
16
|
+
|
|
17
|
+
> **Core provides mechanism. Custom provides data.**
|
|
18
|
+
|
|
19
|
+
Core never imports from `custom/`. Custom code imports from core using the `@core` alias.
|
|
20
|
+
|
|
21
|
+
## Key Paths
|
|
22
|
+
|
|
23
|
+
| Path | Purpose |
|
|
24
|
+
|------|---------|
|
|
25
|
+
| `custom/apps/{slug}/` | Your app lives here |
|
|
26
|
+
| `custom/apps/{slug}/manifest.json` | App metadata, routes, roles |
|
|
27
|
+
| `custom/apps/{slug}/index.tsx` | React entry point |
|
|
28
|
+
| `custom/apps/{slug}/components/` | App-specific components |
|
|
29
|
+
| `custom/functions/` | Custom Netlify functions |
|
|
30
|
+
| `.framework/functions/_shared/` | Core utilities (import via `@core/_shared`) |
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# 1. Scaffold a new app (creates files + registers in DB)
|
|
36
|
+
npm run spine-framework create-app my-app -- --name "My App" --role member
|
|
37
|
+
|
|
38
|
+
# 2. Assemble and run
|
|
39
|
+
npm run assemble && netlify dev
|
|
40
|
+
```
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# Spine npm Packages Plan
|
|
2
|
+
|
|
3
|
+
**Created:** May 28, 2026
|
|
4
|
+
**Version target:** 0.x (breaking change freedom until stable)
|
|
5
|
+
**CLI standard:** All commands via `spine-framework` — no shorthand aliases
|
|
6
|
+
**Status:** Planning
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Goal
|
|
11
|
+
|
|
12
|
+
Publish Spine Core and its apps as independent npm packages that any developer or agentic IDE can install and run from scratch.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install spine-framework
|
|
16
|
+
spine-framework init # stand up DB schema + seed
|
|
17
|
+
spine-framework install-app spine-framework-cortex
|
|
18
|
+
spine-framework install-app spine-framework-portal
|
|
19
|
+
npm run assemble && netlify dev
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Package Model
|
|
25
|
+
|
|
26
|
+
### Three packages, one dependency direction
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
spine-framework ← core platform, no spine dependencies
|
|
30
|
+
spine-framework-cortex → peerDep: spine-framework
|
|
31
|
+
spine-framework-portal → peerDep: spine-framework
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
No cross-dependency between `spine-framework-cortex` and `spine-framework-portal`. Each is wholly self-sufficient.
|
|
35
|
+
|
|
36
|
+
### What each package contains
|
|
37
|
+
|
|
38
|
+
**spine-framework**
|
|
39
|
+
- Core DB schema (`000_foundation.sql`) and seed data (`001_seed.sql`)
|
|
40
|
+
- Core Netlify functions (auth, items, pipelines, accounts, people, webhooks, etc.)
|
|
41
|
+
- Core frontend shell (routing, auth context, AppShell, UI primitives, hooks)
|
|
42
|
+
- CLI (`spine-framework init`, `spine-framework create-app`, `spine-framework migrate`, etc.)
|
|
43
|
+
- Assembly scripts
|
|
44
|
+
- Public API surface (`_shared/index.ts`)
|
|
45
|
+
|
|
46
|
+
**spine-framework-cortex**
|
|
47
|
+
- Frontend: all Cortex pages, components, sidebar
|
|
48
|
+
- `manifest.json` — routes, roles, nav
|
|
49
|
+
- `seed/types.json` — type definitions Cortex needs upserted on install
|
|
50
|
+
- No DB migrations, no owned tables
|
|
51
|
+
|
|
52
|
+
**spine-framework-portal**
|
|
53
|
+
- Frontend: all Portal pages, components, sidebar
|
|
54
|
+
- `manifest.json` — routes, roles, nav
|
|
55
|
+
- `seed/types.json` — type definitions Portal needs upserted on install
|
|
56
|
+
- No DB migrations, no owned tables
|
|
57
|
+
|
|
58
|
+
### App data model principle
|
|
59
|
+
|
|
60
|
+
> Apps never touch DB structure. Apps upsert type records into core's `types` table using `design_schema` to define their data shape.
|
|
61
|
+
|
|
62
|
+
Cortex and Portal share the same core tables (`items`, `types`, `accounts`, `people`). They are independent — no imports between them.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Phases
|
|
67
|
+
|
|
68
|
+
### Phase 1: Merge + Production Validation
|
|
69
|
+
**Goal:** Ship current state to prod, verify nothing is broken before forking.
|
|
70
|
+
|
|
71
|
+
**Checklist:**
|
|
72
|
+
- [ ] Migrations 014 (webhook registry) and 015 (apps table simplify) applied in prod
|
|
73
|
+
- [ ] All apps load correctly (manifest-driven routing works)
|
|
74
|
+
- [ ] Webhook registry doesn't break existing integrations
|
|
75
|
+
- [ ] Boundary check passes (`npm run test:boundary`)
|
|
76
|
+
- [ ] Demo site functional
|
|
77
|
+
|
|
78
|
+
**Branch:** `main`
|
|
79
|
+
**Timeline:** Before demo (2 weeks)
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### Phase 2: Fork for npm Work
|
|
84
|
+
**Goal:** Protect the stable demo branch while doing structural npm work.
|
|
85
|
+
|
|
86
|
+
**Actions:**
|
|
87
|
+
- Create branch `v0-npm-packages` from `main` after Phase 1 merges
|
|
88
|
+
- All Phases 3–6 happen on `v0-npm-packages`
|
|
89
|
+
- `main` remains the demo-stable branch
|
|
90
|
+
- Merge back only when end-to-end install story is verified
|
|
91
|
+
|
|
92
|
+
**Branch:** `v0-npm-packages`
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### Phase 3: Migrations Audit + Foundation SQL
|
|
97
|
+
**Goal:** Single authoritative schema for a fresh install. The critical path item — nothing else in this plan works without it.
|
|
98
|
+
|
|
99
|
+
**Decisions to make before writing SQL:**
|
|
100
|
+
- [x] ~~Include `links` table in core?~~ **Resolved: yes.** `links` and `link_types` already exist in production with 21 rows. Goes in `000_foundation.sql`.
|
|
101
|
+
- [ ] Which types are core seed data vs app seed data? System roles yes. Default item types — draw the line.
|
|
102
|
+
- [ ] RLS policy audit — capture all policies explicitly (easily missed in schema dumps)
|
|
103
|
+
- [ ] ⚠️ `account_paths`, `test_runs`, `test_results` have RLS disabled — decide on policies before including in foundation SQL
|
|
104
|
+
|
|
105
|
+
**Deliverables:**
|
|
106
|
+
- [ ] Audit document: current live DB schema vs migration files — flag all drift
|
|
107
|
+
- [ ] `000_foundation.sql` — creates all core tables, indexes, constraints, RLS policies, functions, triggers in correct dependency order. **Confirmed tables (34 total):** `accounts`, `people`, `types`, `apps`, `roles`, `items`, `threads`, `messages`, `links`, `link_types`, `attachments`, `watchers`, `pipelines`, `pipeline_executions`, `triggers`, `trigger_executions`, `timers`, `ai_agents`, `embeddings`, `integrations`, `prompt_configs`, `api_keys`, `api_key_usage_logs`, `logs`, `actions`, `schedules`, `schedule_executions`, `account_paths`, `test_runs`, `test_results`, `item_progress`, `webhook_handlers`, `app_installations`, `embeddings`
|
|
108
|
+
- [ ] `001_seed.sql` — inserts: system roles, core link types (`account_signals`, `account_opportunities`), bootstrap data core needs to boot. **App-owned link types (`tagged_with`, `analyzed_by`) go in app seed, not here.**
|
|
109
|
+
- [ ] Old migrations moved to `archive/migrations/` with a `README` marking them historical
|
|
110
|
+
|
|
111
|
+
**Verification:**
|
|
112
|
+
```bash
|
|
113
|
+
# Must pass: blank Supabase project + run 000 + 001 = working app
|
|
114
|
+
spine-framework init # runs 000_foundation.sql then 001_seed.sql
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### Phase 4: Separate Core, Cortex, Portal
|
|
120
|
+
**Goal:** Each is an independently packageable unit with no cross-contamination.
|
|
121
|
+
|
|
122
|
+
**Core separation:**
|
|
123
|
+
- [ ] Verify `.framework/` has zero imports from `custom/`
|
|
124
|
+
- [ ] Verify `.framework/` has zero Cortex/Portal-specific references
|
|
125
|
+
- [ ] Boundary check extended to catch Cortex↔Portal cross-imports
|
|
126
|
+
|
|
127
|
+
**Cortex separation:**
|
|
128
|
+
- [ ] All Cortex frontend confirmed in `custom/apps/cortex/`
|
|
129
|
+
- [ ] Extract Cortex type definitions from live DB → `custom/apps/cortex/seed/types.json`
|
|
130
|
+
- [ ] Extract Cortex link types (`tagged_with`, `analyzed_by`) → `custom/apps/cortex/seed/link-types.json`
|
|
131
|
+
- [x] ~~Verify no Portal imports in Cortex~~ **Confirmed clean** (validated May 28, 2026)
|
|
132
|
+
- [x] ~~Verify Cortex imports only from `@core` and its own files~~ **Confirmed clean**
|
|
133
|
+
|
|
134
|
+
**Portal separation:**
|
|
135
|
+
- [ ] All Portal frontend confirmed in `custom/apps/customer-portal/`
|
|
136
|
+
- [ ] Extract Portal type definitions from live DB → `custom/apps/customer-portal/seed/types.json`
|
|
137
|
+
- [ ] Extract any Portal-owned link types → `custom/apps/customer-portal/seed/link-types.json`
|
|
138
|
+
- [x] ~~Verify no Cortex imports in Portal~~ **Confirmed clean** (validated May 28, 2026)
|
|
139
|
+
- [x] ~~Verify Portal imports only from `@core` and its own files~~ **Confirmed clean**
|
|
140
|
+
|
|
141
|
+
**End state directory map:**
|
|
142
|
+
```
|
|
143
|
+
spine-framework/ ← becomes the spine-framework npm package
|
|
144
|
+
.framework/
|
|
145
|
+
functions/
|
|
146
|
+
src/
|
|
147
|
+
migrations/
|
|
148
|
+
000_foundation.sql
|
|
149
|
+
001_seed.sql
|
|
150
|
+
cli/
|
|
151
|
+
|
|
152
|
+
spine-framework-cortex/ ← becomes the spine-framework-cortex npm package
|
|
153
|
+
manifest.json
|
|
154
|
+
index.tsx
|
|
155
|
+
components/
|
|
156
|
+
pages/
|
|
157
|
+
seed/
|
|
158
|
+
types.json
|
|
159
|
+
|
|
160
|
+
spine-framework-portal/ ← becomes the spine-framework-portal npm package
|
|
161
|
+
manifest.json
|
|
162
|
+
index.tsx
|
|
163
|
+
components/
|
|
164
|
+
pages/
|
|
165
|
+
seed/
|
|
166
|
+
types.json
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### Phase 5: npm Packages
|
|
172
|
+
**Goal:** Three publishable packages with working install flows.
|
|
173
|
+
|
|
174
|
+
**spine-framework package.json additions:**
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"name": "spine-framework",
|
|
178
|
+
"version": "0.1.0",
|
|
179
|
+
"private": false,
|
|
180
|
+
"bin": {
|
|
181
|
+
"spine-framework": "./dist/cli/index.js"
|
|
182
|
+
},
|
|
183
|
+
"exports": {
|
|
184
|
+
"./_shared": "./dist/functions/_shared/index.js",
|
|
185
|
+
"./src/*": "./dist/src/*"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**spine-framework-cortex / spine-framework-portal package.json:**
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"name": "spine-framework-cortex",
|
|
194
|
+
"version": "0.1.0",
|
|
195
|
+
"private": false,
|
|
196
|
+
"peerDependencies": {
|
|
197
|
+
"spine-framework": ">=0.1.0"
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**CLI commands to build/verify:**
|
|
203
|
+
|
|
204
|
+
| Command | Description |
|
|
205
|
+
|---------|-------------|
|
|
206
|
+
| `spine-framework init` | Run 000 + 001 migrations against configured Supabase project |
|
|
207
|
+
| `spine-framework migrate` | Run pending migrations |
|
|
208
|
+
| `spine-framework install-app spine-framework-cortex` | Copy app files + upsert type seed records |
|
|
209
|
+
| `spine-framework uninstall-app spine-framework-cortex` | Remove app record + type seed records |
|
|
210
|
+
| `spine-framework create-app <slug>` | Scaffold a new custom app |
|
|
211
|
+
| `spine-framework status` | Show installed apps and migration state |
|
|
212
|
+
|
|
213
|
+
**Build pipeline:**
|
|
214
|
+
- [ ] Add `tsc` build step for functions and CLI → `dist/`
|
|
215
|
+
- [ ] Add Vite library mode build for frontend components → `dist/src/`
|
|
216
|
+
- [ ] Generate `.d.ts` type declarations
|
|
217
|
+
- [ ] `prepublishOnly` script runs build + boundary check + tests
|
|
218
|
+
|
|
219
|
+
**Registry:** GitHub Packages (private) first. Public npm only after end-to-end install verified.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
### Phase 6: End-to-End Install Verification
|
|
224
|
+
**Goal:** Prove the install story works from a completely blank starting point. Must pass before any publish.
|
|
225
|
+
|
|
226
|
+
**Fresh install checklist:**
|
|
227
|
+
- [ ] Blank Supabase project (no existing schema)
|
|
228
|
+
- [ ] `npm install spine-framework`
|
|
229
|
+
- [ ] `spine-framework init` → DB schema created, seed data present, app boots
|
|
230
|
+
- [ ] Admin panel functional
|
|
231
|
+
- [ ] `spine-framework install-app spine-framework-cortex` → Cortex loads, type records seeded
|
|
232
|
+
- [ ] `spine-framework install-app spine-framework-portal` → Portal loads, type records seeded
|
|
233
|
+
- [ ] Both apps functional and independent
|
|
234
|
+
- [ ] `spine-framework create-app my-test` → scaffolds correctly
|
|
235
|
+
- [ ] `npm run assemble && netlify dev` → dev server runs cleanly
|
|
236
|
+
|
|
237
|
+
**This checklist is the publish gate. All items must pass.**
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### Phase 7: Documentation
|
|
242
|
+
**Goal:** A developer or agentic IDE can go from zero to running in one session.
|
|
243
|
+
|
|
244
|
+
**Docs needed:**
|
|
245
|
+
|
|
246
|
+
| Document | Audience |
|
|
247
|
+
|----------|---------|
|
|
248
|
+
| Getting Started (from zero) | New developer / agentic IDE |
|
|
249
|
+
| `spine-framework init` walkthrough | Developer |
|
|
250
|
+
| Installing apps | Developer |
|
|
251
|
+
| Building a custom app | Developer (dev-guides already exist) |
|
|
252
|
+
| Core API reference | Developer (API.md exists, extend) |
|
|
253
|
+
| Upgrade guide | Existing Spine developer |
|
|
254
|
+
| Contributing to core | Core contributor |
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Decisions Log
|
|
259
|
+
|
|
260
|
+
| Decision | Choice | Rationale |
|
|
261
|
+
|----------|--------|-----------|
|
|
262
|
+
| Version target | `0.x` | Breaking change freedom until stable |
|
|
263
|
+
| CLI namespace | `spine-framework` only | No shorthand aliases |
|
|
264
|
+
| App DB model | Type records only, no app-owned tables | Clean install, clean uninstall, no schema drift |
|
|
265
|
+
| Cortex ↔ Portal | Fully independent, no cross-imports | Each is self-sufficient |
|
|
266
|
+
| Shared type records | Both apps use same core tables | Core owns schema, apps own their type definitions |
|
|
267
|
+
| Registry | GitHub Packages (private) → npm (public) | Validate before exposing |
|
|
268
|
+
| Links table | **In core** — `links` + `link_types` in `000_foundation.sql` | Already live in production with 21 rows. `app_id` column on `link_types` distinguishes core vs app-owned relationship types. |
|
|
269
|
+
| App-owned link types | Seeded by app (`seed/link-types.json`), not by core | `tagged_with`, `analyzed_by` are Cortex-owned; `account_signals`, `account_opportunities` are core-owned |
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Open Questions
|
|
274
|
+
|
|
275
|
+
1. **Supabase project config** — How does `spine-framework init` know which Supabase project to target? `.env` file, interactive prompt, or CLI flag?
|
|
276
|
+
2. **Assembly in package model** — Does `assemble.sh` read from `node_modules/spine-framework-cortex/` after install, or does `install-app` copy files into `custom/apps/`? Copy is simpler and matches current model.
|
|
277
|
+
3. **Frontend build** — Does `spine-framework` ship pre-built frontend components (library mode) or source that gets assembled into the host project? Source + assemble matches current model and avoids Vite/React version conflicts.
|
|
278
|
+
4. **RLS on `account_paths`, `test_runs`, `test_results`** — These 3 tables have RLS disabled in production. Decide on policies before writing `000_foundation.sql`. Without policies, enabling RLS will block all access.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Related Documents
|
|
283
|
+
|
|
284
|
+
- `docs/enterprise-framework-implementation-summary.md` — current state of enterprise framework work
|
|
285
|
+
- `docs/dev-guides/` — developer guides for building on Spine (Phases 3–7 prerequisite)
|
|
286
|
+
- `.framework/API.md` — public API surface
|
|
287
|
+
- `.framework/migrations/` — current migration files (to be audited in Phase 3)
|
|
288
|
+
- `scripts/boundary-check.sh` — architectural boundary enforcement
|