@tokens-studio/tokenscript-schemas 0.0.11 → 0.0.13
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/cli/index.cjs +31 -8
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +30 -8
- package/dist/cli/index.js.map +1 -1
- package/package.json +2 -1
- package/src/bundler/bundle-schema.ts +146 -0
- package/src/bundler/index.ts +151 -0
- package/src/bundler/schema-dependency-resolver.ts +299 -0
- package/src/bundler/selective-bundler.test.ts +94 -0
- package/src/bundler/selective-bundler.ts +159 -0
- package/src/bundler/types.ts +93 -0
- package/src/bundler/utils.ts +74 -0
- package/src/cli/commands/bundle.integration.test.ts +153 -0
- package/src/cli/commands/bundle.test.ts +57 -0
- package/src/cli/commands/bundle.ts +237 -0
- package/src/cli/commands/list.ts +109 -0
- package/src/cli/config-schema.ts +36 -0
- package/src/cli/index.ts +50 -0
- package/src/cli/output-generator.ts +63 -0
- package/src/downloader/index.ts +248 -0
- package/src/downloader/types.ts +48 -0
- package/src/index.ts +8 -0
- package/src/schemas/functions/adjust_chroma/adjust-chroma.tokenscript +27 -0
- package/src/schemas/functions/adjust_chroma/schema.json +48 -0
- package/src/schemas/functions/adjust_chroma/unit.test.ts +76 -0
- package/src/schemas/functions/adjust_hue/adjust-hue.tokenscript +32 -0
- package/src/schemas/functions/adjust_hue/schema.json +48 -0
- package/src/schemas/functions/adjust_hue/unit.test.ts +68 -0
- package/src/schemas/functions/adjust_lightness/adjust-lightness.tokenscript +31 -0
- package/src/schemas/functions/adjust_lightness/schema.json +48 -0
- package/src/schemas/functions/adjust_lightness/unit.test.ts +88 -0
- package/src/schemas/functions/adjust_to_contrast/adjust-to-contrast.tokenscript +131 -0
- package/src/schemas/functions/adjust_to_contrast/schema.json +56 -0
- package/src/schemas/functions/adjust_to_contrast/unit.test.ts +81 -0
- package/src/schemas/functions/alpha_blend/alpha-blend.tokenscript +46 -0
- package/src/schemas/functions/alpha_blend/schema.json +28 -0
- package/src/schemas/functions/alpha_blend/unit.test.ts +135 -0
- package/src/schemas/functions/alpha_scale/alpha-scale.tokenscript +38 -0
- package/src/schemas/functions/alpha_scale/schema.json +24 -0
- package/src/schemas/functions/alpha_scale/unit.test.ts +50 -0
- package/src/schemas/functions/analogous/analogous.tokenscript +47 -0
- package/src/schemas/functions/analogous/schema.json +28 -0
- package/src/schemas/functions/analogous/unit.test.ts +64 -0
- package/src/schemas/functions/apca_contrast/apca-contrast.tokenscript +129 -0
- package/src/schemas/functions/apca_contrast/schema.json +24 -0
- package/src/schemas/functions/apca_contrast/unit.test.ts +259 -0
- package/src/schemas/functions/are_similar/are-similar.tokenscript +24 -0
- package/src/schemas/functions/are_similar/schema.json +57 -0
- package/src/schemas/functions/are_similar/unit.test.ts +57 -0
- package/src/schemas/functions/auto_text_color/auto-text-color.tokenscript +41 -0
- package/src/schemas/functions/auto_text_color/schema.json +53 -0
- package/src/schemas/functions/auto_text_color/unit.test.ts +122 -0
- package/src/schemas/functions/best_contrast/best-contrast.tokenscript +66 -0
- package/src/schemas/functions/best_contrast/schema.json +24 -0
- package/src/schemas/functions/best_contrast/unit.test.ts +64 -0
- package/src/schemas/functions/chroma/chroma.tokenscript +12 -0
- package/src/schemas/functions/chroma/schema.json +44 -0
- package/src/schemas/functions/chroma/unit.test.ts +86 -0
- package/src/schemas/functions/clamp_chroma/clamp-chroma.tokenscript +21 -0
- package/src/schemas/functions/clamp_chroma/schema.json +52 -0
- package/src/schemas/functions/clamp_chroma/unit.test.ts +60 -0
- package/src/schemas/functions/clamp_lightness/clamp-lightness.tokenscript +21 -0
- package/src/schemas/functions/clamp_lightness/schema.json +52 -0
- package/src/schemas/functions/clamp_lightness/unit.test.ts +76 -0
- package/src/schemas/functions/clamp_to_gamut/clamp-to-gamut.tokenscript +41 -0
- package/src/schemas/functions/clamp_to_gamut/schema.json +20 -0
- package/src/schemas/functions/clamp_to_gamut/unit.test.ts +167 -0
- package/src/schemas/functions/complement/complement.tokenscript +21 -0
- package/src/schemas/functions/complement/schema.json +20 -0
- package/src/schemas/functions/complement/unit.test.ts +81 -0
- package/src/schemas/functions/contrast_ratio/contrast-ratio.tokenscript +36 -0
- package/src/schemas/functions/contrast_ratio/schema.json +24 -0
- package/src/schemas/functions/contrast_ratio/unit.test.ts +91 -0
- package/src/schemas/functions/cooler/cooler.tokenscript +45 -0
- package/src/schemas/functions/cooler/schema.json +43 -0
- package/src/schemas/functions/cooler/unit.test.ts +69 -0
- package/src/schemas/functions/darken/darken.tokenscript +37 -0
- package/src/schemas/functions/darken/schema.json +24 -0
- package/src/schemas/functions/darken/unit.test.ts +105 -0
- package/src/schemas/functions/delta_e_2000/delta-e-2000.tokenscript +184 -0
- package/src/schemas/functions/delta_e_2000/schema.json +36 -0
- package/src/schemas/functions/delta_e_2000/unit.test.ts +243 -0
- package/src/schemas/functions/delta_e_76/delta-e-76.tokenscript +45 -0
- package/src/schemas/functions/delta_e_76/schema.json +24 -0
- package/src/schemas/functions/delta_e_76/unit.test.ts +123 -0
- package/src/schemas/functions/delta_e_ok/delta-e-ok.tokenscript +43 -0
- package/src/schemas/functions/delta_e_ok/schema.json +24 -0
- package/src/schemas/functions/delta_e_ok/unit.test.ts +235 -0
- package/src/schemas/functions/desaturate/desaturate.tokenscript +32 -0
- package/src/schemas/functions/desaturate/schema.json +24 -0
- package/src/schemas/functions/desaturate/unit.test.ts +54 -0
- package/src/schemas/functions/distributed/distributed.tokenscript +54 -0
- package/src/schemas/functions/distributed/schema.json +32 -0
- package/src/schemas/functions/distributed/unit.test.ts +58 -0
- package/src/schemas/functions/diverging/diverging.tokenscript +58 -0
- package/src/schemas/functions/diverging/schema.json +32 -0
- package/src/schemas/functions/diverging/unit.test.ts +70 -0
- package/src/schemas/functions/grayscale/grayscale.tokenscript +17 -0
- package/src/schemas/functions/grayscale/schema.json +20 -0
- package/src/schemas/functions/grayscale/unit.test.ts +79 -0
- package/src/schemas/functions/harmonize/harmonize.tokenscript +61 -0
- package/src/schemas/functions/harmonize/schema.json +52 -0
- package/src/schemas/functions/harmonize/unit.test.ts +56 -0
- package/src/schemas/functions/hue/hue.tokenscript +12 -0
- package/src/schemas/functions/hue/schema.json +44 -0
- package/src/schemas/functions/hue/unit.test.ts +75 -0
- package/src/schemas/functions/hue_difference/hue-difference.tokenscript +42 -0
- package/src/schemas/functions/hue_difference/schema.json +24 -0
- package/src/schemas/functions/hue_difference/unit.test.ts +125 -0
- package/src/schemas/functions/in_gamut/in-gamut.tokenscript +51 -0
- package/src/schemas/functions/in_gamut/schema.json +24 -0
- package/src/schemas/functions/in_gamut/unit.test.ts +178 -0
- package/src/schemas/functions/interpolate/interpolate.tokenscript +61 -0
- package/src/schemas/functions/interpolate/schema.json +52 -0
- package/src/schemas/functions/interpolate/unit.test.ts +96 -0
- package/src/schemas/functions/invert/invert-initializer.tokenscript +29 -0
- package/src/schemas/functions/invert/schema.json +20 -0
- package/src/schemas/functions/invert/unit.test.ts +216 -0
- package/src/schemas/functions/is_cool/is-cool.tokenscript +41 -0
- package/src/schemas/functions/is_cool/schema.json +20 -0
- package/src/schemas/functions/is_cool/unit.test.ts +189 -0
- package/src/schemas/functions/is_dark/is-dark.tokenscript +16 -0
- package/src/schemas/functions/is_dark/schema.json +24 -0
- package/src/schemas/functions/is_dark/unit.test.ts +87 -0
- package/src/schemas/functions/is_light/is-light.tokenscript +16 -0
- package/src/schemas/functions/is_light/schema.json +24 -0
- package/src/schemas/functions/is_light/unit.test.ts +86 -0
- package/src/schemas/functions/is_neutral/is-neutral.tokenscript +16 -0
- package/src/schemas/functions/is_neutral/schema.json +53 -0
- package/src/schemas/functions/is_neutral/unit.test.ts +85 -0
- package/src/schemas/functions/is_warm/is-warm.tokenscript +62 -0
- package/src/schemas/functions/is_warm/schema.json +20 -0
- package/src/schemas/functions/is_warm/unit.test.ts +161 -0
- package/src/schemas/functions/lighten/lighten.tokenscript +37 -0
- package/src/schemas/functions/lighten/schema.json +24 -0
- package/src/schemas/functions/lighten/unit.test.ts +109 -0
- package/src/schemas/functions/lightness/lightness.tokenscript +12 -0
- package/src/schemas/functions/lightness/schema.json +49 -0
- package/src/schemas/functions/lightness/unit.test.ts +99 -0
- package/src/schemas/functions/luminance/luminance.tokenscript +16 -0
- package/src/schemas/functions/luminance/schema.json +20 -0
- package/src/schemas/functions/luminance/unit.test.ts +105 -0
- package/src/schemas/functions/meets_contrast/meets-contrast.tokenscript +49 -0
- package/src/schemas/functions/meets_contrast/schema.json +28 -0
- package/src/schemas/functions/meets_contrast/unit.test.ts +170 -0
- package/src/schemas/functions/mix/mix.tokenscript +47 -0
- package/src/schemas/functions/mix/schema.json +28 -0
- package/src/schemas/functions/mix/unit.test.ts +95 -0
- package/src/schemas/functions/monochromatic/monochromatic.tokenscript +72 -0
- package/src/schemas/functions/monochromatic/schema.json +24 -0
- package/src/schemas/functions/monochromatic/unit.test.ts +91 -0
- package/src/schemas/functions/muted/muted.tokenscript +25 -0
- package/src/schemas/functions/muted/schema.json +48 -0
- package/src/schemas/functions/muted/unit.test.ts +100 -0
- package/src/schemas/functions/neutral_variant/neutral-variant.tokenscript +23 -0
- package/src/schemas/functions/neutral_variant/schema.json +48 -0
- package/src/schemas/functions/neutral_variant/unit.test.ts +102 -0
- package/src/schemas/functions/relative_luminance/relative-luminance.tokenscript +15 -0
- package/src/schemas/functions/relative_luminance/schema.json +49 -0
- package/src/schemas/functions/relative_luminance/unit.test.ts +104 -0
- package/src/schemas/functions/rotate_hue/rotate-hue.tokenscript +20 -0
- package/src/schemas/functions/rotate_hue/schema.json +24 -0
- package/src/schemas/functions/rotate_hue/unit.test.ts +86 -0
- package/src/schemas/functions/saturate/saturate.tokenscript +33 -0
- package/src/schemas/functions/saturate/schema.json +24 -0
- package/src/schemas/functions/saturate/unit.test.ts +59 -0
- package/src/schemas/functions/scale_chroma/scale-chroma.tokenscript +22 -0
- package/src/schemas/functions/scale_chroma/schema.json +48 -0
- package/src/schemas/functions/scale_chroma/unit.test.ts +79 -0
- package/src/schemas/functions/scale_lightness/scale-lightness.tokenscript +23 -0
- package/src/schemas/functions/scale_lightness/schema.json +48 -0
- package/src/schemas/functions/scale_lightness/unit.test.ts +73 -0
- package/src/schemas/functions/sepia/schema.json +48 -0
- package/src/schemas/functions/sepia/sepia.tokenscript +54 -0
- package/src/schemas/functions/sepia/unit.test.ts +88 -0
- package/src/schemas/functions/set_chroma/schema.json +24 -0
- package/src/schemas/functions/set_chroma/set-chroma.tokenscript +18 -0
- package/src/schemas/functions/set_chroma/unit.test.ts +79 -0
- package/src/schemas/functions/set_hue/schema.json +24 -0
- package/src/schemas/functions/set_hue/set-hue.tokenscript +18 -0
- package/src/schemas/functions/set_hue/unit.test.ts +90 -0
- package/src/schemas/functions/set_lightness/schema.json +24 -0
- package/src/schemas/functions/set_lightness/set-lightness.tokenscript +18 -0
- package/src/schemas/functions/set_lightness/unit.test.ts +80 -0
- package/src/schemas/functions/shade_scale/schema.json +24 -0
- package/src/schemas/functions/shade_scale/shade-scale.tokenscript +61 -0
- package/src/schemas/functions/shade_scale/unit.test.ts +64 -0
- package/src/schemas/functions/split_complement/schema.json +24 -0
- package/src/schemas/functions/split_complement/split-complement.tokenscript +38 -0
- package/src/schemas/functions/split_complement/unit.test.ts +53 -0
- package/src/schemas/functions/steps/schema.json +28 -0
- package/src/schemas/functions/steps/steps.tokenscript +54 -0
- package/src/schemas/functions/steps/unit.test.ts +71 -0
- package/src/schemas/functions/tetradic/schema.json +20 -0
- package/src/schemas/functions/tetradic/tetradic.tokenscript +40 -0
- package/src/schemas/functions/tetradic/unit.test.ts +50 -0
- package/src/schemas/functions/tint_scale/schema.json +32 -0
- package/src/schemas/functions/tint_scale/tint-scale.tokenscript +71 -0
- package/src/schemas/functions/tint_scale/unit.test.ts +64 -0
- package/src/schemas/functions/to_gamut/schema.json +48 -0
- package/src/schemas/functions/to_gamut/to-gamut.tokenscript +96 -0
- package/src/schemas/functions/to_gamut/unit.test.ts +97 -0
- package/src/schemas/functions/triadic/schema.json +20 -0
- package/src/schemas/functions/triadic/triadic.tokenscript +33 -0
- package/src/schemas/functions/triadic/unit.test.ts +64 -0
- package/src/schemas/functions/vibrant/schema.json +48 -0
- package/src/schemas/functions/vibrant/unit.test.ts +55 -0
- package/src/schemas/functions/vibrant/vibrant.tokenscript +29 -0
- package/src/schemas/functions/warmer/schema.json +43 -0
- package/src/schemas/functions/warmer/unit.test.ts +69 -0
- package/src/schemas/functions/warmer/warmer.tokenscript +45 -0
- package/src/schemas/functions/wcag_level/schema.json +48 -0
- package/src/schemas/functions/wcag_level/unit.test.ts +75 -0
- package/src/schemas/functions/wcag_level/wcag-level.tokenscript +50 -0
- package/src/schemas/types/css-color/from-hsl-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-hwb-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-lab-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-lch-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-oklab-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-oklch-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-p3-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-rgb-color.tokenscript +15 -0
- package/src/schemas/types/css-color/from-srgb-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-srgb-linear-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-xyz-d50-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-xyz-d65-color.tokenscript +16 -0
- package/src/schemas/types/css-color/initializer.tokenscript +13 -0
- package/src/schemas/types/css-color/schema.json +148 -0
- package/src/schemas/types/css-color/unit.test.ts +402 -0
- package/src/schemas/types/hex-color/initializer.tokenscript +3 -0
- package/src/schemas/types/hex-color/schema.json +24 -0
- package/src/schemas/types/hex-color/unit.test.ts +123 -0
- package/src/schemas/types/hsl-color/from-srgb.tokenscript +87 -0
- package/src/schemas/types/hsl-color/initializer.tokenscript +16 -0
- package/src/schemas/types/hsl-color/schema.json +48 -0
- package/src/schemas/types/hsl-color/unit.test.ts +201 -0
- package/src/schemas/types/hsv-color/from-srgb.tokenscript +80 -0
- package/src/schemas/types/hsv-color/initializer.tokenscript +16 -0
- package/src/schemas/types/hsv-color/schema.json +48 -0
- package/src/schemas/types/hsv-color/unit.test.ts +162 -0
- package/src/schemas/types/hwb-color/from-hsv.tokenscript +31 -0
- package/src/schemas/types/hwb-color/initializer.tokenscript +16 -0
- package/src/schemas/types/hwb-color/schema.json +48 -0
- package/src/schemas/types/hwb-color/unit.test.ts +150 -0
- package/src/schemas/types/lab-color/from-xyz-d50.tokenscript +78 -0
- package/src/schemas/types/lab-color/initializer.tokenscript +16 -0
- package/src/schemas/types/lab-color/schema.json +48 -0
- package/src/schemas/types/lab-color/unit.test.ts +263 -0
- package/src/schemas/types/lch-color/from-lab.tokenscript +44 -0
- package/src/schemas/types/lch-color/initializer.tokenscript +16 -0
- package/src/schemas/types/lch-color/schema.json +48 -0
- package/src/schemas/types/lch-color/unit.test.ts +173 -0
- package/src/schemas/types/okhsl-color/from-oklab.tokenscript +410 -0
- package/src/schemas/types/okhsl-color/initializer.tokenscript +24 -0
- package/src/schemas/types/okhsl-color/schema.json +48 -0
- package/src/schemas/types/okhsl-color/unit.test.ts +514 -0
- package/src/schemas/types/okhsv-color/from-oklab.tokenscript +286 -0
- package/src/schemas/types/okhsv-color/initializer.tokenscript +24 -0
- package/src/schemas/types/okhsv-color/schema.json +48 -0
- package/src/schemas/types/okhsv-color/unit.test.ts +499 -0
- package/src/schemas/types/oklab-color/from-okhsl.tokenscript +195 -0
- package/src/schemas/types/oklab-color/from-okhsv.tokenscript +197 -0
- package/src/schemas/types/oklab-color/from-oklch.tokenscript +39 -0
- package/src/schemas/types/oklab-color/from-xyz-d65.tokenscript +43 -0
- package/src/schemas/types/oklab-color/initializer.tokenscript +16 -0
- package/src/schemas/types/oklab-color/schema.json +78 -0
- package/src/schemas/types/oklab-color/unit.test.ts +345 -0
- package/src/schemas/types/oklch-color/from-oklab.tokenscript +45 -0
- package/src/schemas/types/oklch-color/initializer.tokenscript +16 -0
- package/src/schemas/types/oklch-color/schema.json +48 -0
- package/src/schemas/types/oklch-color/unit.test.ts +267 -0
- package/src/schemas/types/p3-color/from-p3-linear.tokenscript +59 -0
- package/src/schemas/types/p3-color/initializer.tokenscript +16 -0
- package/src/schemas/types/p3-color/schema.json +48 -0
- package/src/schemas/types/p3-color/unit.test.ts +119 -0
- package/src/schemas/types/p3-linear-color/from-xyz-d65.tokenscript +47 -0
- package/src/schemas/types/p3-linear-color/initializer.tokenscript +16 -0
- package/src/schemas/types/p3-linear-color/schema.json +48 -0
- package/src/schemas/types/p3-linear-color/unit.test.ts +82 -0
- package/src/schemas/types/rgb-color/from-hex.tokenscript +43 -0
- package/src/schemas/types/rgb-color/initializer.tokenscript +16 -0
- package/src/schemas/types/rgb-color/schema.json +55 -0
- package/src/schemas/types/rgb-color/to-hex.tokenscript +42 -0
- package/src/schemas/types/rgb-color/unit.test.ts +302 -0
- package/src/schemas/types/srgb-color/from-hsl.tokenscript +106 -0
- package/src/schemas/types/srgb-color/from-linear.tokenscript +58 -0
- package/src/schemas/types/srgb-color/from-rgb.tokenscript +20 -0
- package/src/schemas/types/srgb-color/initializer.tokenscript +16 -0
- package/src/schemas/types/srgb-color/schema.json +68 -0
- package/src/schemas/types/srgb-color/unit.test.ts +303 -0
- package/src/schemas/types/srgb-linear-color/from-srgb.tokenscript +55 -0
- package/src/schemas/types/srgb-linear-color/from-xyz-d65.tokenscript +34 -0
- package/src/schemas/types/srgb-linear-color/initializer.tokenscript +13 -0
- package/src/schemas/types/srgb-linear-color/schema.json +58 -0
- package/src/schemas/types/srgb-linear-color/unit.test.ts +291 -0
- package/src/schemas/types/xyz-d50-color/from-xyz-d65.tokenscript +36 -0
- package/src/schemas/types/xyz-d50-color/initializer.tokenscript +16 -0
- package/src/schemas/types/xyz-d50-color/schema.json +48 -0
- package/src/schemas/types/xyz-d50-color/unit.test.ts +240 -0
- package/src/schemas/types/xyz-d65-color/from-linear-p3.tokenscript +47 -0
- package/src/schemas/types/xyz-d65-color/from-linear-srgb.tokenscript +38 -0
- package/src/schemas/types/xyz-d65-color/from-oklab.tokenscript +44 -0
- package/src/schemas/types/xyz-d65-color/initializer.tokenscript +16 -0
- package/src/schemas/types/xyz-d65-color/schema.json +68 -0
- package/src/schemas/types/xyz-d65-color/unit.test.ts +319 -0
- package/src/utils/schema-uri.ts +192 -0
- package/src/utils/type.ts +194 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema bundler - bundles schemas for distribution
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { bundleSchemaFromDirectory } from "@/bundler/bundle-schema";
|
|
8
|
+
import type {
|
|
9
|
+
BundledRegistry,
|
|
10
|
+
ColorSpecification,
|
|
11
|
+
FunctionSpecification,
|
|
12
|
+
SchemaSpecification,
|
|
13
|
+
} from "@/bundler/types.js";
|
|
14
|
+
import { getSubdirectories } from "@/bundler/utils";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Default registry URL for build-time bundling
|
|
18
|
+
*/
|
|
19
|
+
const DEFAULT_REGISTRY_URL = "https://schema.tokenscript.dev.gcp.tokens.studio";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Bundle a single schema from its directory
|
|
23
|
+
*/
|
|
24
|
+
async function bundleSchema(schemaDir: string, schemaSlug: string): Promise<SchemaSpecification> {
|
|
25
|
+
// Use shared bundling logic with baseUrl for build-time
|
|
26
|
+
const bundled = await bundleSchemaFromDirectory(schemaDir, {
|
|
27
|
+
baseUrl: DEFAULT_REGISTRY_URL,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Add slug from folder name
|
|
31
|
+
bundled.slug = schemaSlug;
|
|
32
|
+
|
|
33
|
+
return bundled;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Bundle all color type schemas from a category directory
|
|
38
|
+
*/
|
|
39
|
+
async function bundleTypeCategory(categoryDir: string): Promise<ColorSpecification[]> {
|
|
40
|
+
const bundles: ColorSpecification[] = [];
|
|
41
|
+
const schemaSlugs = await getSubdirectories(categoryDir);
|
|
42
|
+
|
|
43
|
+
for (const slug of schemaSlugs) {
|
|
44
|
+
const schemaDir = join(categoryDir, slug);
|
|
45
|
+
console.log(` Bundling ${slug}...`);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const bundle = await bundleSchema(schemaDir, slug);
|
|
49
|
+
if (bundle.type === "color") {
|
|
50
|
+
bundles.push(bundle as ColorSpecification);
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error(` ✗ Failed to bundle ${slug}:`, error);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return bundles;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Bundle all function schemas from a category directory
|
|
62
|
+
*/
|
|
63
|
+
async function bundleFunctionCategory(categoryDir: string): Promise<FunctionSpecification[]> {
|
|
64
|
+
const bundles: FunctionSpecification[] = [];
|
|
65
|
+
const schemaSlugs = await getSubdirectories(categoryDir);
|
|
66
|
+
|
|
67
|
+
for (const slug of schemaSlugs) {
|
|
68
|
+
const schemaDir = join(categoryDir, slug);
|
|
69
|
+
console.log(` Bundling ${slug}...`);
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const bundle = await bundleSchema(schemaDir, slug);
|
|
73
|
+
if (bundle.type === "function") {
|
|
74
|
+
bundles.push(bundle as FunctionSpecification);
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error(` ✗ Failed to bundle ${slug}:`, error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return bundles;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Bundle all schemas from the schemas directory
|
|
86
|
+
*/
|
|
87
|
+
export async function bundleAllSchemas(
|
|
88
|
+
schemasDir: string,
|
|
89
|
+
outputDir: string,
|
|
90
|
+
): Promise<BundledRegistry> {
|
|
91
|
+
// Bundle types
|
|
92
|
+
console.log("\nBundling type schemas...");
|
|
93
|
+
const typesDir = join(schemasDir, "types");
|
|
94
|
+
const types = await bundleTypeCategory(typesDir);
|
|
95
|
+
console.log(`✓ Bundled ${types.length} type schemas`);
|
|
96
|
+
|
|
97
|
+
// Bundle functions
|
|
98
|
+
console.log("\nBundling function schemas...");
|
|
99
|
+
const functionsDir = join(schemasDir, "functions");
|
|
100
|
+
const functions = await bundleFunctionCategory(functionsDir);
|
|
101
|
+
console.log(`✓ Bundled ${functions.length} function schemas`);
|
|
102
|
+
|
|
103
|
+
// Create bundled registry
|
|
104
|
+
const registry: BundledRegistry = {
|
|
105
|
+
version: "0.0.10",
|
|
106
|
+
types,
|
|
107
|
+
functions,
|
|
108
|
+
metadata: {
|
|
109
|
+
generatedAt: new Date().toISOString(),
|
|
110
|
+
totalSchemas: types.length + functions.length,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Ensure output directory exists
|
|
115
|
+
await mkdir(outputDir, { recursive: true });
|
|
116
|
+
|
|
117
|
+
// Write complete registry
|
|
118
|
+
const registryPath = join(outputDir, "registry.json");
|
|
119
|
+
await writeFile(registryPath, JSON.stringify(registry, null, 2));
|
|
120
|
+
console.log(`\n✓ Written complete registry to ${registryPath}`);
|
|
121
|
+
|
|
122
|
+
// Write individual category bundles
|
|
123
|
+
const typesPath = join(outputDir, "types.json");
|
|
124
|
+
await writeFile(typesPath, JSON.stringify({ version: registry.version, types }, null, 2));
|
|
125
|
+
console.log(`✓ Written types bundle to ${typesPath}`);
|
|
126
|
+
|
|
127
|
+
const functionsPath = join(outputDir, "functions.json");
|
|
128
|
+
await writeFile(functionsPath, JSON.stringify({ version: registry.version, functions }, null, 2));
|
|
129
|
+
console.log(`✓ Written functions bundle to ${functionsPath}`);
|
|
130
|
+
|
|
131
|
+
// Write individual schema bundles
|
|
132
|
+
const typesOutputDir = join(outputDir, "types");
|
|
133
|
+
await mkdir(typesOutputDir, { recursive: true });
|
|
134
|
+
for (const type of types) {
|
|
135
|
+
const typePath = join(typesOutputDir, `${type.slug}.json`);
|
|
136
|
+
await writeFile(typePath, JSON.stringify(type, null, 2));
|
|
137
|
+
}
|
|
138
|
+
console.log(`✓ Written ${types.length} individual type schemas`);
|
|
139
|
+
|
|
140
|
+
const functionsOutputDir = join(outputDir, "functions");
|
|
141
|
+
await mkdir(functionsOutputDir, { recursive: true });
|
|
142
|
+
for (const func of functions) {
|
|
143
|
+
const funcPath = join(functionsOutputDir, `${func.slug}.json`);
|
|
144
|
+
await writeFile(funcPath, JSON.stringify(func, null, 2));
|
|
145
|
+
}
|
|
146
|
+
console.log(`✓ Written ${functions.length} individual function schemas`);
|
|
147
|
+
|
|
148
|
+
return registry;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export type * from "@/bundler/types.js";
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for resolving schema dependencies automatically
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { log } from "@tests/helpers/logger.js";
|
|
7
|
+
import type {
|
|
8
|
+
ColorSpecification,
|
|
9
|
+
FunctionSpecification,
|
|
10
|
+
SchemaSpecification,
|
|
11
|
+
} from "@/bundler/types.js";
|
|
12
|
+
import { extractSchemaName, parseSchemaUri } from "@/utils/schema-uri";
|
|
13
|
+
import { bundleSchemaFromDirectory } from "./bundle-schema.js";
|
|
14
|
+
|
|
15
|
+
export interface SchemaReference {
|
|
16
|
+
slug: string;
|
|
17
|
+
type: "type" | "function";
|
|
18
|
+
uri: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ExtractRequirementsOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Whether to include type dependencies from color conversions
|
|
24
|
+
* Useful for testing to ensure all conversion types are loaded
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
includeColorTypeDependencies?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Extract all required schema URIs from a schema specification
|
|
32
|
+
* Only extracts explicit requirements - not conversions (which are capabilities, not dependencies)
|
|
33
|
+
* However, for testing purposes, can optionally include color type dependencies from conversions
|
|
34
|
+
*/
|
|
35
|
+
function extractRequirements(
|
|
36
|
+
spec: SchemaSpecification,
|
|
37
|
+
options: ExtractRequirementsOptions = {},
|
|
38
|
+
): string[] {
|
|
39
|
+
const requirements: string[] = [];
|
|
40
|
+
|
|
41
|
+
if (spec.type === "function") {
|
|
42
|
+
// Functions can have explicit requirements
|
|
43
|
+
const funcSpec = spec as FunctionSpecification;
|
|
44
|
+
if (funcSpec.requirements) {
|
|
45
|
+
requirements.push(...funcSpec.requirements);
|
|
46
|
+
}
|
|
47
|
+
} else if (spec.type === "color" && options.includeColorTypeDependencies) {
|
|
48
|
+
// Color types have requirements through conversions (when flag is enabled)
|
|
49
|
+
// This is useful for testing to ensure all conversion types are loaded
|
|
50
|
+
const colorSpec = spec as ColorSpecification;
|
|
51
|
+
for (const conversion of colorSpec.conversions || []) {
|
|
52
|
+
// Add source if it's not $self
|
|
53
|
+
if (conversion.source !== "$self") {
|
|
54
|
+
requirements.push(conversion.source);
|
|
55
|
+
}
|
|
56
|
+
// Add target if it's not $self
|
|
57
|
+
if (conversion.target !== "$self") {
|
|
58
|
+
requirements.push(conversion.target);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Note: By default, color types don't have dependencies via conversions
|
|
63
|
+
// Conversions are capabilities, not requirements
|
|
64
|
+
|
|
65
|
+
return requirements;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Resolve a URI to a schema slug and type
|
|
70
|
+
* Works with both full URIs and just schema names
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* resolveSchemaReference("/api/v1/core/rgb-color/0/") => { slug: "rgb-color", type: "type" }
|
|
74
|
+
* resolveSchemaReference("rgb-color") => { slug: "rgb-color", type: "type" }
|
|
75
|
+
*/
|
|
76
|
+
export function resolveSchemaReference(uriOrName: string): SchemaReference | null {
|
|
77
|
+
// Try parsing as URI first
|
|
78
|
+
const components = parseSchemaUri(uriOrName);
|
|
79
|
+
|
|
80
|
+
if (components) {
|
|
81
|
+
// Successfully parsed as URI
|
|
82
|
+
const type = components.category === "function" ? "function" : "type";
|
|
83
|
+
return {
|
|
84
|
+
slug: components.name,
|
|
85
|
+
type,
|
|
86
|
+
uri: uriOrName,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Try extracting name (handles partial URIs)
|
|
91
|
+
const extractedName = extractSchemaName(uriOrName);
|
|
92
|
+
if (extractedName) {
|
|
93
|
+
return {
|
|
94
|
+
slug: extractedName,
|
|
95
|
+
type: "type", // Default to type if we can't determine
|
|
96
|
+
uri: uriOrName,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Treat as plain slug
|
|
101
|
+
if (uriOrName && !uriOrName.includes("/")) {
|
|
102
|
+
return {
|
|
103
|
+
slug: uriOrName,
|
|
104
|
+
type: "type", // Default to type
|
|
105
|
+
uri: "", // No URI, just a slug
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ResolvedDependencies {
|
|
113
|
+
types: string[];
|
|
114
|
+
functions: string[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface DependencyNode {
|
|
118
|
+
slug: string;
|
|
119
|
+
type: "type" | "function";
|
|
120
|
+
dependencies: string[];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface CollectRequiredSchemasOptions extends ExtractRequirementsOptions {
|
|
124
|
+
baseUrl?: string;
|
|
125
|
+
schemasDir?: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Recursively collect all required schemas for a given schema
|
|
130
|
+
* Returns a flat list of all dependencies (including transitive ones)
|
|
131
|
+
*
|
|
132
|
+
* @param slugOrUri - Schema slug (e.g., "rgb-color") or full URI
|
|
133
|
+
* @param type - Schema type ("type" or "function"), optional if URI is provided
|
|
134
|
+
* @param options - Options for dependency collection
|
|
135
|
+
* @returns Object with separated type and function dependencies
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* // Using slug
|
|
139
|
+
* await collectRequiredSchemas("invert", "function")
|
|
140
|
+
* // => { types: ["rgb-color", "hex-color"], functions: [] }
|
|
141
|
+
*
|
|
142
|
+
* // Using URI
|
|
143
|
+
* await collectRequiredSchemas("/api/v1/core/rgb-color/0/")
|
|
144
|
+
* // => { types: ["hex-color"], functions: [] }
|
|
145
|
+
*/
|
|
146
|
+
export async function collectRequiredSchemas(
|
|
147
|
+
slugOrUri: string,
|
|
148
|
+
type?: "type" | "function",
|
|
149
|
+
options: CollectRequiredSchemasOptions = {},
|
|
150
|
+
): Promise<ResolvedDependencies> {
|
|
151
|
+
const { baseUrl, schemasDir, ...extractOptions } = options;
|
|
152
|
+
const visited = new Set<string>();
|
|
153
|
+
const typeSchemas = new Set<string>();
|
|
154
|
+
const functionSchemas = new Set<string>();
|
|
155
|
+
|
|
156
|
+
async function traverse(currentSlugOrUri: string, currentType?: "type" | "function") {
|
|
157
|
+
// Resolve to a proper schema reference
|
|
158
|
+
const ref = resolveSchemaReference(currentSlugOrUri);
|
|
159
|
+
if (!ref) {
|
|
160
|
+
log.warn(`Could not resolve schema reference: ${currentSlugOrUri}`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Use provided type if available, otherwise use resolved type
|
|
165
|
+
const effectiveType = currentType || ref.type;
|
|
166
|
+
const slug = ref.slug;
|
|
167
|
+
|
|
168
|
+
// Create a unique key for visited tracking
|
|
169
|
+
const key = `${effectiveType}:${slug}`;
|
|
170
|
+
if (visited.has(key)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
visited.add(key);
|
|
174
|
+
|
|
175
|
+
// Try to load the schema
|
|
176
|
+
let spec: SchemaSpecification;
|
|
177
|
+
try {
|
|
178
|
+
// Dynamically determine schema directory
|
|
179
|
+
const categoryDir = effectiveType === "type" ? "types" : "functions";
|
|
180
|
+
const resolvedSchemasDir =
|
|
181
|
+
schemasDir || process.env.SCHEMAS_DIR || join(process.cwd(), "src/schemas");
|
|
182
|
+
const schemaDir = join(resolvedSchemasDir, categoryDir, slug);
|
|
183
|
+
|
|
184
|
+
spec = await bundleSchemaFromDirectory(schemaDir, baseUrl ? { baseUrl } : undefined);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
log.warn(`Failed to load schema ${slug} (${effectiveType}):`, error);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Extract requirements from this schema
|
|
191
|
+
const requirements = extractRequirements(spec, extractOptions);
|
|
192
|
+
|
|
193
|
+
// Recursively traverse requirements
|
|
194
|
+
for (const reqUri of requirements) {
|
|
195
|
+
const reqRef = resolveSchemaReference(reqUri);
|
|
196
|
+
if (reqRef) {
|
|
197
|
+
// Add to appropriate set
|
|
198
|
+
if (reqRef.type === "function") {
|
|
199
|
+
functionSchemas.add(reqRef.slug);
|
|
200
|
+
} else {
|
|
201
|
+
typeSchemas.add(reqRef.slug);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Recursively collect dependencies
|
|
205
|
+
await traverse(reqUri, reqRef.type);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Start traversal
|
|
211
|
+
await traverse(slugOrUri, type);
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
types: Array.from(typeSchemas),
|
|
215
|
+
functions: Array.from(functionSchemas),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Collect all schemas needed for a list of schemas (including their dependencies)
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* await collectRequiredSchemasForList([
|
|
224
|
+
* { slug: "invert", type: "function" },
|
|
225
|
+
* { slug: "rgb-color", type: "type" }
|
|
226
|
+
* ])
|
|
227
|
+
* // => { types: ["rgb-color", "hex-color"], functions: ["invert"] }
|
|
228
|
+
*/
|
|
229
|
+
export async function collectRequiredSchemasForList(
|
|
230
|
+
schemas: Array<{ slug: string; type: "type" | "function" }>,
|
|
231
|
+
options: CollectRequiredSchemasOptions = {},
|
|
232
|
+
): Promise<ResolvedDependencies> {
|
|
233
|
+
const allTypes = new Set<string>();
|
|
234
|
+
const allFunctions = new Set<string>();
|
|
235
|
+
|
|
236
|
+
for (const schema of schemas) {
|
|
237
|
+
const deps = await collectRequiredSchemas(schema.slug, schema.type, options);
|
|
238
|
+
|
|
239
|
+
// Add the schema itself
|
|
240
|
+
if (schema.type === "function") {
|
|
241
|
+
allFunctions.add(schema.slug);
|
|
242
|
+
} else {
|
|
243
|
+
allTypes.add(schema.slug);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Add dependencies
|
|
247
|
+
for (const t of deps.types) {
|
|
248
|
+
allTypes.add(t);
|
|
249
|
+
}
|
|
250
|
+
for (const f of deps.functions) {
|
|
251
|
+
allFunctions.add(f);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
types: Array.from(allTypes),
|
|
257
|
+
functions: Array.from(allFunctions),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Collect dependency tree for schemas (non-recursive, shows direct dependencies only)
|
|
263
|
+
*/
|
|
264
|
+
export async function collectDependencyTree(
|
|
265
|
+
schemas: Array<{ slug: string; type: "type" | "function" }>,
|
|
266
|
+
options: CollectRequiredSchemasOptions = {},
|
|
267
|
+
): Promise<Map<string, DependencyNode>> {
|
|
268
|
+
const { baseUrl, schemasDir, ...extractOptions } = options;
|
|
269
|
+
const tree = new Map<string, DependencyNode>();
|
|
270
|
+
|
|
271
|
+
for (const schema of schemas) {
|
|
272
|
+
const categoryDir = schema.type === "type" ? "types" : "functions";
|
|
273
|
+
const resolvedSchemasDir =
|
|
274
|
+
schemasDir || process.env.SCHEMAS_DIR || join(process.cwd(), "src/schemas");
|
|
275
|
+
const schemaDir = join(resolvedSchemasDir, categoryDir, schema.slug);
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
const spec = await bundleSchemaFromDirectory(schemaDir, baseUrl ? { baseUrl } : undefined);
|
|
279
|
+
const requirements = extractRequirements(spec, extractOptions);
|
|
280
|
+
|
|
281
|
+
// Extract just the slugs from URIs
|
|
282
|
+
const dependencySlugs = requirements.map((uri) => {
|
|
283
|
+
const ref = resolveSchemaReference(uri);
|
|
284
|
+
return ref ? `${ref.type}:${ref.slug}` : uri;
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const key = `${schema.type}:${schema.slug}`;
|
|
288
|
+
tree.set(key, {
|
|
289
|
+
slug: schema.slug,
|
|
290
|
+
type: schema.type,
|
|
291
|
+
dependencies: dependencySlugs,
|
|
292
|
+
});
|
|
293
|
+
} catch (error) {
|
|
294
|
+
log.warn(`Failed to load schema ${schema.slug} (${schema.type}):`, error);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return tree;
|
|
299
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { bundleSelectiveSchemas } from "./selective-bundler.js";
|
|
4
|
+
|
|
5
|
+
const SCHEMAS_DIR = join(process.cwd(), "src/schemas");
|
|
6
|
+
|
|
7
|
+
describe("Selective Bundler", () => {
|
|
8
|
+
it("should bundle a single type schema", async () => {
|
|
9
|
+
const result = await bundleSelectiveSchemas({
|
|
10
|
+
schemas: ["hex-color"],
|
|
11
|
+
schemasDir: SCHEMAS_DIR,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
expect(result.schemas).toHaveLength(1);
|
|
15
|
+
expect(result.schemas[0].uri).toContain("hex-color");
|
|
16
|
+
expect(result.schemas[0].schema.type).toBe("color");
|
|
17
|
+
expect(result.metadata.requestedSchemas).toEqual(["hex-color"]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should bundle multiple type schemas with dependencies", async () => {
|
|
21
|
+
const result = await bundleSelectiveSchemas({
|
|
22
|
+
schemas: ["rgb-color", "oklch-color"],
|
|
23
|
+
schemasDir: SCHEMAS_DIR,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Should include requested schemas plus dependencies
|
|
27
|
+
expect(result.schemas.length).toBeGreaterThan(2);
|
|
28
|
+
|
|
29
|
+
// Check that requested schemas are included
|
|
30
|
+
const uris = result.schemas.map((s) => s.uri);
|
|
31
|
+
expect(uris.some((uri) => uri.includes("rgb-color"))).toBe(true);
|
|
32
|
+
expect(uris.some((uri) => uri.includes("oklch-color"))).toBe(true);
|
|
33
|
+
|
|
34
|
+
// Check that hex-color (a common dependency) is included
|
|
35
|
+
expect(uris.some((uri) => uri.includes("hex-color"))).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should bundle function schema with type dependencies", async () => {
|
|
39
|
+
const result = await bundleSelectiveSchemas({
|
|
40
|
+
schemas: ["function:invert"],
|
|
41
|
+
schemasDir: SCHEMAS_DIR,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const uris = result.schemas.map((s) => s.uri);
|
|
45
|
+
|
|
46
|
+
// Should include the function itself
|
|
47
|
+
expect(uris.some((uri) => uri.includes("/function/invert/"))).toBe(true);
|
|
48
|
+
|
|
49
|
+
// Should include type dependencies (rgb-color, hex-color)
|
|
50
|
+
expect(uris.some((uri) => uri.includes("rgb-color"))).toBe(true);
|
|
51
|
+
expect(uris.some((uri) => uri.includes("hex-color"))).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("should inline script content", async () => {
|
|
55
|
+
const result = await bundleSelectiveSchemas({
|
|
56
|
+
schemas: ["hex-color"],
|
|
57
|
+
schemasDir: SCHEMAS_DIR,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const hexSchema = result.schemas[0].schema;
|
|
61
|
+
|
|
62
|
+
if (hexSchema.type === "color") {
|
|
63
|
+
// Check that initializer script is inlined (not a file reference)
|
|
64
|
+
const initScript = hexSchema.initializers[0].script.script;
|
|
65
|
+
expect(initScript).not.toContain("./");
|
|
66
|
+
expect(initScript.length).toBeGreaterThan(10);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should use provided base URL in URIs", async () => {
|
|
71
|
+
const customBaseUrl = "https://custom.example.com";
|
|
72
|
+
|
|
73
|
+
const result = await bundleSelectiveSchemas({
|
|
74
|
+
schemas: ["hex-color"],
|
|
75
|
+
schemasDir: SCHEMAS_DIR,
|
|
76
|
+
baseUrl: customBaseUrl,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(result.schemas[0].uri).toContain(customBaseUrl);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should include metadata", async () => {
|
|
83
|
+
const result = await bundleSelectiveSchemas({
|
|
84
|
+
schemas: ["rgb-color"],
|
|
85
|
+
schemasDir: SCHEMAS_DIR,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(result.metadata.requestedSchemas).toEqual(["rgb-color"]);
|
|
89
|
+
expect(result.metadata.resolvedDependencies).toContain("rgb-color");
|
|
90
|
+
expect(result.metadata.resolvedDependencies).toContain("hex-color");
|
|
91
|
+
expect(result.metadata.generatedAt).toBeDefined();
|
|
92
|
+
expect(new Date(result.metadata.generatedAt).getTime()).toBeGreaterThan(0);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selective schema bundler for CLI
|
|
3
|
+
* Bundles specific schemas with automatic dependency resolution
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { access } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { bundleSchemaFromDirectory } from "./bundle-schema.js";
|
|
9
|
+
import {
|
|
10
|
+
collectDependencyTree,
|
|
11
|
+
collectRequiredSchemasForList,
|
|
12
|
+
type DependencyNode,
|
|
13
|
+
} from "./schema-dependency-resolver.js";
|
|
14
|
+
import type { ColorSpecification, FunctionSpecification } from "./types.js";
|
|
15
|
+
|
|
16
|
+
export interface SelectiveBundleOptions {
|
|
17
|
+
schemas: string[]; // Schema slugs to bundle
|
|
18
|
+
schemasDir?: string; // Source directory (default: src/schemas)
|
|
19
|
+
baseUrl?: string; // Registry URL for URIs
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface BundledSchemaEntry {
|
|
23
|
+
uri: string;
|
|
24
|
+
schema: ColorSpecification | FunctionSpecification;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SelectiveBundleResult {
|
|
28
|
+
schemas: BundledSchemaEntry[];
|
|
29
|
+
metadata: {
|
|
30
|
+
requestedSchemas: string[];
|
|
31
|
+
resolvedDependencies: string[];
|
|
32
|
+
generatedAt: string;
|
|
33
|
+
};
|
|
34
|
+
dependencyTree: Map<string, DependencyNode>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Detect whether a schema is a type or function by checking which directory exists
|
|
39
|
+
*/
|
|
40
|
+
async function detectSchemaType(
|
|
41
|
+
slug: string,
|
|
42
|
+
schemasDir: string,
|
|
43
|
+
): Promise<"type" | "function" | null> {
|
|
44
|
+
const typeDir = join(schemasDir, "types", slug);
|
|
45
|
+
const functionDir = join(schemasDir, "functions", slug);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await access(typeDir);
|
|
49
|
+
return "type";
|
|
50
|
+
} catch {
|
|
51
|
+
// Not a type, try function
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
await access(functionDir);
|
|
56
|
+
return "function";
|
|
57
|
+
} catch {
|
|
58
|
+
// Not found in either
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Bundle specific schemas with automatic dependency resolution
|
|
66
|
+
*/
|
|
67
|
+
export async function bundleSelectiveSchemas(
|
|
68
|
+
options: SelectiveBundleOptions,
|
|
69
|
+
): Promise<SelectiveBundleResult> {
|
|
70
|
+
const schemasDir = options.schemasDir || join(process.cwd(), "src/schemas");
|
|
71
|
+
const baseUrl = options.baseUrl || "https://schema.tokenscript.dev.gcp.tokens.studio";
|
|
72
|
+
|
|
73
|
+
// Parse schema slugs - they might have type prefixes like "function:invert"
|
|
74
|
+
const parsedSchemas = await Promise.all(
|
|
75
|
+
options.schemas.map(async (slug) => {
|
|
76
|
+
if (slug.includes(":")) {
|
|
77
|
+
const [type, name] = slug.split(":");
|
|
78
|
+
return {
|
|
79
|
+
slug: name,
|
|
80
|
+
type: (type === "function" ? "function" : "type") as "type" | "function",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Auto-detect type by checking which directory exists
|
|
85
|
+
const detectedType = await detectSchemaType(slug, schemasDir);
|
|
86
|
+
if (detectedType === null) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Schema '${slug}' not found in types or functions directories. ` +
|
|
89
|
+
`Use 'function:${slug}' or 'type:${slug}' prefix to be explicit.`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { slug, type: detectedType };
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Collect all required schemas (including dependencies)
|
|
98
|
+
// For CLI bundling, we include color type dependencies so conversions work
|
|
99
|
+
const deps = await collectRequiredSchemasForList(parsedSchemas, {
|
|
100
|
+
baseUrl,
|
|
101
|
+
schemasDir,
|
|
102
|
+
includeColorTypeDependencies: true,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Collect dependency tree for all schemas (including resolved dependencies)
|
|
106
|
+
const allParsedSchemas = [
|
|
107
|
+
...deps.types.map((slug) => ({ slug, type: "type" as const })),
|
|
108
|
+
...deps.functions.map((slug) => ({ slug, type: "function" as const })),
|
|
109
|
+
];
|
|
110
|
+
const dependencyTree = await collectDependencyTree(allParsedSchemas, {
|
|
111
|
+
baseUrl,
|
|
112
|
+
schemasDir,
|
|
113
|
+
includeColorTypeDependencies: true,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Track all schema slugs for metadata
|
|
117
|
+
const allSchemas = [...new Set([...deps.types, ...deps.functions])];
|
|
118
|
+
|
|
119
|
+
// Bundle all schemas
|
|
120
|
+
const bundledSchemas: BundledSchemaEntry[] = [];
|
|
121
|
+
|
|
122
|
+
// Bundle type schemas
|
|
123
|
+
for (const typeSlug of deps.types) {
|
|
124
|
+
const schemaDir = join(schemasDir, "types", typeSlug);
|
|
125
|
+
const bundled = await bundleSchemaFromDirectory(schemaDir, { baseUrl });
|
|
126
|
+
|
|
127
|
+
if (bundled.type === "color") {
|
|
128
|
+
const uri = `${baseUrl}/api/v1/core/${typeSlug}/0/`;
|
|
129
|
+
bundledSchemas.push({
|
|
130
|
+
uri,
|
|
131
|
+
schema: bundled as ColorSpecification,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Bundle function schemas
|
|
137
|
+
for (const funcSlug of deps.functions) {
|
|
138
|
+
const schemaDir = join(schemasDir, "functions", funcSlug);
|
|
139
|
+
const bundled = await bundleSchemaFromDirectory(schemaDir, { baseUrl });
|
|
140
|
+
|
|
141
|
+
if (bundled.type === "function") {
|
|
142
|
+
const uri = `${baseUrl}/api/v1/function/${funcSlug}/0/`;
|
|
143
|
+
bundledSchemas.push({
|
|
144
|
+
uri,
|
|
145
|
+
schema: bundled as FunctionSpecification,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
schemas: bundledSchemas,
|
|
152
|
+
metadata: {
|
|
153
|
+
requestedSchemas: options.schemas,
|
|
154
|
+
resolvedDependencies: allSchemas,
|
|
155
|
+
generatedAt: new Date().toISOString(),
|
|
156
|
+
},
|
|
157
|
+
dependencyTree,
|
|
158
|
+
};
|
|
159
|
+
}
|