polen 0.11.0-next.16 → 0.11.0-next.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/build/api/builder/ssg/generate.d.ts.map +1 -1
- package/build/api/builder/ssg/generate.js +5 -5
- package/build/api/builder/ssg/generate.js.map +1 -1
- package/build/api/builder/ssg/page-generator.worker.js +13 -3
- package/build/api/builder/ssg/page-generator.worker.js.map +1 -1
- package/build/api/config/input.d.ts +88 -3
- package/build/api/config/input.d.ts.map +1 -1
- package/build/api/config/normalized.d.ts +92 -7
- package/build/api/config/normalized.d.ts.map +1 -1
- package/build/api/config/normalized.js +11 -3
- package/build/api/config/normalized.js.map +1 -1
- package/build/api/config-template/template.js +2 -2
- package/build/api/config-template/template.js.map +1 -1
- package/build/api/content/sidebar.d.ts.map +1 -1
- package/build/api/content/sidebar.js +2 -1
- package/build/api/content/sidebar.js.map +1 -1
- package/build/api/examples/config.d.ts +366 -3
- package/build/api/examples/config.d.ts.map +1 -1
- package/build/api/examples/config.js +25 -3
- package/build/api/examples/config.js.map +1 -1
- package/build/api/examples/diagnostic/diagnostic.d.ts +5 -5
- package/build/api/examples/diagnostic/missing-versions.d.ts +5 -4
- package/build/api/examples/diagnostic/missing-versions.d.ts.map +1 -1
- package/build/api/examples/diagnostic/missing-versions.js +3 -2
- package/build/api/examples/diagnostic/missing-versions.js.map +1 -1
- package/build/api/examples/diagnostic/unknown-version.d.ts +5 -4
- package/build/api/examples/diagnostic/unknown-version.d.ts.map +1 -1
- package/build/api/examples/diagnostic/unknown-version.js +3 -2
- package/build/api/examples/diagnostic/unknown-version.js.map +1 -1
- package/build/api/examples/diagnostic/validation-error.d.ts +3 -2
- package/build/api/examples/diagnostic/validation-error.d.ts.map +1 -1
- package/build/api/examples/diagnostic/validation-error.js +9 -3
- package/build/api/examples/diagnostic/validation-error.js.map +1 -1
- package/build/api/examples/diagnostic/validator.d.ts.map +1 -1
- package/build/api/examples/diagnostic/validator.js +115 -69
- package/build/api/examples/diagnostic/validator.js.map +1 -1
- package/build/api/examples/filter.d.ts.map +1 -1
- package/build/api/examples/filter.js +9 -6
- package/build/api/examples/filter.js.map +1 -1
- package/build/api/examples/scanner.d.ts.map +1 -1
- package/build/api/examples/scanner.js +93 -103
- package/build/api/examples/scanner.js.map +1 -1
- package/build/api/examples/type-usage-indexer.d.ts.map +1 -1
- package/build/api/examples/type-usage-indexer.js +18 -32
- package/build/api/examples/type-usage-indexer.js.map +1 -1
- package/build/api/iso/schema/routing.d.ts.map +1 -1
- package/build/api/iso/schema/routing.js +8 -8
- package/build/api/iso/schema/routing.js.map +1 -1
- package/build/api/iso/schema/validation.d.ts.map +1 -1
- package/build/api/iso/schema/validation.js +3 -2
- package/build/api/iso/schema/validation.js.map +1 -1
- package/build/api/schema/input-sources/directory.js +2 -2
- package/build/api/schema/input-sources/directory.js.map +1 -1
- package/build/api/schema/input-sources/versioned-directory.d.ts +3 -3
- package/build/api/schema/input-sources/versioned-directory.d.ts.map +1 -1
- package/build/api/schema/input-sources/versioned-directory.js +6 -4
- package/build/api/schema/input-sources/versioned-directory.js.map +1 -1
- package/build/api/schema/load.d.ts.map +1 -1
- package/build/api/schema/load.js +2 -2
- package/build/api/schema/load.js.map +1 -1
- package/build/cli/commands/hero-image.js +1 -1
- package/build/cli/commands/hero-image.js.map +1 -1
- package/build/lib/catalog/catalog.d.ts +65 -24
- package/build/lib/catalog/catalog.d.ts.map +1 -1
- package/build/lib/catalog/catalog.js +72 -16
- package/build/lib/catalog/catalog.js.map +1 -1
- package/build/lib/catalog/versioned.d.ts +46 -26
- package/build/lib/catalog/versioned.d.ts.map +1 -1
- package/build/lib/catalog/versioned.js +44 -7
- package/build/lib/catalog/versioned.js.map +1 -1
- package/build/lib/catalog-statistics/analyze-catalog.js +3 -3
- package/build/lib/catalog-statistics/analyze-catalog.js.map +1 -1
- package/build/lib/document/document.d.ts +55 -5
- package/build/lib/document/document.d.ts.map +1 -1
- package/build/lib/document/document.js +96 -2
- package/build/lib/document/document.js.map +1 -1
- package/build/lib/document/versioned.d.ts +2 -2
- package/build/lib/document/versioned.d.ts.map +1 -1
- package/build/lib/document/versioned.js +7 -7
- package/build/lib/document/versioned.js.map +1 -1
- package/build/lib/lifecycles/lifecycles.d.ts +5 -4
- package/build/lib/lifecycles/lifecycles.d.ts.map +1 -1
- package/build/lib/lifecycles/lifecycles.js +15 -13
- package/build/lib/lifecycles/lifecycles.js.map +1 -1
- package/build/lib/version-coverage/$$.d.ts +2 -0
- package/build/lib/version-coverage/$$.d.ts.map +1 -0
- package/build/lib/version-coverage/$$.js +2 -0
- package/build/lib/version-coverage/$$.js.map +1 -0
- package/build/lib/version-coverage/$.d.ts.map +1 -0
- package/build/lib/version-coverage/$.js.map +1 -0
- package/build/lib/{version-selection/version-selection.d.ts → version-coverage/version-coverage.d.ts} +1 -1
- package/build/lib/version-coverage/version-coverage.d.ts.map +1 -0
- package/build/lib/{version-selection/version-selection.js → version-coverage/version-coverage.js} +2 -2
- package/build/lib/version-coverage/version-coverage.js.map +1 -0
- package/build/template/components/Changelog/Changelog.d.ts.map +1 -1
- package/build/template/components/Changelog/Changelog.js +7 -7
- package/build/template/components/Changelog/Changelog.js.map +1 -1
- package/build/template/components/GraphQLDocument.d.ts +1 -1
- package/build/template/components/GraphQLDocument.d.ts.map +1 -1
- package/build/template/components/GraphQLDocument.js +10 -38
- package/build/template/components/GraphQLDocument.js.map +1 -1
- package/build/template/components/GraphQLInteractive/lib/parser.d.ts +28 -0
- package/build/template/components/GraphQLInteractive/lib/parser.d.ts.map +1 -1
- package/build/template/components/GraphQLInteractive/lib/parser.js +60 -27
- package/build/template/components/GraphQLInteractive/lib/parser.js.map +1 -1
- package/build/template/components/VersionCoveragePicker.d.ts +1 -1
- package/build/template/components/VersionCoveragePicker.d.ts.map +1 -1
- package/build/template/components/VersionCoveragePicker.js +4 -6
- package/build/template/components/VersionCoveragePicker.js.map +1 -1
- package/build/template/components/VersionPicker.d.ts.map +1 -1
- package/build/template/components/VersionPicker.js +5 -2
- package/build/template/components/VersionPicker.js.map +1 -1
- package/build/template/components/home/FeaturesGrid.js +1 -1
- package/build/template/components/home/FeaturesGrid.js.map +1 -1
- package/build/template/components/home/HeroSection.js +1 -1
- package/build/template/components/home/HeroSection.js.map +1 -1
- package/build/template/components/home/QuickStart.d.ts.map +1 -1
- package/build/template/components/home/QuickStart.js +8 -4
- package/build/template/components/home/QuickStart.js.map +1 -1
- package/build/template/components/home/RecentChanges.d.ts.map +1 -1
- package/build/template/components/home/RecentChanges.js +2 -1
- package/build/template/components/home/RecentChanges.js.map +1 -1
- package/build/template/hooks/use-highlighted.d.ts.map +1 -1
- package/build/template/hooks/use-highlighted.js +19 -13
- package/build/template/hooks/use-highlighted.js.map +1 -1
- package/build/template/lib/fetch-text.d.ts +18 -0
- package/build/template/lib/fetch-text.d.ts.map +1 -1
- package/build/template/lib/fetch-text.js +32 -4
- package/build/template/lib/fetch-text.js.map +1 -1
- package/build/template/routes/changelog.d.ts +1 -1
- package/build/template/routes/changelog.d.ts.map +1 -1
- package/build/template/routes/changelog.js +7 -4
- package/build/template/routes/changelog.js.map +1 -1
- package/build/template/routes/examples/_index.js +1 -1
- package/build/template/routes/examples/_index.js.map +1 -1
- package/build/template/routes/examples/name.d.ts.map +1 -1
- package/build/template/routes/examples/name.js +4 -2
- package/build/template/routes/examples/name.js.map +1 -1
- package/build/template/routes/reference.js +6 -6
- package/build/template/routes/reference.js.map +1 -1
- package/build/template/stores/toast.d.ts.map +1 -1
- package/build/template/stores/toast.js +5 -3
- package/build/template/stores/toast.js.map +1 -1
- package/build/vite/plugins/navbar.js +1 -1
- package/build/vite/plugins/navbar.js.map +1 -1
- package/build/vite/plugins/routes-manifest.js +1 -1
- package/build/vite/plugins/routes-manifest.js.map +1 -1
- package/package.json +7 -7
- package/src/api/builder/ssg/generate.ts +10 -5
- package/src/api/builder/ssg/page-generator.worker.ts +18 -3
- package/src/api/config/normalized.ts +12 -3
- package/src/api/config-template/template.ts +2 -2
- package/src/api/content/sidebar.ts +3 -3
- package/src/api/examples/config.test.ts +10 -0
- package/src/api/examples/config.ts +33 -4
- package/src/api/examples/diagnostic/missing-versions.ts +3 -2
- package/src/api/examples/diagnostic/unknown-version.ts +3 -2
- package/src/api/examples/diagnostic/validation-error.ts +9 -3
- package/src/api/examples/diagnostic/validator.test.ts +100 -55
- package/src/api/examples/diagnostic/validator.ts +148 -105
- package/src/api/examples/filter.ts +9 -6
- package/src/api/examples/scanner.ts +144 -120
- package/src/api/examples/type-usage-indexer.test.ts +44 -33
- package/src/api/examples/type-usage-indexer.ts +25 -40
- package/src/api/iso/schema/routing.ts +10 -10
- package/src/api/iso/schema/validation.ts +3 -2
- package/src/api/schema/$.test.ts +2 -2
- package/src/api/schema/input-sources/directory.ts +2 -2
- package/src/api/schema/input-sources/versioned-directory.ts +11 -8
- package/src/api/schema/load.ts +2 -2
- package/src/cli/commands/hero-image.ts +1 -1
- package/src/lib/catalog/catalog.ts +93 -16
- package/src/lib/catalog/versioned.ts +57 -7
- package/src/lib/catalog-statistics/$.test.ts +22 -12
- package/src/lib/catalog-statistics/analyze-catalog.ts +3 -3
- package/src/lib/document/document.ts +135 -2
- package/src/lib/document/versioned.ts +8 -8
- package/src/lib/lifecycles/lifecycles.ts +33 -28
- package/src/lib/version-coverage/$$.ts +1 -0
- package/src/lib/{version-selection/version-selection.ts → version-coverage/version-coverage.ts} +1 -1
- package/src/template/components/Changelog/Changelog.tsx +10 -6
- package/src/template/components/GraphQLDocument.tsx +11 -68
- package/src/template/components/GraphQLInteractive/lib/parser.ts +81 -29
- package/src/template/components/VersionCoveragePicker.tsx +4 -5
- package/src/template/components/VersionPicker.tsx +9 -2
- package/src/template/components/home/FeaturesGrid.tsx +1 -1
- package/src/template/components/home/HeroSection.tsx +1 -1
- package/src/template/components/home/QuickStart.tsx +16 -7
- package/src/template/components/home/RecentChanges.tsx +3 -1
- package/src/template/hooks/use-highlighted.ts +31 -19
- package/src/template/lib/fetch-text.ts +45 -4
- package/src/template/routes/changelog.tsx +10 -4
- package/src/template/routes/examples/_index.tsx +1 -1
- package/src/template/routes/examples/name.tsx +4 -2
- package/src/template/routes/reference.tsx +6 -6
- package/src/template/stores/toast.ts +6 -3
- package/src/vite/plugins/navbar.ts +1 -1
- package/src/vite/plugins/routes-manifest.ts +1 -1
- package/build/lib/graph/$$.d.ts +0 -2
- package/build/lib/graph/$$.d.ts.map +0 -1
- package/build/lib/graph/$$.js +0 -2
- package/build/lib/graph/$$.js.map +0 -1
- package/build/lib/graph/$.d.ts +0 -2
- package/build/lib/graph/$.d.ts.map +0 -1
- package/build/lib/graph/$.js +0 -2
- package/build/lib/graph/$.js.map +0 -1
- package/build/lib/graph/graph.d.ts +0 -127
- package/build/lib/graph/graph.d.ts.map +0 -1
- package/build/lib/graph/graph.js +0 -152
- package/build/lib/graph/graph.js.map +0 -1
- package/build/lib/mask/$$.d.ts +0 -3
- package/build/lib/mask/$$.d.ts.map +0 -1
- package/build/lib/mask/$$.js +0 -3
- package/build/lib/mask/$$.js.map +0 -1
- package/build/lib/mask/$.d.ts +0 -2
- package/build/lib/mask/$.d.ts.map +0 -1
- package/build/lib/mask/$.js +0 -2
- package/build/lib/mask/$.js.map +0 -1
- package/build/lib/mask/apply.d.ts +0 -86
- package/build/lib/mask/apply.d.ts.map +0 -1
- package/build/lib/mask/apply.js +0 -86
- package/build/lib/mask/apply.js.map +0 -1
- package/build/lib/mask/mask.d.ts +0 -124
- package/build/lib/mask/mask.d.ts.map +0 -1
- package/build/lib/mask/mask.js +0 -137
- package/build/lib/mask/mask.js.map +0 -1
- package/build/lib/mask/mask.test-d.d.ts +0 -2
- package/build/lib/mask/mask.test-d.d.ts.map +0 -1
- package/build/lib/mask/mask.test-d.js +0 -102
- package/build/lib/mask/mask.test-d.js.map +0 -1
- package/build/lib/version-selection/$$.d.ts +0 -2
- package/build/lib/version-selection/$$.d.ts.map +0 -1
- package/build/lib/version-selection/$$.js +0 -2
- package/build/lib/version-selection/$$.js.map +0 -1
- package/build/lib/version-selection/$.d.ts.map +0 -1
- package/build/lib/version-selection/$.js.map +0 -1
- package/build/lib/version-selection/version-selection.d.ts.map +0 -1
- package/build/lib/version-selection/version-selection.js.map +0 -1
- package/src/lib/graph/$$.ts +0 -1
- package/src/lib/graph/$.ts +0 -1
- package/src/lib/graph/graph.ts +0 -197
- package/src/lib/mask/$$.ts +0 -2
- package/src/lib/mask/$.test.ts +0 -226
- package/src/lib/mask/$.ts +0 -1
- package/src/lib/mask/apply.ts +0 -134
- package/src/lib/mask/mask.test-d.ts +0 -156
- package/src/lib/mask/mask.ts +0 -244
- package/src/lib/version-selection/$$.ts +0 -1
- /package/build/lib/{version-selection → version-coverage}/$.d.ts +0 -0
- /package/build/lib/{version-selection → version-coverage}/$.js +0 -0
- /package/src/lib/{version-selection → version-coverage}/$.ts +0 -0
package/src/api/schema/$.test.ts
CHANGED
@@ -3,7 +3,7 @@ import { Catalog } from '#lib/catalog/$'
|
|
3
3
|
import { Grafaid } from '#lib/grafaid'
|
4
4
|
import { MemoryFilesystem } from '#lib/memory-filesystem/$'
|
5
5
|
import * as NodeFileSystem from '@effect/platform-node/NodeFileSystem'
|
6
|
-
import { Effect } from 'effect'
|
6
|
+
import { Effect, HashMap } from 'effect'
|
7
7
|
import { expect } from 'vitest'
|
8
8
|
import { Test } from '../../../tests/unit/helpers/test.js'
|
9
9
|
import { Schema } from './$.js'
|
@@ -421,7 +421,7 @@ testWithFileSystem<BaseTestCase & {
|
|
421
421
|
expect(result!._tag).toBe('CatalogVersioned')
|
422
422
|
if (expected.versionCount !== undefined) {
|
423
423
|
const versioned = result as Catalog.Versioned.Versioned
|
424
|
-
expect(versioned.entries
|
424
|
+
expect(HashMap.size(versioned.entries)).toBe(expected.versionCount)
|
425
425
|
}
|
426
426
|
} else {
|
427
427
|
expect(result!._tag).toBe('CatalogUnversioned')
|
@@ -98,8 +98,8 @@ export const loader = InputSource.createEffect({
|
|
98
98
|
// Check if we have either:
|
99
99
|
// 1. A single schema.graphql file (non-versioned mode)
|
100
100
|
// 2. Any .graphql files with valid date names (versioned mode)
|
101
|
-
const hasSchemaFile =
|
102
|
-
const hasVersionedFiles =
|
101
|
+
const hasSchemaFile = Array.some(files, file => file === 'schema.graphql')
|
102
|
+
const hasVersionedFiles = Array.some(files, file => {
|
103
103
|
if (!file.endsWith('.graphql')) return false
|
104
104
|
const name = Path.basename(file, '.graphql')
|
105
105
|
return /^\d{4}-\d{2}-\d{2}$/.test(name)
|
@@ -10,7 +10,7 @@ import { debugPolen } from '#singletons/debug'
|
|
10
10
|
import { PlatformError } from '@effect/platform/Error'
|
11
11
|
import { FileSystem } from '@effect/platform/FileSystem'
|
12
12
|
import { Arr, Path } from '@wollybeard/kit'
|
13
|
-
import { Effect } from 'effect'
|
13
|
+
import { Array, Effect, HashMap } from 'effect'
|
14
14
|
import type { GraphQLSchema } from 'graphql'
|
15
15
|
|
16
16
|
const debug = debugPolen.sub(`schema:data-source-versioned-schema-directory`)
|
@@ -393,9 +393,14 @@ export const readOrThrow = (
|
|
393
393
|
// Reverse to have newest first
|
394
394
|
catalogEntries.reverse()
|
395
395
|
|
396
|
+
// Convert array to HashMap with version as key
|
397
|
+
const entriesMap = HashMap.fromIterable(
|
398
|
+
catalogEntries.map(entry => [entry.version, entry] as const),
|
399
|
+
)
|
400
|
+
|
396
401
|
debug(`computed ${catalogEntries.length} entries`)
|
397
402
|
return Catalog.Versioned.make({
|
398
|
-
entries:
|
403
|
+
entries: entriesMap,
|
399
404
|
})
|
400
405
|
})
|
401
406
|
|
@@ -447,9 +452,8 @@ export const loader = InputSource.createEffect({
|
|
447
452
|
// Check for revision files or schema.graphql
|
448
453
|
const dirFiles = yield* Effect.either(fs.readDirectory(versionPath))
|
449
454
|
if (dirFiles._tag === 'Right') {
|
450
|
-
const hasRevisions = dirFiles.right
|
451
|
-
/^\d{4}-\d{2}-\d{2}\.graphql$/.test(file) || file === 'schema.graphql'
|
452
|
-
)
|
455
|
+
const hasRevisions = Array.some(dirFiles.right, file =>
|
456
|
+
/^\d{4}-\d{2}-\d{2}\.graphql$/.test(file) || file === 'schema.graphql')
|
453
457
|
if (hasRevisions) {
|
454
458
|
return true
|
455
459
|
}
|
@@ -515,9 +519,8 @@ export const loader = InputSource.createEffect({
|
|
515
519
|
// Check for schema files
|
516
520
|
const dirFiles = yield* Effect.either(fs.readDirectory(versionPath))
|
517
521
|
if (dirFiles._tag === 'Right') {
|
518
|
-
const hasSchemaFiles = dirFiles.right
|
519
|
-
/^\d{4}-\d{2}-\d{2}\.graphql$/.test(file) || file === 'schema.graphql'
|
520
|
-
)
|
522
|
+
const hasSchemaFiles = Array.some(dirFiles.right, file =>
|
523
|
+
/^\d{4}-\d{2}-\d{2}\.graphql$/.test(file) || file === 'schema.graphql')
|
521
524
|
|
522
525
|
if (hasSchemaFiles) {
|
523
526
|
debug('found valid schema files, proceeding with full read')
|
package/src/api/schema/load.ts
CHANGED
@@ -7,7 +7,7 @@ import { Catalog } from '#lib/catalog/$'
|
|
7
7
|
import type { PlatformError } from '@effect/platform/Error'
|
8
8
|
import type { FileSystem } from '@effect/platform/FileSystem'
|
9
9
|
import { Arr } from '@wollybeard/kit'
|
10
|
-
import { Effect } from 'effect'
|
10
|
+
import { Array, Effect, Option } from 'effect'
|
11
11
|
|
12
12
|
// For now, we'll need a type that accepts both promise and effect sources
|
13
13
|
type AnyInputSource = InputSource | EffectInputSource
|
@@ -142,7 +142,7 @@ export const loadOrNull = (
|
|
142
142
|
const catalog = loadedSchema.data as Catalog.Catalog
|
143
143
|
Catalog.fold(
|
144
144
|
(versioned) => {
|
145
|
-
for (const schema of versioned
|
145
|
+
for (const schema of Catalog.Versioned.getAll(versioned)) {
|
146
146
|
Augmentations.apply(schema.definition, augmentations)
|
147
147
|
}
|
148
148
|
},
|
@@ -133,7 +133,7 @@ export const heroImage = Command.make(
|
|
133
133
|
const schemaResult = yield* Api.Schema.loadOrNull(config)
|
134
134
|
if (schemaResult?.data) {
|
135
135
|
try {
|
136
|
-
const latestSchema = Catalog.
|
136
|
+
const latestSchema = Catalog.getLatest(schemaResult.data)
|
137
137
|
if (latestSchema?.definition) {
|
138
138
|
schema = latestSchema.definition
|
139
139
|
const context = AiImageGeneration.analyzeSchema(schema)
|
@@ -1,13 +1,33 @@
|
|
1
1
|
import { S } from '#lib/kit-temp/effect'
|
2
2
|
import { Schema } from '#lib/schema/$'
|
3
|
+
import { VersionCoverage } from '#lib/version-coverage'
|
3
4
|
import { Version } from '#lib/version/$'
|
4
|
-
import { Match } from 'effect'
|
5
|
+
import { Data, Either, HashMap, Match, Option } from 'effect'
|
5
6
|
import * as Unversioned from './unversioned.js'
|
6
7
|
import * as Versioned from './versioned.js'
|
7
8
|
|
8
9
|
export * as Unversioned from './unversioned.js'
|
9
10
|
export * as Versioned from './versioned.js'
|
10
11
|
|
12
|
+
// ============================================================================
|
13
|
+
// Error Types
|
14
|
+
// ============================================================================
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Error thrown when a version is not found in the catalog
|
18
|
+
*/
|
19
|
+
export class VersionNotFoundInCatalogError extends Data.TaggedError('VersionNotFoundInCatalogError')<{
|
20
|
+
readonly version: string
|
21
|
+
readonly reason: string
|
22
|
+
}> {}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Error thrown when a catalog has no entries
|
26
|
+
*/
|
27
|
+
export class EmptyCatalogError extends Data.TaggedError('EmptyCatalogError')<{
|
28
|
+
readonly reason: string
|
29
|
+
}> {}
|
30
|
+
|
11
31
|
// ============================================================================
|
12
32
|
// Schema
|
13
33
|
// ============================================================================
|
@@ -62,7 +82,7 @@ export const equivalence = S.equivalence(Catalog)
|
|
62
82
|
*/
|
63
83
|
export const getVersionCount = (catalog: Catalog): number =>
|
64
84
|
fold(
|
65
|
-
(versioned) => versioned.entries
|
85
|
+
(versioned) => HashMap.size(versioned.entries),
|
66
86
|
(_unversioned) => 1, // Unversioned catalog is effectively one version
|
67
87
|
)(catalog)
|
68
88
|
|
@@ -79,28 +99,85 @@ export const getSchemaVersionString = (schema: Schema.Schema): string => {
|
|
79
99
|
* Get the version string from a schema.
|
80
100
|
* Returns the stringified version for versioned schemas, or '__UNVERSIONED__' for unversioned schemas.
|
81
101
|
*/
|
82
|
-
export const
|
102
|
+
export const getLatest = (catalog: Catalog): Schema.Schema =>
|
83
103
|
Match.value(catalog).pipe(Match.tagsExhaustive({
|
84
|
-
CatalogVersioned:
|
85
|
-
|
86
|
-
const latestEntry = versioned.entries[0]!
|
87
|
-
return latestEntry
|
88
|
-
},
|
89
|
-
CatalogUnversioned: (unversioned) => {
|
90
|
-
return unversioned.schema
|
91
|
-
},
|
104
|
+
CatalogVersioned: Versioned.getLatestOrThrow,
|
105
|
+
CatalogUnversioned: (unversioned) => unversioned.schema,
|
92
106
|
}))
|
93
107
|
|
94
108
|
/**
|
95
109
|
* Get the latest version identifier from a catalog.
|
96
|
-
* Returns the version for versioned catalogs, or
|
110
|
+
* Returns the version for versioned catalogs, or none for unversioned catalogs.
|
97
111
|
*/
|
98
|
-
export const
|
99
|
-
if (!catalog) return
|
112
|
+
export const getLatestVersion = (catalog?: Catalog): Option.Option<Version.Version> => {
|
113
|
+
if (!catalog) return Option.none()
|
100
114
|
return Match.value(catalog).pipe(
|
101
115
|
Match.tagsExhaustive({
|
102
|
-
CatalogUnversioned: () =>
|
103
|
-
CatalogVersioned: (cat) =>
|
116
|
+
CatalogUnversioned: () => Option.none(),
|
117
|
+
CatalogVersioned: (cat) => {
|
118
|
+
const versions = Versioned.getVersions(cat)
|
119
|
+
return versions[0] ? Option.some(versions[0]) : Option.none()
|
120
|
+
},
|
104
121
|
}),
|
105
122
|
)
|
106
123
|
}
|
124
|
+
|
125
|
+
// ============================================================================
|
126
|
+
// Resolution Functions
|
127
|
+
// ============================================================================
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Resolve schema from catalog for a given version coverage.
|
131
|
+
*
|
132
|
+
* @param catalog - The schema catalog
|
133
|
+
* @param versionCoverage - The version coverage to use (optional, defaults to latest)
|
134
|
+
* @returns Either with the resolved schema or error
|
135
|
+
*/
|
136
|
+
export const resolveCatalogSchemaEither = (
|
137
|
+
catalog: Catalog,
|
138
|
+
versionCoverage?: VersionCoverage.VersionCoverage | null,
|
139
|
+
): Either.Either<Schema.Schema, VersionNotFoundInCatalogError | EmptyCatalogError> => {
|
140
|
+
if (Unversioned.is(catalog)) {
|
141
|
+
return Either.right(catalog.schema)
|
142
|
+
}
|
143
|
+
|
144
|
+
// If no version coverage specified, use latest
|
145
|
+
if (!versionCoverage) {
|
146
|
+
return Versioned.getLatest(catalog)
|
147
|
+
}
|
148
|
+
|
149
|
+
// Get the latest version from the coverage
|
150
|
+
const version = VersionCoverage.getLatest(versionCoverage)
|
151
|
+
|
152
|
+
const schemaOption = HashMap.get(catalog.entries, version)
|
153
|
+
if (Option.isNone(schemaOption)) {
|
154
|
+
return Either.left(
|
155
|
+
new VersionNotFoundInCatalogError({
|
156
|
+
version: Version.encodeSync(version),
|
157
|
+
reason: `Version ${Version.encodeSync(version)} not found in catalog`,
|
158
|
+
}),
|
159
|
+
)
|
160
|
+
}
|
161
|
+
|
162
|
+
return Either.right(Option.getOrThrow(schemaOption))
|
163
|
+
}
|
164
|
+
|
165
|
+
/**
|
166
|
+
* Resolve schema from catalog for a given version coverage.
|
167
|
+
*
|
168
|
+
* @param catalog - The schema catalog
|
169
|
+
* @param versionCoverage - The version coverage to use (optional, defaults to latest)
|
170
|
+
* @returns The resolved schema
|
171
|
+
* @throws {Error} If catalog is versioned but version is not found
|
172
|
+
* @deprecated Use resolveCatalogSchemaEither which returns Either
|
173
|
+
*/
|
174
|
+
export const resolveCatalogSchema = (
|
175
|
+
catalog: Catalog,
|
176
|
+
versionCoverage?: VersionCoverage.VersionCoverage | null,
|
177
|
+
): Schema.Schema => {
|
178
|
+
const result = resolveCatalogSchemaEither(catalog, versionCoverage)
|
179
|
+
if (Either.isLeft(result)) {
|
180
|
+
throw new Error(result.left.reason)
|
181
|
+
}
|
182
|
+
return result.right
|
183
|
+
}
|
@@ -1,12 +1,18 @@
|
|
1
1
|
import { S } from '#lib/kit-temp/effect'
|
2
|
+
import { Array, Either, HashMap, Iterable, Order, pipe } from 'effect'
|
2
3
|
import { Schema } from '../schema/$.js'
|
4
|
+
import { Version } from '../version/$.js'
|
5
|
+
import { EmptyCatalogError } from './catalog.js'
|
3
6
|
|
4
7
|
// ============================================================================
|
5
8
|
// Schema
|
6
9
|
// ============================================================================
|
7
10
|
|
8
11
|
export const Versioned = S.TaggedStruct('CatalogVersioned', {
|
9
|
-
entries: S.
|
12
|
+
entries: S.HashMap({
|
13
|
+
key: Version.Version,
|
14
|
+
value: Schema.Versioned.Versioned,
|
15
|
+
}),
|
10
16
|
}).annotations({
|
11
17
|
identifier: 'CatalogVersioned',
|
12
18
|
title: 'Versioned Catalog',
|
@@ -45,18 +51,62 @@ export const equivalence = S.equivalence(Versioned)
|
|
45
51
|
// Domain Logic
|
46
52
|
// ============================================================================
|
47
53
|
|
54
|
+
/**
|
55
|
+
* Get the latest schema from a versioned catalog.
|
56
|
+
* The latest version is determined by Version.max comparison.
|
57
|
+
*
|
58
|
+
* @param catalog - The versioned catalog
|
59
|
+
* @returns Either with the latest schema or EmptyCatalogError
|
60
|
+
*/
|
61
|
+
export const getLatest = (catalog: Versioned): Either.Either<Schema.Versioned.Versioned, EmptyCatalogError> => {
|
62
|
+
const schema = getAll(catalog)[0]
|
63
|
+
if (!schema) {
|
64
|
+
return Either.left(
|
65
|
+
new EmptyCatalogError({
|
66
|
+
reason: 'Versioned catalog has no entries - cannot get latest schema',
|
67
|
+
}),
|
68
|
+
)
|
69
|
+
}
|
70
|
+
return Either.right(schema)
|
71
|
+
}
|
72
|
+
|
48
73
|
/**
|
49
74
|
* Get the latest schema definition from a versioned catalog.
|
50
|
-
* The latest version is
|
75
|
+
* The latest version is determined by Version.max comparison.
|
51
76
|
*
|
52
77
|
* @param catalog - The versioned catalog
|
53
78
|
* @returns The GraphQL schema definition of the latest version
|
54
79
|
* @throws {Error} If the catalog has no entries
|
80
|
+
* @deprecated Use getLatest which returns Either
|
55
81
|
*/
|
56
|
-
export const
|
57
|
-
const
|
58
|
-
if (
|
59
|
-
throw new Error(
|
82
|
+
export const getLatestOrThrow = (catalog: Versioned): Schema.Versioned.Versioned => {
|
83
|
+
const result = getLatest(catalog)
|
84
|
+
if (Either.isLeft(result)) {
|
85
|
+
throw new Error(result.left.reason)
|
60
86
|
}
|
61
|
-
return
|
87
|
+
return result.right
|
88
|
+
}
|
89
|
+
|
90
|
+
/**
|
91
|
+
* Get all schemas sorted by version (newest first)
|
92
|
+
*/
|
93
|
+
export const getAll = (catalog: Versioned): Schema.Versioned.Versioned[] => {
|
94
|
+
return pipe(
|
95
|
+
catalog.entries,
|
96
|
+
HashMap.values,
|
97
|
+
Array.fromIterable,
|
98
|
+
// Put newest versions first in array
|
99
|
+
Array.sort(Order.reverse(Schema.Versioned.order)),
|
100
|
+
)
|
101
|
+
}
|
102
|
+
|
103
|
+
/**
|
104
|
+
* Get all versions sorted (newest first)
|
105
|
+
*/
|
106
|
+
export const getVersions = (catalog: Versioned): Version.Version[] => {
|
107
|
+
return pipe(
|
108
|
+
catalog,
|
109
|
+
getAll,
|
110
|
+
Array.map(_ => _.version),
|
111
|
+
)
|
62
112
|
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { Catalog } from '#lib/catalog/$'
|
2
2
|
import { DateOnly } from '#lib/date-only/$'
|
3
3
|
import { Version } from '#lib/version/$'
|
4
|
+
import { HashMap } from 'effect'
|
4
5
|
import { buildSchema, type GraphQLSchema } from 'graphql'
|
5
6
|
import { describe, expect, test } from 'vitest'
|
6
7
|
import { CatalogStatistics } from './$.js'
|
@@ -91,14 +92,20 @@ describe('analyzeCatalog', () => {
|
|
91
92
|
name: 'versioned catalog',
|
92
93
|
catalog: () =>
|
93
94
|
Catalog.Versioned.make({
|
94
|
-
entries:
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
95
|
+
entries: HashMap.make(
|
96
|
+
[
|
97
|
+
Version.fromInteger(1),
|
98
|
+
makeVersionedEntry(1, buildSchema('type Query { hello: String }'), ['2024-01-01', '2024-01-15']),
|
99
|
+
],
|
100
|
+
[
|
101
|
+
Version.fromInteger(2),
|
102
|
+
makeVersionedEntry(
|
103
|
+
2,
|
104
|
+
buildSchema('type Query { hello: String, world: String } type User { id: ID!, name: String }'),
|
105
|
+
['2024-02-01'],
|
106
|
+
),
|
107
|
+
],
|
108
|
+
),
|
102
109
|
}),
|
103
110
|
expectedVersions: 2,
|
104
111
|
expectedCurrentVersion: '2',
|
@@ -132,10 +139,13 @@ describe('analyzeCatalog', () => {
|
|
132
139
|
|
133
140
|
test('calculates stability metrics', () => {
|
134
141
|
const catalog = Catalog.Versioned.make({
|
135
|
-
entries:
|
136
|
-
|
137
|
-
|
138
|
-
|
142
|
+
entries: HashMap.make(
|
143
|
+
[
|
144
|
+
Version.fromInteger(1),
|
145
|
+
makeVersionedEntry(1, buildSchema('type Query { test: String }'), ['2024-01-01', '2024-01-05']),
|
146
|
+
],
|
147
|
+
[Version.fromInteger(2), makeVersionedEntry(2, buildSchema('type Query { test: String }'), ['2024-01-10'])],
|
148
|
+
),
|
139
149
|
})
|
140
150
|
|
141
151
|
const report = CatalogStatistics.analyzeCatalog(catalog)
|
@@ -16,7 +16,7 @@ export const analyzeCatalog = (catalog: Catalog.Catalog, options: AnalyzeOptions
|
|
16
16
|
const processResult = Catalog.fold(
|
17
17
|
// Versioned catalog
|
18
18
|
(versioned) => {
|
19
|
-
for (const entry of versioned
|
19
|
+
for (const entry of Catalog.Versioned.getAll(versioned)) {
|
20
20
|
const versionId = Version.encodeSync(entry.version)
|
21
21
|
// Analyze the schema definition for this version
|
22
22
|
// Use the first revision date if available
|
@@ -55,8 +55,8 @@ export const analyzeCatalog = (catalog: Catalog.Catalog, options: AnalyzeOptions
|
|
55
55
|
// Calculate stability metrics
|
56
56
|
const stability = calculateStabilityMetrics(versions, revisionDates)
|
57
57
|
|
58
|
-
// Get current version (
|
59
|
-
const current = versions[
|
58
|
+
// Get current version (first in array since sorted newest first)
|
59
|
+
const current = versions[0]
|
60
60
|
|
61
61
|
return {
|
62
62
|
stability,
|
@@ -1,6 +1,30 @@
|
|
1
|
+
import { Catalog } from '#lib/catalog/$'
|
1
2
|
import { S } from '#lib/kit-temp/effect'
|
3
|
+
import { Schema } from '#lib/schema/$'
|
4
|
+
import { VersionCoverage } from '#lib/version-coverage'
|
5
|
+
import { Version } from '#lib/version/$'
|
6
|
+
import { Data, Either, Option } from 'effect'
|
2
7
|
import { DocumentUnversioned } from './unversioned.js'
|
3
|
-
import
|
8
|
+
import * as DocumentVersioned from './versioned.js'
|
9
|
+
|
10
|
+
// ============================================================================
|
11
|
+
// Error Types
|
12
|
+
// ============================================================================
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Error thrown when trying to use version coverage with an unversioned catalog
|
16
|
+
*/
|
17
|
+
export class VersionCoverageMismatchError extends Data.TaggedError('VersionCoverageMismatchError')<{
|
18
|
+
readonly reason: string
|
19
|
+
}> {}
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Error thrown when a version is not found in the document
|
23
|
+
*/
|
24
|
+
export class VersionNotFoundInDocumentError extends Data.TaggedError('VersionNotFoundInDocumentError')<{
|
25
|
+
readonly version: string
|
26
|
+
readonly reason: string
|
27
|
+
}> {}
|
4
28
|
|
5
29
|
// ============================================================================
|
6
30
|
// Schema
|
@@ -13,7 +37,7 @@ import { DocumentVersioned } from './versioned.js'
|
|
13
37
|
*/
|
14
38
|
export const Document = S.Union(
|
15
39
|
DocumentUnversioned,
|
16
|
-
DocumentVersioned,
|
40
|
+
DocumentVersioned.DocumentVersioned,
|
17
41
|
)
|
18
42
|
|
19
43
|
// ============================================================================
|
@@ -35,3 +59,112 @@ export const is = S.is(Document)
|
|
35
59
|
export const decode = S.decode(Document)
|
36
60
|
export const decodeSync = S.decodeSync(Document)
|
37
61
|
export const encode = S.encode(Document)
|
62
|
+
|
63
|
+
// ============================================================================
|
64
|
+
// Domain Logic - Resolution Functions
|
65
|
+
// ============================================================================
|
66
|
+
|
67
|
+
/**
|
68
|
+
* Resolve document content for a given version coverage.
|
69
|
+
*
|
70
|
+
* @param document - The document to resolve content from
|
71
|
+
* @param versionCoverage - The version coverage to use (optional, defaults to latest)
|
72
|
+
* @returns The resolved document content
|
73
|
+
* @throws {Error} If version is not found in document
|
74
|
+
*/
|
75
|
+
export const resolveDocumentContent = (
|
76
|
+
document: Document,
|
77
|
+
versionCoverage?: VersionCoverage.VersionCoverage | null,
|
78
|
+
): string => {
|
79
|
+
if (document._tag === 'DocumentUnversioned') {
|
80
|
+
return document.document
|
81
|
+
}
|
82
|
+
|
83
|
+
// If no version coverage specified, use latest
|
84
|
+
if (!versionCoverage) {
|
85
|
+
return DocumentVersioned.getContentForLatestVersionOrThrow(document)
|
86
|
+
}
|
87
|
+
|
88
|
+
// Get the latest version from the coverage
|
89
|
+
const version = VersionCoverage.getLatest(versionCoverage)
|
90
|
+
const contentOption = DocumentVersioned.getContentForVersion(document, version)
|
91
|
+
|
92
|
+
if (Option.isNone(contentOption)) {
|
93
|
+
throw new VersionNotFoundInDocumentError({
|
94
|
+
version: Version.encodeSync(version),
|
95
|
+
reason: `Version ${Version.encodeSync(version)} not covered by document`,
|
96
|
+
})
|
97
|
+
}
|
98
|
+
|
99
|
+
return contentOption.value
|
100
|
+
}
|
101
|
+
|
102
|
+
/**
|
103
|
+
* Resolve both document content and schema for a given version coverage.
|
104
|
+
* This is the primary resolution function that handles all combinations
|
105
|
+
* of versioned/unversioned documents and catalogs.
|
106
|
+
*
|
107
|
+
* @param document - The document to resolve content from
|
108
|
+
* @param catalog - The schema catalog (optional)
|
109
|
+
* @param versionCoverage - The version coverage to use (optional, defaults to latest)
|
110
|
+
* @returns Either with resolved content and optional schema, or error
|
111
|
+
*/
|
112
|
+
export const resolveDocumentAndSchema = (
|
113
|
+
document: Document,
|
114
|
+
catalog?: Catalog.Catalog,
|
115
|
+
versionCoverage?: VersionCoverage.VersionCoverage | null,
|
116
|
+
): Either.Either<
|
117
|
+
{ content: string; schema?: Schema.Schema },
|
118
|
+
| VersionCoverageMismatchError
|
119
|
+
| VersionNotFoundInDocumentError
|
120
|
+
| Catalog.VersionNotFoundInCatalogError
|
121
|
+
| Catalog.EmptyCatalogError
|
122
|
+
> => {
|
123
|
+
// Handle unversioned document
|
124
|
+
if (document._tag === 'DocumentUnversioned') {
|
125
|
+
const content = document.document
|
126
|
+
|
127
|
+
if (!catalog) {
|
128
|
+
return Either.right({ content })
|
129
|
+
}
|
130
|
+
|
131
|
+
return Catalog.resolveCatalogSchemaEither(catalog, null).pipe(
|
132
|
+
Either.map(schema => ({ content, schema })),
|
133
|
+
)
|
134
|
+
}
|
135
|
+
|
136
|
+
// Handle versioned document
|
137
|
+
let content: string
|
138
|
+
try {
|
139
|
+
content = resolveDocumentContent(document, versionCoverage)
|
140
|
+
} catch (error) {
|
141
|
+
// resolveDocumentContent throws our tagged error
|
142
|
+
if (error instanceof VersionNotFoundInDocumentError) {
|
143
|
+
return Either.left(error)
|
144
|
+
}
|
145
|
+
// This shouldn't happen but handle gracefully
|
146
|
+
return Either.left(
|
147
|
+
new VersionNotFoundInDocumentError({
|
148
|
+
version: String(versionCoverage),
|
149
|
+
reason: error instanceof Error ? error.message : String(error),
|
150
|
+
}),
|
151
|
+
)
|
152
|
+
}
|
153
|
+
|
154
|
+
if (!catalog) {
|
155
|
+
return Either.right({ content })
|
156
|
+
}
|
157
|
+
|
158
|
+
// Cannot use version coverage with unversioned catalog
|
159
|
+
if (versionCoverage && Catalog.Unversioned.is(catalog)) {
|
160
|
+
return Either.left(
|
161
|
+
new VersionCoverageMismatchError({
|
162
|
+
reason: 'Cannot use a version coverage with an unversioned catalog',
|
163
|
+
}),
|
164
|
+
)
|
165
|
+
}
|
166
|
+
|
167
|
+
return Catalog.resolveCatalogSchemaEither(catalog, versionCoverage).pipe(
|
168
|
+
Either.map(schema => ({ content, schema })),
|
169
|
+
)
|
170
|
+
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { S } from '#lib/kit-temp/effect'
|
2
|
-
import { VersionCoverage } from '#lib/version-
|
2
|
+
import { VersionCoverage } from '#lib/version-coverage'
|
3
3
|
import { Version } from '#lib/version/$'
|
4
4
|
import { HashMap, Option } from 'effect'
|
5
5
|
|
@@ -56,21 +56,21 @@ export const encode = S.encode(DocumentVersioned)
|
|
56
56
|
export const getContentForVersion = (
|
57
57
|
doc: DocumentVersioned,
|
58
58
|
version: Version.Version,
|
59
|
-
): string
|
59
|
+
): Option.Option<string> => {
|
60
60
|
// Try exact match first (single version key)
|
61
61
|
const exactMatch = HashMap.get(doc.versionDocuments, version)
|
62
62
|
if (Option.isSome(exactMatch)) {
|
63
|
-
return exactMatch.value
|
63
|
+
return Option.some(exactMatch.value)
|
64
64
|
}
|
65
65
|
|
66
66
|
// Check version sets
|
67
67
|
for (const [selection, content] of HashMap.entries(doc.versionDocuments)) {
|
68
68
|
if (VersionCoverage.isSet(selection) && VersionCoverage.contains(selection, version)) {
|
69
|
-
return content
|
69
|
+
return Option.some(content)
|
70
70
|
}
|
71
71
|
}
|
72
72
|
|
73
|
-
return
|
73
|
+
return Option.none()
|
74
74
|
}
|
75
75
|
|
76
76
|
/**
|
@@ -98,9 +98,9 @@ export const getContentForLatestVersionOrThrow = (doc: DocumentVersioned): strin
|
|
98
98
|
// Use Version.max with reduce to find the latest version
|
99
99
|
const latestVersion = versions.reduce(Version.max)
|
100
100
|
|
101
|
-
const
|
102
|
-
if (
|
101
|
+
const contentOption = getContentForVersion(doc, latestVersion)
|
102
|
+
if (Option.isNone(contentOption)) {
|
103
103
|
throw new Error('Latest version not found in document')
|
104
104
|
}
|
105
|
-
return
|
105
|
+
return contentOption.value
|
106
106
|
}
|