@c-time/frelio-dependency-map 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # frelio-dependency-map
2
+
3
+ Type definitions and utilities for frelio dependency map files.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install frelio-dependency-map
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Basic Usage
14
+
15
+ ```typescript
16
+ import { DependencyMap, isDependencyMap } from 'frelio-dependency-map'
17
+ import fs from 'fs'
18
+
19
+ // Load a dependency map file
20
+ const data = JSON.parse(fs.readFileSync('_dependency-map.json', 'utf-8'))
21
+
22
+ // Validate the data
23
+ if (isDependencyMap(data)) {
24
+ // data is now typed as DependencyMap
25
+ console.log('Output paths:', Object.keys(data))
26
+ }
27
+ ```
28
+
29
+ ### Validation with Detailed Errors
30
+
31
+ ```typescript
32
+ import { validateDependencyMap } from 'frelio-dependency-map'
33
+
34
+ const result = validateDependencyMap(data)
35
+ if (!result.valid) {
36
+ console.error('Validation errors:', result.errors)
37
+ }
38
+ ```
39
+
40
+ ### Finding Affected Outputs
41
+
42
+ ```typescript
43
+ import { findAffectedOutputs, DependencyMap } from 'frelio-dependency-map'
44
+
45
+ const map: DependencyMap = {
46
+ '/articles/{slug}.json': ['content_types/article', 'contents/*/article/*'],
47
+ }
48
+
49
+ // Find which outputs need rebuilding when a file changes
50
+ const affected = findAffectedOutputs(
51
+ map,
52
+ 'contents/published/article/my-post.json'
53
+ )
54
+ console.log(affected) // ['/articles/{slug}.json']
55
+ ```
56
+
57
+ ### JSON Schema Validation
58
+
59
+ ```typescript
60
+ import { dependencyMapSchema } from 'frelio-dependency-map/schema'
61
+ import Ajv from 'ajv'
62
+
63
+ const ajv = new Ajv()
64
+ const validate = ajv.compile(dependencyMapSchema)
65
+
66
+ if (!validate(data)) {
67
+ console.error(validate.errors)
68
+ }
69
+ ```
70
+
71
+ ## API
72
+
73
+ ### Types
74
+
75
+ - `DependencyMap` - Main type for dependency map objects
76
+ - `OutputPath` - Type alias for output path strings
77
+ - `DependencyPath` - Type alias for dependency path strings
78
+ - `ValidationResult` - Result of validation operations
79
+ - `ValidationError` - Individual validation error
80
+
81
+ ### Functions
82
+
83
+ - `isDependencyMap(value)` - Type guard for DependencyMap
84
+ - `validateDependencyMap(value)` - Validates with detailed errors
85
+ - `getDependencyPaths(map, outputPath)` - Get dependencies for an output
86
+ - `getOutputPaths(map)` - Get all output paths
87
+ - `getAllDependencyPaths(map)` - Get all unique dependency paths
88
+ - `mergeDependencyMaps(...maps)` - Merge multiple maps
89
+ - `findAffectedOutputs(map, changedPath)` - Find outputs affected by a change
90
+ - `createDependencyMap()` - Create an empty dependency map
91
+ - `setDependencies(map, outputPath, deps)` - Set dependencies for an output
92
+ - `removeDependencies(map, outputPath)` - Remove an output from the map
93
+
94
+ ### Constants
95
+
96
+ - `DEPENDENCY_PATH_PREFIXES` - Common path prefixes
97
+ - `dependencyMapSchema` - JSON Schema for validation
98
+
99
+ ## License
100
+
101
+ MIT
@@ -0,0 +1,9 @@
1
+ import type { DependencyMap, ValidationResult } from './types.js';
2
+ /**
3
+ * Type guard to check if a value is a valid DependencyMap
4
+ */
5
+ export declare function isDependencyMap(value: unknown): value is DependencyMap;
6
+ /**
7
+ * Validates a value as a DependencyMap and returns detailed errors
8
+ */
9
+ export declare function validateDependencyMap(value: unknown): ValidationResult;
package/dist/guards.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Type guard to check if a value is a valid DependencyMap
3
+ */
4
+ export function isDependencyMap(value) {
5
+ if (value === null || typeof value !== 'object') {
6
+ return false;
7
+ }
8
+ const obj = value;
9
+ for (const [key, val] of Object.entries(obj)) {
10
+ // Key must be a non-empty string
11
+ if (typeof key !== 'string' || key.length === 0) {
12
+ return false;
13
+ }
14
+ // Value must be an array of strings
15
+ if (!Array.isArray(val)) {
16
+ return false;
17
+ }
18
+ for (const item of val) {
19
+ if (typeof item !== 'string') {
20
+ return false;
21
+ }
22
+ }
23
+ }
24
+ return true;
25
+ }
26
+ /**
27
+ * Validates a value as a DependencyMap and returns detailed errors
28
+ */
29
+ export function validateDependencyMap(value) {
30
+ const errors = [];
31
+ if (value === null) {
32
+ errors.push({ path: '', message: 'Value is null' });
33
+ return { valid: false, errors };
34
+ }
35
+ if (typeof value !== 'object') {
36
+ errors.push({ path: '', message: `Expected object, got ${typeof value}` });
37
+ return { valid: false, errors };
38
+ }
39
+ const obj = value;
40
+ for (const [key, val] of Object.entries(obj)) {
41
+ if (typeof key !== 'string' || key.length === 0) {
42
+ errors.push({
43
+ path: key,
44
+ message: 'Output path must be a non-empty string',
45
+ });
46
+ continue;
47
+ }
48
+ if (!Array.isArray(val)) {
49
+ errors.push({
50
+ path: key,
51
+ message: `Expected array of dependencies, got ${typeof val}`,
52
+ });
53
+ continue;
54
+ }
55
+ for (let i = 0; i < val.length; i++) {
56
+ const item = val[i];
57
+ if (typeof item !== 'string') {
58
+ errors.push({
59
+ path: `${key}[${i}]`,
60
+ message: `Expected string dependency path, got ${typeof item}`,
61
+ });
62
+ }
63
+ else if (item.length === 0) {
64
+ errors.push({
65
+ path: `${key}[${i}]`,
66
+ message: 'Dependency path cannot be empty',
67
+ });
68
+ }
69
+ }
70
+ }
71
+ return { valid: errors.length === 0, errors };
72
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * frelio-dependency-map
3
+ *
4
+ * Type definitions and utilities for frelio dependency map files.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import {
9
+ * DependencyMap,
10
+ * isDependencyMap,
11
+ * validateDependencyMap,
12
+ * findAffectedOutputs
13
+ * } from 'frelio-dependency-map'
14
+ *
15
+ * // Load and validate a dependency map
16
+ * const data = JSON.parse(fs.readFileSync('_dependency-map.json', 'utf-8'))
17
+ *
18
+ * if (isDependencyMap(data)) {
19
+ * // Type-safe access
20
+ * const affected = findAffectedOutputs(data, 'contents/published/article/my-post.json')
21
+ * console.log('Affected outputs:', affected)
22
+ * }
23
+ * ```
24
+ *
25
+ * @packageDocumentation
26
+ */
27
+ export type { DependencyMap, OutputPath, DependencyPath, DependencyPathPrefix, ValidationResult, ValidationError, } from './types.js';
28
+ export { DEPENDENCY_PATH_PREFIXES } from './types.js';
29
+ export { dependencyMapSchema } from './schema.js';
30
+ export type { DependencyMapSchema } from './schema.js';
31
+ export { isDependencyMap, validateDependencyMap } from './guards.js';
32
+ export { getDependencyPaths, getOutputPaths, getAllDependencyPaths, mergeDependencyMaps, findAffectedOutputs, createDependencyMap, setDependencies, removeDependencies, } from './utils.js';
package/dist/index.js ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * frelio-dependency-map
3
+ *
4
+ * Type definitions and utilities for frelio dependency map files.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import {
9
+ * DependencyMap,
10
+ * isDependencyMap,
11
+ * validateDependencyMap,
12
+ * findAffectedOutputs
13
+ * } from 'frelio-dependency-map'
14
+ *
15
+ * // Load and validate a dependency map
16
+ * const data = JSON.parse(fs.readFileSync('_dependency-map.json', 'utf-8'))
17
+ *
18
+ * if (isDependencyMap(data)) {
19
+ * // Type-safe access
20
+ * const affected = findAffectedOutputs(data, 'contents/published/article/my-post.json')
21
+ * console.log('Affected outputs:', affected)
22
+ * }
23
+ * ```
24
+ *
25
+ * @packageDocumentation
26
+ */
27
+ export { DEPENDENCY_PATH_PREFIXES } from './types.js';
28
+ // Schema
29
+ export { dependencyMapSchema } from './schema.js';
30
+ // Type guards & validation
31
+ export { isDependencyMap, validateDependencyMap } from './guards.js';
32
+ // Utilities
33
+ export { getDependencyPaths, getOutputPaths, getAllDependencyPaths, mergeDependencyMaps, findAffectedOutputs, createDependencyMap, setDependencies, removeDependencies, } from './utils.js';
@@ -0,0 +1,22 @@
1
+ /**
2
+ * JSON Schema for DependencyMap validation
3
+ */
4
+ export declare const dependencyMapSchema: {
5
+ readonly $schema: "http://json-schema.org/draft-07/schema#";
6
+ readonly title: "DependencyMap";
7
+ readonly description: "Maps output file paths to arrays of dependency paths for incremental builds";
8
+ readonly type: "object";
9
+ readonly additionalProperties: {
10
+ readonly type: "array";
11
+ readonly items: {
12
+ readonly type: "string";
13
+ readonly minLength: 1;
14
+ };
15
+ readonly description: "Array of dependency paths (glob patterns allowed)";
16
+ };
17
+ readonly examples: readonly [{
18
+ readonly '/articles/{slug}.json': readonly ["content_types/article", "templates/article/detail.html", "contents/*/article/*"];
19
+ readonly '/articles/index.json': readonly ["content_types/article", "templates/article/list.html", "contents/*/article/*"];
20
+ }];
21
+ };
22
+ export type DependencyMapSchema = typeof dependencyMapSchema;
package/dist/schema.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * JSON Schema for DependencyMap validation
3
+ */
4
+ export const dependencyMapSchema = {
5
+ $schema: 'http://json-schema.org/draft-07/schema#',
6
+ title: 'DependencyMap',
7
+ description: 'Maps output file paths to arrays of dependency paths for incremental builds',
8
+ type: 'object',
9
+ additionalProperties: {
10
+ type: 'array',
11
+ items: {
12
+ type: 'string',
13
+ minLength: 1,
14
+ },
15
+ description: 'Array of dependency paths (glob patterns allowed)',
16
+ },
17
+ examples: [
18
+ {
19
+ '/articles/{slug}.json': [
20
+ 'content_types/article',
21
+ 'templates/article/detail.html',
22
+ 'contents/*/article/*',
23
+ ],
24
+ '/articles/index.json': [
25
+ 'content_types/article',
26
+ 'templates/article/list.html',
27
+ 'contents/*/article/*',
28
+ ],
29
+ },
30
+ ],
31
+ };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Dependency Map type definitions
3
+ *
4
+ * A dependency map tracks which source files affect each output file,
5
+ * enabling incremental/differential builds.
6
+ */
7
+ /**
8
+ * Output path pattern (e.g., "/articles/{slug}.json", "/articles/index.json")
9
+ * Can include template variables like {slug}, {page}, etc.
10
+ */
11
+ export type OutputPath = string;
12
+ /**
13
+ * Dependency path pattern
14
+ * Examples: "contents/&#42;/article/&#42;", "templates/article/detail.html"
15
+ * Can include glob patterns for matching multiple files.
16
+ *
17
+ * Common prefixes:
18
+ * - content_types/ - Content type definitions
19
+ * - templates/ - Template files
20
+ * - contents/ - Content data files (often with glob patterns)
21
+ */
22
+ export type DependencyPath = string;
23
+ /**
24
+ * Dependency Map
25
+ *
26
+ * Maps output file paths to arrays of dependency paths.
27
+ * Used by the builder for incremental builds - when any dependency changes,
28
+ * the corresponding output files need to be regenerated.
29
+ *
30
+ * Example:
31
+ * ```
32
+ * {
33
+ * "/articles/{slug}.json": [
34
+ * "content_types/article",
35
+ * "templates/article/detail.html",
36
+ * "contents/&#42;/article/&#42;"
37
+ * ]
38
+ * }
39
+ * ```
40
+ */
41
+ export type DependencyMap = {
42
+ [outputPath: OutputPath]: DependencyPath[];
43
+ };
44
+ /**
45
+ * Common dependency path prefixes used in frelio projects
46
+ */
47
+ export declare const DEPENDENCY_PATH_PREFIXES: {
48
+ /** Content type schema definitions */
49
+ readonly CONTENT_TYPES: "content_types/";
50
+ /** Template files */
51
+ readonly TEMPLATES: "templates/";
52
+ /** Content data files */
53
+ readonly CONTENTS: "contents/";
54
+ };
55
+ export type DependencyPathPrefix = (typeof DEPENDENCY_PATH_PREFIXES)[keyof typeof DEPENDENCY_PATH_PREFIXES];
56
+ /**
57
+ * Result of validation operations
58
+ */
59
+ export type ValidationResult = {
60
+ valid: boolean;
61
+ errors: ValidationError[];
62
+ };
63
+ export type ValidationError = {
64
+ path: string;
65
+ message: string;
66
+ };
package/dist/types.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Dependency Map type definitions
3
+ *
4
+ * A dependency map tracks which source files affect each output file,
5
+ * enabling incremental/differential builds.
6
+ */
7
+ /**
8
+ * Common dependency path prefixes used in frelio projects
9
+ */
10
+ export const DEPENDENCY_PATH_PREFIXES = {
11
+ /** Content type schema definitions */
12
+ CONTENT_TYPES: 'content_types/',
13
+ /** Template files */
14
+ TEMPLATES: 'templates/',
15
+ /** Content data files */
16
+ CONTENTS: 'contents/',
17
+ };
@@ -0,0 +1,36 @@
1
+ import type { DependencyMap, DependencyPath, OutputPath } from './types.js';
2
+ /**
3
+ * Gets the dependency paths for a specific output path
4
+ * Returns an empty array if the output path is not found
5
+ */
6
+ export declare function getDependencyPaths(map: DependencyMap, outputPath: OutputPath): DependencyPath[];
7
+ /**
8
+ * Gets all output paths defined in the dependency map
9
+ */
10
+ export declare function getOutputPaths(map: DependencyMap): OutputPath[];
11
+ /**
12
+ * Gets all unique dependency paths across all outputs
13
+ */
14
+ export declare function getAllDependencyPaths(map: DependencyMap): DependencyPath[];
15
+ /**
16
+ * Merges multiple dependency maps into one
17
+ * Later maps override earlier ones for the same output path
18
+ */
19
+ export declare function mergeDependencyMaps(...maps: DependencyMap[]): DependencyMap;
20
+ /**
21
+ * Finds all output paths that depend on a given dependency path
22
+ * Supports exact match and simple glob pattern matching
23
+ */
24
+ export declare function findAffectedOutputs(map: DependencyMap, changedPath: DependencyPath): OutputPath[];
25
+ /**
26
+ * Creates an empty dependency map
27
+ */
28
+ export declare function createDependencyMap(): DependencyMap;
29
+ /**
30
+ * Adds or updates dependencies for an output path
31
+ */
32
+ export declare function setDependencies(map: DependencyMap, outputPath: OutputPath, dependencies: DependencyPath[]): DependencyMap;
33
+ /**
34
+ * Removes an output path from the dependency map
35
+ */
36
+ export declare function removeDependencies(map: DependencyMap, outputPath: OutputPath): DependencyMap;
package/dist/utils.js ADDED
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Gets the dependency paths for a specific output path
3
+ * Returns an empty array if the output path is not found
4
+ */
5
+ export function getDependencyPaths(map, outputPath) {
6
+ return map[outputPath] ?? [];
7
+ }
8
+ /**
9
+ * Gets all output paths defined in the dependency map
10
+ */
11
+ export function getOutputPaths(map) {
12
+ return Object.keys(map);
13
+ }
14
+ /**
15
+ * Gets all unique dependency paths across all outputs
16
+ */
17
+ export function getAllDependencyPaths(map) {
18
+ const allPaths = new Set();
19
+ for (const paths of Object.values(map)) {
20
+ for (const path of paths) {
21
+ allPaths.add(path);
22
+ }
23
+ }
24
+ return Array.from(allPaths).sort();
25
+ }
26
+ /**
27
+ * Merges multiple dependency maps into one
28
+ * Later maps override earlier ones for the same output path
29
+ */
30
+ export function mergeDependencyMaps(...maps) {
31
+ const result = {};
32
+ for (const map of maps) {
33
+ for (const [outputPath, deps] of Object.entries(map)) {
34
+ result[outputPath] = [...deps];
35
+ }
36
+ }
37
+ return result;
38
+ }
39
+ /**
40
+ * Finds all output paths that depend on a given dependency path
41
+ * Supports exact match and simple glob pattern matching
42
+ */
43
+ export function findAffectedOutputs(map, changedPath) {
44
+ const affected = [];
45
+ for (const [outputPath, deps] of Object.entries(map)) {
46
+ for (const dep of deps) {
47
+ if (matchesDependencyPath(dep, changedPath)) {
48
+ affected.push(outputPath);
49
+ break;
50
+ }
51
+ }
52
+ }
53
+ return affected;
54
+ }
55
+ /**
56
+ * Checks if a dependency pattern matches a given path
57
+ * Supports * as a wildcard for any single path segment
58
+ */
59
+ function matchesDependencyPath(pattern, path) {
60
+ // Exact match
61
+ if (pattern === path) {
62
+ return true;
63
+ }
64
+ // Convert glob pattern to regex
65
+ // * matches any characters except /
66
+ const regexPattern = pattern
67
+ .replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape special regex chars
68
+ .replace(/\*/g, '[^/]*'); // * matches any segment
69
+ try {
70
+ const regex = new RegExp(`^${regexPattern}$`);
71
+ return regex.test(path);
72
+ }
73
+ catch {
74
+ // If regex compilation fails, fall back to exact match
75
+ return pattern === path;
76
+ }
77
+ }
78
+ /**
79
+ * Creates an empty dependency map
80
+ */
81
+ export function createDependencyMap() {
82
+ return {};
83
+ }
84
+ /**
85
+ * Adds or updates dependencies for an output path
86
+ */
87
+ export function setDependencies(map, outputPath, dependencies) {
88
+ return {
89
+ ...map,
90
+ [outputPath]: [...dependencies],
91
+ };
92
+ }
93
+ /**
94
+ * Removes an output path from the dependency map
95
+ */
96
+ export function removeDependencies(map, outputPath) {
97
+ const { [outputPath]: _, ...rest } = map;
98
+ return rest;
99
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@c-time/frelio-dependency-map",
3
+ "version": "1.0.0",
4
+ "description": "Type definitions and utilities for frelio dependency map files",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./schema": {
14
+ "types": "./dist/schema.d.ts",
15
+ "import": "./dist/schema.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.build.json",
24
+ "dev": "tsc -p tsconfig.build.json --watch",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "keywords": [
28
+ "frelio",
29
+ "dependency-map",
30
+ "typescript",
31
+ "types",
32
+ "cms"
33
+ ],
34
+ "license": "MIT",
35
+ "devDependencies": {
36
+ "typescript": "^5.7.0"
37
+ },
38
+ "engines": {
39
+ "node": ">=18"
40
+ }
41
+ }