@elevasis/core 0.10.0 → 0.11.1
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/dist/index.d.ts +69 -159
- package/dist/index.js +324 -613
- package/dist/organization-model/index.d.ts +69 -159
- package/dist/organization-model/index.js +324 -613
- package/dist/test-utils/index.d.ts +192 -45
- package/dist/test-utils/index.js +260 -600
- package/package.json +1 -1
- package/src/__tests__/template-core-compatibility.test.ts +73 -91
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +94 -182
- package/src/auth/multi-tenancy/index.ts +20 -17
- package/src/auth/multi-tenancy/memberships/api-schemas.ts +142 -126
- package/src/auth/multi-tenancy/memberships/index.ts +26 -22
- package/src/auth/multi-tenancy/permissions.test.ts +42 -0
- package/src/auth/multi-tenancy/permissions.ts +104 -0
- package/src/organization-model/README.md +102 -97
- package/src/organization-model/__tests__/defaults.test.ts +19 -6
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +24 -93
- package/src/organization-model/__tests__/graph.test.ts +82 -894
- package/src/organization-model/__tests__/resolve.test.ts +59 -690
- package/src/organization-model/__tests__/schema.test.ts +83 -407
- package/src/organization-model/contracts.ts +4 -3
- package/src/organization-model/defaults.ts +277 -141
- package/src/organization-model/domains/features.ts +31 -22
- package/src/organization-model/domains/navigation.ts +27 -20
- package/src/organization-model/foundation.ts +42 -54
- package/src/organization-model/graph/build.ts +42 -217
- package/src/organization-model/graph/index.ts +4 -4
- package/src/organization-model/graph/link.ts +10 -0
- package/src/organization-model/graph/schema.ts +21 -16
- package/src/organization-model/graph/types.ts +10 -10
- package/src/organization-model/helpers.ts +74 -0
- package/src/organization-model/index.ts +7 -7
- package/src/organization-model/organization-graph.mdx +89 -272
- package/src/organization-model/organization-model.mdx +152 -320
- package/src/organization-model/published.ts +20 -19
- package/src/organization-model/resolve.ts +8 -33
- package/src/organization-model/schema.ts +63 -205
- package/src/organization-model/types.ts +12 -11
- package/src/platform/constants/versions.ts +3 -3
- package/src/platform/registry/__tests__/command-view.test.ts +6 -5
- package/src/platform/registry/__tests__/resource-link.test.ts +30 -0
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +15 -15
- package/src/platform/registry/command-view.ts +10 -12
- package/src/platform/registry/index.ts +93 -93
- package/src/platform/registry/resource-link.ts +32 -0
- package/src/platform/registry/resource-registry.ts +917 -876
- package/src/platform/registry/serialization.ts +56 -73
- package/src/platform/registry/serialized-types.ts +17 -12
- package/src/platform/registry/types.ts +14 -43
- package/src/reference/_generated/contracts.md +94 -182
- package/src/reference/glossary.md +71 -105
- package/src/scaffold-registry/__tests__/index.test.ts +125 -1
- package/src/scaffold-registry/__tests__/schema.test.ts +48 -20
- package/src/scaffold-registry/index.ts +236 -188
- package/src/scaffold-registry/schema.ts +47 -22
- package/src/supabase/database.types.ts +2880 -2719
- package/src/test-utils/fixtures/memberships.ts +82 -80
- package/src/platform/registry/domains.ts +0 -165
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { loadScaffoldRegistryFast } from '../index'
|
|
2
|
+
import { findMatchingEntries, findMissingDependentPaths, loadScaffoldRegistryFast } from '../index'
|
|
3
|
+
import type { ScaffoldRegistry, ScaffoldRegistryEntry } from '../schema'
|
|
4
|
+
|
|
5
|
+
function makeEntry(id: string, sources: string[]): ScaffoldRegistryEntry {
|
|
6
|
+
return {
|
|
7
|
+
id,
|
|
8
|
+
kind: 'manual-scaffold',
|
|
9
|
+
owner: 'packages/core',
|
|
10
|
+
sources,
|
|
11
|
+
dependents: [{ path: 'packages/core/package.json', regen: 'manual', hint: 'h' }]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function registryFromEntries(entries: ScaffoldRegistryEntry[]): ScaffoldRegistry {
|
|
16
|
+
return { version: '1', entries }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function matchedIds(registry: ScaffoldRegistry, filePath: string): string[] {
|
|
20
|
+
return findMatchingEntries(registry, filePath).map((e) => e.id)
|
|
21
|
+
}
|
|
3
22
|
|
|
4
23
|
describe('loadScaffoldRegistryFast', () => {
|
|
5
24
|
it('preserves external sync metadata from the compiled registry', () => {
|
|
@@ -15,3 +34,108 @@ describe('loadScaffoldRegistryFast', () => {
|
|
|
15
34
|
})
|
|
16
35
|
})
|
|
17
36
|
})
|
|
37
|
+
|
|
38
|
+
describe('findMissingDependentPaths', () => {
|
|
39
|
+
const baseEntry = {
|
|
40
|
+
id: 'fake-entry',
|
|
41
|
+
kind: 'manual-scaffold' as const,
|
|
42
|
+
owner: 'packages/core',
|
|
43
|
+
sources: ['packages/core/src/**'],
|
|
44
|
+
dependents: [{ path: 'packages/core/this-file-does-not-exist.ts', regen: 'manual', hint: 'h' }]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
it('flags dependents whose paths are missing on disk', () => {
|
|
48
|
+
const registry: ScaffoldRegistry = { version: '1', entries: [baseEntry] }
|
|
49
|
+
const missing = findMissingDependentPaths(registry, process.cwd())
|
|
50
|
+
expect(missing).toEqual([{ entryId: 'fake-entry', path: 'packages/core/this-file-does-not-exist.ts' }])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('skips symbolic targets, globs, and (self)', () => {
|
|
54
|
+
const registry: ScaffoldRegistry = {
|
|
55
|
+
version: '1',
|
|
56
|
+
entries: [
|
|
57
|
+
{
|
|
58
|
+
...baseEntry,
|
|
59
|
+
dependents: [
|
|
60
|
+
{ path: 'docs: sync-preservation-matrix', regen: 'manual', hint: 'h' },
|
|
61
|
+
{ path: 'autogen-target:foo', regen: 'manual', hint: 'h' },
|
|
62
|
+
{ path: 'packages/core/src/**/*.ts', regen: 'manual', hint: 'h' },
|
|
63
|
+
{ path: '(self)', regen: 'manual', hint: 'h' }
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
expect(findMissingDependentPaths(registry, process.cwd())).toEqual([])
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('skips sync-preservation entries (their paths target derived external projects)', () => {
|
|
72
|
+
const registry: ScaffoldRegistry = {
|
|
73
|
+
version: '1',
|
|
74
|
+
entries: [
|
|
75
|
+
{
|
|
76
|
+
id: 'sync-entry',
|
|
77
|
+
kind: 'sync-preservation',
|
|
78
|
+
owner: '@elevasis/core',
|
|
79
|
+
category: 'merge',
|
|
80
|
+
strategy: 'merge-regions',
|
|
81
|
+
delete_policy: 'none',
|
|
82
|
+
sources: ['external/_template/foo.ts'],
|
|
83
|
+
dependents: [{ path: 'core/src/foo-not-in-monorepo.ts', regen: 'manual', hint: 'h' }]
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
expect(findMissingDependentPaths(registry, process.cwd())).toEqual([])
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
describe('findMatchingEntries (pattern matching)', () => {
|
|
92
|
+
it('matches exact paths', () => {
|
|
93
|
+
const reg = registryFromEntries([makeEntry('exact', ['packages/core/src/index.ts'])])
|
|
94
|
+
expect(matchedIds(reg, 'packages/core/src/index.ts')).toEqual(['exact'])
|
|
95
|
+
expect(matchedIds(reg, 'packages/core/src/other.ts')).toEqual([])
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('matches /** suffix as deep prefix', () => {
|
|
99
|
+
const reg = registryFromEntries([makeEntry('deep', ['packages/core/src/**'])])
|
|
100
|
+
expect(matchedIds(reg, 'packages/core/src/foo.ts')).toEqual(['deep'])
|
|
101
|
+
expect(matchedIds(reg, 'packages/core/src/nested/deeply/foo.ts')).toEqual(['deep'])
|
|
102
|
+
expect(matchedIds(reg, 'packages/core/package.json')).toEqual([])
|
|
103
|
+
expect(matchedIds(reg, 'packages/core/src')).toEqual([])
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('matches /* suffix as single-level prefix', () => {
|
|
107
|
+
const reg = registryFromEntries([makeEntry('shallow', ['packages/*/package.json'])])
|
|
108
|
+
expect(matchedIds(reg, 'packages/core/package.json')).toEqual(['shallow'])
|
|
109
|
+
expect(matchedIds(reg, 'packages/ui/package.json')).toEqual(['shallow'])
|
|
110
|
+
expect(matchedIds(reg, 'packages/core/nested/package.json')).toEqual([])
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('current matcher does NOT support mixed **/*.ext patterns (Step 3 upgrade)', () => {
|
|
114
|
+
// The simple matcher's `/*` suffix branch only handles a single trailing
|
|
115
|
+
// wildcard. Patterns like `**/*.ts` fall through and effectively miss real
|
|
116
|
+
// files. Locked in to flag any change when full micromatch lands.
|
|
117
|
+
const reg = registryFromEntries([makeEntry('ext', ['apps/api/src/**/*.ts'])])
|
|
118
|
+
expect(matchedIds(reg, 'apps/api/src/foo.ts')).toEqual([])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('normalizes Windows backslashes to forward slashes', () => {
|
|
122
|
+
const reg = registryFromEntries([makeEntry('winpath', ['packages/core/src/**'])])
|
|
123
|
+
expect(matchedIds(reg, 'packages\\core\\src\\foo.ts')).toEqual(['winpath'])
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('matches a directory-prefix pattern (no glob characters)', () => {
|
|
127
|
+
const reg = registryFromEntries([makeEntry('dir', ['packages/core/src'])])
|
|
128
|
+
expect(matchedIds(reg, 'packages/core/src/foo.ts')).toEqual(['dir'])
|
|
129
|
+
expect(matchedIds(reg, 'packages/core/srcfoo.ts')).toEqual([])
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('returns multiple entries when several match the same file', () => {
|
|
133
|
+
const reg = registryFromEntries([makeEntry('a', ['packages/core/src/**']), makeEntry('b', ['packages/core/src'])])
|
|
134
|
+
expect(matchedIds(reg, 'packages/core/src/foo.ts').sort()).toEqual(['a', 'b'])
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('returns empty when no entries match', () => {
|
|
138
|
+
const reg = registryFromEntries([makeEntry('a', ['packages/core/src/**'])])
|
|
139
|
+
expect(matchedIds(reg, 'apps/api/src/foo.ts')).toEqual([])
|
|
140
|
+
})
|
|
141
|
+
})
|
|
@@ -18,12 +18,9 @@ describe('ScaffoldEntryKindSchema', () => {
|
|
|
18
18
|
const validKinds = [
|
|
19
19
|
'autogen',
|
|
20
20
|
'manual-scaffold',
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'sdk-cli-generator',
|
|
25
|
-
'validator',
|
|
26
|
-
'other'
|
|
21
|
+
'sync-preservation',
|
|
22
|
+
'validator',
|
|
23
|
+
'other'
|
|
27
24
|
]
|
|
28
25
|
for (const kind of validKinds) {
|
|
29
26
|
expect(ScaffoldEntryKindSchema.safeParse(kind).success).toBe(true)
|
|
@@ -91,9 +88,8 @@ const validEntry = {
|
|
|
91
88
|
dependents: [
|
|
92
89
|
{
|
|
93
90
|
path: '.navigation/_generated/skeleton.md',
|
|
94
|
-
regen: 'pnpm navigation:generate',
|
|
95
|
-
|
|
96
|
-
hint: 'Regenerate after rename.'
|
|
91
|
+
regen: 'pnpm navigation:generate',
|
|
92
|
+
hint: 'Regenerate after rename.'
|
|
97
93
|
}
|
|
98
94
|
]
|
|
99
95
|
}
|
|
@@ -176,10 +172,24 @@ describe('ScaffoldRegistryEntrySchema — valid entry', () => {
|
|
|
176
172
|
expect(ScaffoldRegistryEntrySchema.safeParse(withBadAutoRegen).success).toBe(false)
|
|
177
173
|
})
|
|
178
174
|
|
|
179
|
-
it('parses all
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
175
|
+
it('parses all seed-entry kinds with required invariants', () => {
|
|
176
|
+
const entries = [
|
|
177
|
+
{ ...validEntry, kind: 'autogen', regen: 'pnpm navigation:generate' },
|
|
178
|
+
{ ...validEntry, kind: 'manual-scaffold' },
|
|
179
|
+
{
|
|
180
|
+
...validEntry,
|
|
181
|
+
kind: 'sync-preservation',
|
|
182
|
+
owner: 'template',
|
|
183
|
+
category: 'replace',
|
|
184
|
+
strategy: 'replace-all',
|
|
185
|
+
delete_policy: 'none'
|
|
186
|
+
},
|
|
187
|
+
{ ...validEntry, kind: 'validator', dependents: [{ path: '(self)', regen: 'pnpm meta:verify' }] },
|
|
188
|
+
{ ...validEntry, kind: 'other', notes: 'Vendor generated.' }
|
|
189
|
+
]
|
|
190
|
+
for (const entry of entries) {
|
|
191
|
+
const result = ScaffoldRegistryEntrySchema.safeParse(entry)
|
|
192
|
+
const kind = entry.kind
|
|
183
193
|
expect(result.success, `kind ${kind} should parse`).toBe(true)
|
|
184
194
|
}
|
|
185
195
|
})
|
|
@@ -201,6 +211,21 @@ describe('ScaffoldRegistryEntrySchema — valid entry', () => {
|
|
|
201
211
|
}
|
|
202
212
|
})
|
|
203
213
|
|
|
214
|
+
it('rejects autogen entries without top-level regen', () => {
|
|
215
|
+
const result = ScaffoldRegistryEntrySchema.safeParse({ ...validEntry, kind: 'autogen' })
|
|
216
|
+
expect(result.success).toBe(false)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('rejects other entries without notes', () => {
|
|
220
|
+
const result = ScaffoldRegistryEntrySchema.safeParse({ ...validEntry, kind: 'other' })
|
|
221
|
+
expect(result.success).toBe(false)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('rejects sync-preservation entries without external sync metadata', () => {
|
|
225
|
+
const result = ScaffoldRegistryEntrySchema.safeParse({ ...validEntry, kind: 'sync-preservation' })
|
|
226
|
+
expect(result.success).toBe(false)
|
|
227
|
+
})
|
|
228
|
+
|
|
204
229
|
it('keeps legacy free-form owner values valid when external sync fields are omitted', () => {
|
|
205
230
|
const result = ScaffoldRegistryEntrySchema.safeParse({
|
|
206
231
|
...validEntry,
|
|
@@ -361,13 +386,16 @@ describe('ScaffoldRegistrySchema', () => {
|
|
|
361
386
|
sources: ['apps/docs/content/docs/**/meta.json'],
|
|
362
387
|
dependents: [{ path: '(self)', regen: 'pnpm check-docs-meta' }]
|
|
363
388
|
},
|
|
364
|
-
{
|
|
365
|
-
id: 'external-template-hook-added',
|
|
366
|
-
kind: 'sync-preservation',
|
|
367
|
-
owner: '
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
389
|
+
{
|
|
390
|
+
id: 'external-template-hook-added',
|
|
391
|
+
kind: 'sync-preservation',
|
|
392
|
+
owner: 'template',
|
|
393
|
+
category: 'replace',
|
|
394
|
+
strategy: 'replace-all',
|
|
395
|
+
delete_policy: 'none',
|
|
396
|
+
sources: ['external/_template/.claude/hooks/*.mjs'],
|
|
397
|
+
dependents: [{ path: 'external/_template/.claude/settings.json', regen: 'manual' }]
|
|
398
|
+
}
|
|
371
399
|
]
|
|
372
400
|
}
|
|
373
401
|
const result = ScaffoldRegistrySchema.safeParse(multiEntry)
|