@ryanatkn/gro 0.170.0 → 0.171.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/dist/build.task.d.ts +6 -1
- package/dist/build.task.d.ts.map +1 -1
- package/dist/build.task.js +86 -5
- package/dist/build_cache.d.ts +100 -0
- package/dist/build_cache.d.ts.map +1 -0
- package/dist/build_cache.js +289 -0
- package/dist/deploy.task.d.ts.map +1 -1
- package/dist/deploy.task.js +13 -10
- package/dist/esbuild_plugin_svelte.js +1 -1
- package/dist/gen.d.ts.map +1 -1
- package/dist/gro_config.d.ts +30 -1
- package/dist/gro_config.d.ts.map +1 -1
- package/dist/gro_config.js +28 -4
- package/dist/hash.d.ts +1 -1
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js +1 -2
- package/dist/invoke_task.d.ts.map +1 -1
- package/dist/invoke_task.js +2 -1
- package/dist/package.d.ts.map +1 -1
- package/dist/package.js +23 -14
- package/dist/package_json.js +1 -1
- package/package.json +3 -3
- package/src/lib/build.task.ts +110 -6
- package/src/lib/build_cache.ts +362 -0
- package/src/lib/changelog.ts +1 -1
- package/src/lib/changeset.task.ts +1 -1
- package/src/lib/commit.task.ts +1 -1
- package/src/lib/deploy.task.ts +14 -10
- package/src/lib/esbuild_plugin_svelte.ts +1 -1
- package/src/lib/gen.ts +2 -1
- package/src/lib/gro_config.ts +62 -3
- package/src/lib/hash.ts +2 -4
- package/src/lib/invoke_task.ts +5 -2
- package/src/lib/package.ts +23 -14
- package/src/lib/package_json.ts +2 -2
- package/src/lib/parse_exports_context.ts +2 -2
- package/src/lib/parse_imports.ts +1 -1
- package/src/lib/upgrade.task.ts +1 -1
- package/dist/test_helpers.d.ts +0 -22
- package/dist/test_helpers.d.ts.map +0 -1
- package/dist/test_helpers.js +0 -123
- package/src/lib/test_helpers.ts +0 -161
package/dist/build.task.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import type
|
|
2
|
+
import { type Task } from './task.ts';
|
|
3
3
|
export declare const Args: z.ZodObject<{
|
|
4
4
|
sync: z.ZodDefault<z.ZodBoolean>;
|
|
5
5
|
'no-sync': z.ZodDefault<z.ZodBoolean>;
|
|
6
6
|
install: z.ZodDefault<z.ZodBoolean>;
|
|
7
7
|
'no-install': z.ZodDefault<z.ZodBoolean>;
|
|
8
|
+
force_build: z.ZodDefault<z.ZodBoolean>;
|
|
8
9
|
}, z.core.$strict>;
|
|
9
10
|
export type Args = z.infer<typeof Args>;
|
|
11
|
+
/**
|
|
12
|
+
* Length of git commit hash when displayed in logs (standard git convention).
|
|
13
|
+
*/
|
|
14
|
+
export declare const GIT_SHORT_HASH_LENGTH = 7;
|
|
10
15
|
export declare const task: Task<Args>;
|
|
11
16
|
//# sourceMappingURL=build.task.d.ts.map
|
package/dist/build.task.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/build.task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"build.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/build.task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAMtB,OAAO,EAAa,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AAWhD,eAAO,MAAM,IAAI;;;;;;kBAYf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC;;GAEG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAC;AASvC,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CAmG3B,CAAC"}
|
package/dist/build.task.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { styleText as st } from 'node:util';
|
|
3
|
+
import { git_check_clean_workspace, git_current_commit_hash } from '@ryanatkn/belt/git.js';
|
|
4
|
+
import { rmSync, existsSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { Task_Error } from "./task.js";
|
|
2
7
|
import { Plugins } from "./plugin.js";
|
|
3
8
|
import { clean_fs } from "./clean_fs.js";
|
|
9
|
+
import { is_build_cache_valid, create_build_cache_metadata, save_build_cache_metadata, discover_build_output_dirs, } from "./build_cache.js";
|
|
10
|
+
import { paths } from "./paths.js";
|
|
4
11
|
export const Args = z.strictObject({
|
|
5
12
|
sync: z.boolean().meta({ description: 'dual of no-sync' }).default(true),
|
|
6
13
|
'no-sync': z.boolean().meta({ description: 'opt out of gro sync' }).default(false),
|
|
@@ -9,24 +16,98 @@ export const Args = z.strictObject({
|
|
|
9
16
|
.boolean()
|
|
10
17
|
.meta({ description: 'opt out of installing packages before building' })
|
|
11
18
|
.default(false),
|
|
19
|
+
force_build: z
|
|
20
|
+
.boolean()
|
|
21
|
+
.meta({ description: 'force a fresh build, ignoring the cache' })
|
|
22
|
+
.default(false),
|
|
12
23
|
});
|
|
24
|
+
/**
|
|
25
|
+
* Length of git commit hash when displayed in logs (standard git convention).
|
|
26
|
+
*/
|
|
27
|
+
export const GIT_SHORT_HASH_LENGTH = 7;
|
|
28
|
+
/**
|
|
29
|
+
* Formats a git commit hash for display in logs.
|
|
30
|
+
* Returns '[none]' if hash is null (e.g., not in a git repo).
|
|
31
|
+
*/
|
|
32
|
+
const format_commit_hash = (hash) => hash?.slice(0, GIT_SHORT_HASH_LENGTH) ?? '[none]';
|
|
13
33
|
export const task = {
|
|
14
34
|
summary: 'build the project',
|
|
15
35
|
Args,
|
|
16
36
|
run: async (ctx) => {
|
|
17
|
-
const { args, invoke_task, log } = ctx;
|
|
18
|
-
const { sync, install } = args;
|
|
37
|
+
const { args, invoke_task, log, config } = ctx;
|
|
38
|
+
const { sync, install, force_build } = args;
|
|
19
39
|
if (sync || install) {
|
|
20
40
|
if (!sync)
|
|
21
41
|
log.warn('sync is false but install is true, so ignoring the sync option');
|
|
22
42
|
await invoke_task('sync', { install });
|
|
23
43
|
}
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
44
|
+
// Batch git calls upfront for performance (spawning processes is expensive)
|
|
45
|
+
const [workspace_status, initial_commit] = await Promise.all([
|
|
46
|
+
git_check_clean_workspace(),
|
|
47
|
+
git_current_commit_hash(),
|
|
48
|
+
]);
|
|
49
|
+
const workspace_dirty = !!workspace_status;
|
|
50
|
+
// Discover build output directories once to avoid redundant filesystem scans
|
|
51
|
+
let build_dirs;
|
|
52
|
+
// Check build cache unless force_build is set or workspace is dirty
|
|
53
|
+
if (!workspace_dirty && !force_build) {
|
|
54
|
+
const cache_valid = await is_build_cache_valid(config, log, initial_commit);
|
|
55
|
+
if (cache_valid) {
|
|
56
|
+
log.info(st('cyan', 'skipping build, cache is valid'), st('dim', '(use --force_build to rebuild)'));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else if (workspace_dirty) {
|
|
61
|
+
// IMPORTANT: When workspace is dirty, we delete cache AND all outputs to prevent stale state.
|
|
62
|
+
// Rationale: Uncommitted changes could be reverted, leaving cached outputs from reverted code.
|
|
63
|
+
// This conservative approach prioritizes safety over convenience during development.
|
|
64
|
+
const cache_path = join(paths.build, 'build.json');
|
|
65
|
+
if (existsSync(cache_path)) {
|
|
66
|
+
rmSync(cache_path, { force: true });
|
|
67
|
+
}
|
|
68
|
+
// Delete all build output directories
|
|
69
|
+
build_dirs = discover_build_output_dirs();
|
|
70
|
+
for (const dir of build_dirs) {
|
|
71
|
+
rmSync(dir, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
log.info(st('yellow', 'workspace has uncommitted changes - skipping build cache'));
|
|
74
|
+
// Skip clean_fs - already manually cleaned cache and all build outputs above
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
log.info(st('yellow', 'forcing fresh build, ignoring cache'));
|
|
78
|
+
}
|
|
79
|
+
// Clean build outputs (skip if workspace was dirty - already cleaned manually above)
|
|
80
|
+
if (!workspace_dirty) {
|
|
81
|
+
await clean_fs({ build_dist: true });
|
|
82
|
+
}
|
|
27
83
|
const plugins = await Plugins.create({ ...ctx, dev: false, watch: false });
|
|
28
84
|
await plugins.setup();
|
|
29
85
|
await plugins.adapt();
|
|
30
86
|
await plugins.teardown();
|
|
87
|
+
// Verify workspace didn't become dirty during build
|
|
88
|
+
const final_workspace_status = await git_check_clean_workspace();
|
|
89
|
+
if (final_workspace_status !== workspace_status) {
|
|
90
|
+
// Workspace state changed during build - this indicates a problem
|
|
91
|
+
throw new Task_Error('Build process modified tracked files or created untracked files.\n\n' +
|
|
92
|
+
'Git status after build:\n' +
|
|
93
|
+
final_workspace_status +
|
|
94
|
+
'\n\n' +
|
|
95
|
+
'Builds should only write to output directories (build/, dist/, etc.).\n' +
|
|
96
|
+
'This usually indicates a plugin or build step is incorrectly modifying source files.');
|
|
97
|
+
}
|
|
98
|
+
// Save build cache metadata after successful build (only if workspace is clean)
|
|
99
|
+
if (!workspace_dirty) {
|
|
100
|
+
// Race condition protection: verify git commit didn't change during build
|
|
101
|
+
const current_commit = await git_current_commit_hash();
|
|
102
|
+
if (current_commit !== initial_commit) {
|
|
103
|
+
log.warn(st('yellow', 'git commit changed during build'), st('dim', `(${format_commit_hash(initial_commit)} → ${format_commit_hash(current_commit)})`), '- cache not saved');
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
// Commit is stable - safe to save cache
|
|
107
|
+
const metadata = await create_build_cache_metadata(config, log, initial_commit, build_dirs);
|
|
108
|
+
save_build_cache_metadata(metadata, log);
|
|
109
|
+
log.debug('Build cache metadata saved');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
31
112
|
},
|
|
32
113
|
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Logger } from '@ryanatkn/belt/log.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { Gro_Config } from './gro_config.ts';
|
|
4
|
+
export declare const BUILD_CACHE_METADATA_FILENAME = "build.json";
|
|
5
|
+
export declare const BUILD_CACHE_VERSION = "1";
|
|
6
|
+
/**
|
|
7
|
+
* Metadata about a single build output file.
|
|
8
|
+
* Includes cryptographic hash for validation plus filesystem stats for debugging and optimization.
|
|
9
|
+
*/
|
|
10
|
+
export declare const Build_Output_Entry: z.ZodObject<{
|
|
11
|
+
path: z.ZodString;
|
|
12
|
+
hash: z.ZodString;
|
|
13
|
+
size: z.ZodNumber;
|
|
14
|
+
mtime: z.ZodNumber;
|
|
15
|
+
ctime: z.ZodNumber;
|
|
16
|
+
mode: z.ZodNumber;
|
|
17
|
+
}, z.core.$strict>;
|
|
18
|
+
export type Build_Output_Entry = z.infer<typeof Build_Output_Entry>;
|
|
19
|
+
/**
|
|
20
|
+
* Metadata stored in .gro/ directory to track build cache validity.
|
|
21
|
+
* Schema validates structure at load time to catch corrupted cache files.
|
|
22
|
+
*/
|
|
23
|
+
export declare const Build_Cache_Metadata: z.ZodObject<{
|
|
24
|
+
version: z.ZodString;
|
|
25
|
+
git_commit: z.ZodNullable<z.ZodString>;
|
|
26
|
+
build_cache_config_hash: z.ZodString;
|
|
27
|
+
timestamp: z.ZodString;
|
|
28
|
+
outputs: z.ZodArray<z.ZodObject<{
|
|
29
|
+
path: z.ZodString;
|
|
30
|
+
hash: z.ZodString;
|
|
31
|
+
size: z.ZodNumber;
|
|
32
|
+
mtime: z.ZodNumber;
|
|
33
|
+
ctime: z.ZodNumber;
|
|
34
|
+
mode: z.ZodNumber;
|
|
35
|
+
}, z.core.$strict>>;
|
|
36
|
+
}, z.core.$strict>;
|
|
37
|
+
export type Build_Cache_Metadata = z.infer<typeof Build_Cache_Metadata>;
|
|
38
|
+
/**
|
|
39
|
+
* Computes the cache key components for a build.
|
|
40
|
+
* This determines whether a cached build can be reused.
|
|
41
|
+
*
|
|
42
|
+
* @param config Gro config (build_cache_config_hash is already computed during config load)
|
|
43
|
+
* @param log Logger
|
|
44
|
+
* @param git_commit Optional pre-computed git commit hash (optimization to avoid re-reading)
|
|
45
|
+
*/
|
|
46
|
+
export declare const compute_build_cache_key: (config: Gro_Config, log: Logger, git_commit?: string | null) => Promise<{
|
|
47
|
+
git_commit: string | null;
|
|
48
|
+
build_cache_config_hash: string;
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Loads build cache metadata from .gro/ directory.
|
|
52
|
+
* Invalid or corrupted cache files are automatically deleted.
|
|
53
|
+
*/
|
|
54
|
+
export declare const load_build_cache_metadata: () => Build_Cache_Metadata | null;
|
|
55
|
+
/**
|
|
56
|
+
* Saves build cache metadata to .gro/ directory.
|
|
57
|
+
* Errors are logged but don't fail the build (cache is optional).
|
|
58
|
+
*/
|
|
59
|
+
export declare const save_build_cache_metadata: (metadata: Build_Cache_Metadata, log?: Logger) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Validates that a cached build is still valid by checking stats and hashing outputs.
|
|
62
|
+
* Uses size as a fast negative check before expensive hashing.
|
|
63
|
+
* This is comprehensive validation to catch manual tampering or corruption.
|
|
64
|
+
*/
|
|
65
|
+
export declare const validate_build_cache: (metadata: Build_Cache_Metadata) => Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Main function to check if the build cache is valid.
|
|
68
|
+
* Returns true if the cached build can be used, false if a fresh build is needed.
|
|
69
|
+
*
|
|
70
|
+
* @param config Gro config
|
|
71
|
+
* @param log Logger
|
|
72
|
+
* @param git_commit Optional pre-computed git commit hash (optimization)
|
|
73
|
+
*/
|
|
74
|
+
export declare const is_build_cache_valid: (config: Gro_Config, log: Logger, git_commit?: string | null) => Promise<boolean>;
|
|
75
|
+
/**
|
|
76
|
+
* Collects information about all files in build output directories.
|
|
77
|
+
* Returns an array of entries with path, hash, size, mtime, ctime, and mode.
|
|
78
|
+
*
|
|
79
|
+
* Files are hashed in parallel for performance. For very large builds (10k+ files),
|
|
80
|
+
* this may take several seconds but ensures complete cache validation.
|
|
81
|
+
*
|
|
82
|
+
* @param build_dirs Array of output directories to scan (e.g., ['build', 'dist', 'dist_server'])
|
|
83
|
+
*/
|
|
84
|
+
export declare const collect_build_outputs: (build_dirs: Array<string>) => Promise<Array<Build_Output_Entry>>;
|
|
85
|
+
/**
|
|
86
|
+
* Discovers all build output directories in the current working directory.
|
|
87
|
+
* Returns an array of directory names that exist: build/, dist/, dist_*
|
|
88
|
+
*/
|
|
89
|
+
export declare const discover_build_output_dirs: () => Array<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Creates build cache metadata after a successful build.
|
|
92
|
+
* Automatically discovers all build output directories (build/, dist/, dist_*).
|
|
93
|
+
*
|
|
94
|
+
* @param config Gro config
|
|
95
|
+
* @param log Logger
|
|
96
|
+
* @param git_commit Optional pre-computed git commit hash (optimization)
|
|
97
|
+
* @param build_dirs Optional pre-discovered build directories (optimization to avoid redundant filesystem scans)
|
|
98
|
+
*/
|
|
99
|
+
export declare const create_build_cache_metadata: (config: Gro_Config, log: Logger, git_commit?: string | null, build_dirs?: Array<string>) => Promise<Build_Cache_Metadata>;
|
|
100
|
+
//# sourceMappingURL=build_cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build_cache.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/build_cache.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,uBAAuB,CAAC;AAGlD,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAIhD,eAAO,MAAM,6BAA6B,eAAe,CAAC;AAC1D,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;;;kBAW7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;kBAU/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GACnC,QAAQ,UAAU,EAClB,KAAK,MAAM,EACX,aAAa,MAAM,GAAG,IAAI,KACxB,OAAO,CAAC;IACV,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,uBAAuB,EAAE,MAAM,CAAC;CAChC,CAYA,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,QAAO,oBAAoB,GAAG,IAoCnE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,GAAI,UAAU,oBAAoB,EAAE,MAAM,MAAM,KAAG,IAcxF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAU,UAAU,oBAAoB,KAAG,OAAO,CAAC,OAAO,CA8B1F,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAChC,QAAQ,UAAU,EAClB,KAAK,MAAM,EACX,aAAa,MAAM,GAAG,IAAI,KACxB,OAAO,CAAC,OAAO,CA+BjB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GACjC,YAAY,KAAK,CAAC,MAAM,CAAC,KACvB,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,CA2DnC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,QAAO,KAAK,CAAC,MAAM,CA2BzD,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,GACvC,QAAQ,UAAU,EAClB,KAAK,MAAM,EACX,aAAa,MAAM,GAAG,IAAI,EAC1B,aAAa,KAAK,CAAC,MAAM,CAAC,KACxB,OAAO,CAAC,oBAAoB,CAW9B,CAAC"}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { styleText as st } from 'node:util';
|
|
4
|
+
import { git_current_commit_hash } from '@ryanatkn/belt/git.js';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { to_hash } from "./hash.js";
|
|
7
|
+
import { paths } from "./paths.js";
|
|
8
|
+
import { SVELTEKIT_BUILD_DIRNAME, SVELTEKIT_DIST_DIRNAME, GRO_DIST_PREFIX } from "./constants.js";
|
|
9
|
+
export const BUILD_CACHE_METADATA_FILENAME = 'build.json';
|
|
10
|
+
export const BUILD_CACHE_VERSION = '1';
|
|
11
|
+
/**
|
|
12
|
+
* Metadata about a single build output file.
|
|
13
|
+
* Includes cryptographic hash for validation plus filesystem stats for debugging and optimization.
|
|
14
|
+
*/
|
|
15
|
+
export const Build_Output_Entry = z.strictObject({
|
|
16
|
+
path: z
|
|
17
|
+
.string()
|
|
18
|
+
.meta({ description: "relative path from project root (e.g., 'build/index.html')." }),
|
|
19
|
+
hash: z.string().meta({ description: 'SHA-256 hash of file contents' }),
|
|
20
|
+
size: z.number().meta({ description: 'file size in bytes' }),
|
|
21
|
+
mtime: z.number().meta({ description: 'modification time in milliseconds since epoch' }),
|
|
22
|
+
ctime: z.number().meta({
|
|
23
|
+
description: 'POSIX change time in milliseconds since epoch',
|
|
24
|
+
}),
|
|
25
|
+
mode: z.number().meta({ description: 'unix file permission mode (e.g., 33188 = 0644)' }),
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Metadata stored in .gro/ directory to track build cache validity.
|
|
29
|
+
* Schema validates structure at load time to catch corrupted cache files.
|
|
30
|
+
*/
|
|
31
|
+
export const Build_Cache_Metadata = z.strictObject({
|
|
32
|
+
version: z.string().meta({ description: 'schema version for future compatibility' }),
|
|
33
|
+
git_commit: z.string().nullable().meta({ description: 'git commit hash at time of build' }),
|
|
34
|
+
build_cache_config_hash: z
|
|
35
|
+
.string()
|
|
36
|
+
.meta({ description: "hash of user's custom build_cache_config from gro.config.ts." }),
|
|
37
|
+
timestamp: z.string().meta({ description: 'timestamp when build completed' }),
|
|
38
|
+
outputs: z
|
|
39
|
+
.array(Build_Output_Entry)
|
|
40
|
+
.meta({ description: 'build output files with hashes and filesystem stats' }),
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Computes the cache key components for a build.
|
|
44
|
+
* This determines whether a cached build can be reused.
|
|
45
|
+
*
|
|
46
|
+
* @param config Gro config (build_cache_config_hash is already computed during config load)
|
|
47
|
+
* @param log Logger
|
|
48
|
+
* @param git_commit Optional pre-computed git commit hash (optimization to avoid re-reading)
|
|
49
|
+
*/
|
|
50
|
+
export const compute_build_cache_key = async (config, log, git_commit) => {
|
|
51
|
+
// 1. Git commit hash - primary cache key
|
|
52
|
+
const commit = git_commit !== undefined ? git_commit : await git_current_commit_hash();
|
|
53
|
+
if (!commit) {
|
|
54
|
+
log.warn('Not in a git repository - build cache will use null git commit');
|
|
55
|
+
}
|
|
56
|
+
// 2. Build cache config hash - already computed during config normalization
|
|
57
|
+
return {
|
|
58
|
+
git_commit: commit,
|
|
59
|
+
build_cache_config_hash: config.build_cache_config_hash,
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Loads build cache metadata from .gro/ directory.
|
|
64
|
+
* Invalid or corrupted cache files are automatically deleted.
|
|
65
|
+
*/
|
|
66
|
+
export const load_build_cache_metadata = () => {
|
|
67
|
+
const metadata_path = join(paths.build, BUILD_CACHE_METADATA_FILENAME);
|
|
68
|
+
if (!existsSync(metadata_path)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const contents = readFileSync(metadata_path, 'utf-8');
|
|
73
|
+
const parsed = JSON.parse(contents);
|
|
74
|
+
// Validate structure with Zod
|
|
75
|
+
const metadata = Build_Cache_Metadata.parse(parsed);
|
|
76
|
+
// Validate version
|
|
77
|
+
if (metadata.version !== BUILD_CACHE_VERSION) {
|
|
78
|
+
// Clean up stale cache with old schema version
|
|
79
|
+
try {
|
|
80
|
+
rmSync(metadata_path, { force: true });
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Ignore cleanup errors
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
return metadata;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Clean up corrupted/invalid cache file
|
|
91
|
+
// (catches JSON.parse, Zod validation, and version errors)
|
|
92
|
+
try {
|
|
93
|
+
rmSync(metadata_path, { force: true });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Ignore cleanup errors
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Saves build cache metadata to .gro/ directory.
|
|
103
|
+
* Errors are logged but don't fail the build (cache is optional).
|
|
104
|
+
*/
|
|
105
|
+
export const save_build_cache_metadata = (metadata, log) => {
|
|
106
|
+
try {
|
|
107
|
+
// Ensure .gro directory exists
|
|
108
|
+
mkdirSync(paths.build, { recursive: true });
|
|
109
|
+
const metadata_path = join(paths.build, BUILD_CACHE_METADATA_FILENAME);
|
|
110
|
+
writeFileSync(metadata_path, JSON.stringify(metadata, null, '\t'), 'utf-8');
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
// Cache writes are optional - log warning but don't fail the build
|
|
114
|
+
log?.warn(st('yellow', 'Failed to save build cache'), st('dim', `(${error instanceof Error ? error.message : String(error)})`));
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Validates that a cached build is still valid by checking stats and hashing outputs.
|
|
119
|
+
* Uses size as a fast negative check before expensive hashing.
|
|
120
|
+
* This is comprehensive validation to catch manual tampering or corruption.
|
|
121
|
+
*/
|
|
122
|
+
export const validate_build_cache = async (metadata) => {
|
|
123
|
+
// Verify all tracked output files exist and have matching size
|
|
124
|
+
for (const output of metadata.outputs) {
|
|
125
|
+
if (!existsSync(output.path)) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
// Fast negative check: size mismatch = definitely invalid
|
|
129
|
+
// This avoids expensive file reads and hashing for files that have clearly changed
|
|
130
|
+
const stats = statSync(output.path);
|
|
131
|
+
if (stats.size !== output.size) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Size matches for all files - now verify content with cryptographic hashing
|
|
136
|
+
// Hash all files in parallel for performance
|
|
137
|
+
const hash_promises = metadata.outputs.map(async (output) => {
|
|
138
|
+
try {
|
|
139
|
+
const contents = readFileSync(output.path);
|
|
140
|
+
const actual_hash = await to_hash(contents);
|
|
141
|
+
return actual_hash === output.hash;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// File deleted/inaccessible between checks = cache invalid
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
const results = await Promise.all(hash_promises);
|
|
149
|
+
return results.every((valid) => valid);
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Main function to check if the build cache is valid.
|
|
153
|
+
* Returns true if the cached build can be used, false if a fresh build is needed.
|
|
154
|
+
*
|
|
155
|
+
* @param config Gro config
|
|
156
|
+
* @param log Logger
|
|
157
|
+
* @param git_commit Optional pre-computed git commit hash (optimization)
|
|
158
|
+
*/
|
|
159
|
+
export const is_build_cache_valid = async (config, log, git_commit) => {
|
|
160
|
+
// Load existing metadata
|
|
161
|
+
const metadata = load_build_cache_metadata();
|
|
162
|
+
if (!metadata) {
|
|
163
|
+
log.debug('No build cache metadata found');
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
// Compute current cache key
|
|
167
|
+
const current = await compute_build_cache_key(config, log, git_commit);
|
|
168
|
+
// Check if cache keys have changed
|
|
169
|
+
if (metadata.git_commit !== current.git_commit) {
|
|
170
|
+
log.debug('Build cache invalid: git commit changed');
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (metadata.build_cache_config_hash !== current.build_cache_config_hash) {
|
|
174
|
+
log.debug('Build cache invalid: build_cache_config changed');
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
// Comprehensive validation: verify output files
|
|
178
|
+
const outputs_valid = await validate_build_cache(metadata);
|
|
179
|
+
if (!outputs_valid) {
|
|
180
|
+
log.debug('Build cache invalid: output files missing or corrupted');
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
log.info(st('green', 'Build cache valid'), st('dim', `(from ${metadata.timestamp})`));
|
|
184
|
+
return true;
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Collects information about all files in build output directories.
|
|
188
|
+
* Returns an array of entries with path, hash, size, mtime, ctime, and mode.
|
|
189
|
+
*
|
|
190
|
+
* Files are hashed in parallel for performance. For very large builds (10k+ files),
|
|
191
|
+
* this may take several seconds but ensures complete cache validation.
|
|
192
|
+
*
|
|
193
|
+
* @param build_dirs Array of output directories to scan (e.g., ['build', 'dist', 'dist_server'])
|
|
194
|
+
*/
|
|
195
|
+
export const collect_build_outputs = async (build_dirs) => {
|
|
196
|
+
const files_to_hash = [];
|
|
197
|
+
// Recursively collect files
|
|
198
|
+
const collect_files = (dir, relative_base, dir_prefix) => {
|
|
199
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
200
|
+
for (const entry of entries) {
|
|
201
|
+
// Skip metadata file itself
|
|
202
|
+
if (entry.name === BUILD_CACHE_METADATA_FILENAME) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const full_path = join(dir, entry.name);
|
|
206
|
+
const relative_path = relative_base ? join(relative_base, entry.name) : entry.name;
|
|
207
|
+
const cache_key = join(dir_prefix, relative_path);
|
|
208
|
+
if (entry.isDirectory()) {
|
|
209
|
+
collect_files(full_path, relative_path, dir_prefix);
|
|
210
|
+
}
|
|
211
|
+
else if (entry.isFile()) {
|
|
212
|
+
files_to_hash.push({ full_path, cache_key });
|
|
213
|
+
}
|
|
214
|
+
// Symlinks are intentionally ignored - we only hash regular files
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
// Collect files from all build directories
|
|
218
|
+
for (const build_dir of build_dirs) {
|
|
219
|
+
if (!existsSync(build_dir)) {
|
|
220
|
+
continue; // Skip non-existent directories
|
|
221
|
+
}
|
|
222
|
+
collect_files(build_dir, '', build_dir);
|
|
223
|
+
}
|
|
224
|
+
// Hash all files in parallel and collect stats
|
|
225
|
+
const hash_promises = files_to_hash.map(async ({ full_path, cache_key }) => {
|
|
226
|
+
const stats = statSync(full_path);
|
|
227
|
+
const contents = readFileSync(full_path);
|
|
228
|
+
const hash = await to_hash(contents);
|
|
229
|
+
return {
|
|
230
|
+
path: cache_key,
|
|
231
|
+
hash,
|
|
232
|
+
size: stats.size,
|
|
233
|
+
mtime: stats.mtimeMs,
|
|
234
|
+
ctime: stats.ctimeMs,
|
|
235
|
+
mode: stats.mode,
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
return await Promise.all(hash_promises);
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* Discovers all build output directories in the current working directory.
|
|
242
|
+
* Returns an array of directory names that exist: build/, dist/, dist_*
|
|
243
|
+
*/
|
|
244
|
+
export const discover_build_output_dirs = () => {
|
|
245
|
+
const build_dirs = [];
|
|
246
|
+
// Check for SvelteKit app output (build/)
|
|
247
|
+
if (existsSync(SVELTEKIT_BUILD_DIRNAME)) {
|
|
248
|
+
build_dirs.push(SVELTEKIT_BUILD_DIRNAME);
|
|
249
|
+
}
|
|
250
|
+
// Check for SvelteKit library output (dist/)
|
|
251
|
+
if (existsSync(SVELTEKIT_DIST_DIRNAME)) {
|
|
252
|
+
build_dirs.push(SVELTEKIT_DIST_DIRNAME);
|
|
253
|
+
}
|
|
254
|
+
// Check for server and other plugin outputs (dist_*)
|
|
255
|
+
const root_entries = readdirSync('.');
|
|
256
|
+
const dist_dirs = root_entries.filter((p) => {
|
|
257
|
+
if (!p.startsWith(GRO_DIST_PREFIX))
|
|
258
|
+
return false;
|
|
259
|
+
try {
|
|
260
|
+
return statSync(p).isDirectory();
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
// File was deleted/moved during iteration - skip it
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
build_dirs.push(...dist_dirs);
|
|
268
|
+
return build_dirs;
|
|
269
|
+
};
|
|
270
|
+
/**
|
|
271
|
+
* Creates build cache metadata after a successful build.
|
|
272
|
+
* Automatically discovers all build output directories (build/, dist/, dist_*).
|
|
273
|
+
*
|
|
274
|
+
* @param config Gro config
|
|
275
|
+
* @param log Logger
|
|
276
|
+
* @param git_commit Optional pre-computed git commit hash (optimization)
|
|
277
|
+
* @param build_dirs Optional pre-discovered build directories (optimization to avoid redundant filesystem scans)
|
|
278
|
+
*/
|
|
279
|
+
export const create_build_cache_metadata = async (config, log, git_commit, build_dirs) => {
|
|
280
|
+
const cache_key = await compute_build_cache_key(config, log, git_commit);
|
|
281
|
+
const dirs = build_dirs ?? discover_build_output_dirs();
|
|
282
|
+
const outputs = await collect_build_outputs(dirs);
|
|
283
|
+
return {
|
|
284
|
+
version: BUILD_CACHE_VERSION,
|
|
285
|
+
...cache_key,
|
|
286
|
+
timestamp: new Date().toISOString(),
|
|
287
|
+
outputs,
|
|
288
|
+
};
|
|
289
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/deploy.task.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAqBtB,OAAO,EAAa,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AAkBhD,eAAO,MAAM,IAAI;;;;;;;;;;;;;;kBAiCf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,
|
|
1
|
+
{"version":3,"file":"deploy.task.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/deploy.task.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAqBtB,OAAO,EAAa,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AAkBhD,eAAO,MAAM,IAAI;;;;;;;;;;;;;;kBAiCf,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAExC,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CA6L3B,CAAC"}
|
package/dist/deploy.task.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Task_Error } from "./task.js";
|
|
|
10
10
|
import { print_path } from "./paths.js";
|
|
11
11
|
import { GRO_DIRNAME, GIT_DIRNAME, SVELTEKIT_BUILD_DIRNAME } from "./constants.js";
|
|
12
12
|
import { empty_dir } from "./fs.js";
|
|
13
|
-
// docs at
|
|
13
|
+
// docs at ../docs/deploy.md
|
|
14
14
|
// terminal command for testing:
|
|
15
15
|
// npm run bootstrap && rm -rf .gro && clear && gro deploy --source no-git-workspace --no-build --dry
|
|
16
16
|
// TODO customize
|
|
@@ -111,11 +111,14 @@ export const task = {
|
|
|
111
111
|
}
|
|
112
112
|
else {
|
|
113
113
|
await spawn('git', ['reset', '--hard'], target_spawn_options); // in case it's dirty
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
// Skip pulling target branch when resetting (optimization - we reset after anyway)
|
|
115
|
+
if (!reset) {
|
|
116
|
+
await git_pull(origin, target, target_spawn_options);
|
|
117
|
+
if (await git_check_clean_workspace(target_spawn_options)) {
|
|
118
|
+
// We're in a bad state because the local branch lost continuity with the remote,
|
|
119
|
+
// so delete the directory and continue as if it wasn't there.
|
|
120
|
+
await rm(resolved_deploy_dir, { recursive: true });
|
|
121
|
+
}
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
}
|
|
@@ -165,10 +168,6 @@ export const task = {
|
|
|
165
168
|
if (build) {
|
|
166
169
|
await invoke_task('build');
|
|
167
170
|
}
|
|
168
|
-
if (!existsSync(build_dir)) {
|
|
169
|
-
log.error(st('red', 'directory to deploy does not exist after building:'), build_dir);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
171
|
}
|
|
173
172
|
catch (err) {
|
|
174
173
|
log.error(st('red', 'build failed'), 'but', st('green', 'no changes were made to git'), print_error(err));
|
|
@@ -177,6 +176,10 @@ export const task = {
|
|
|
177
176
|
}
|
|
178
177
|
throw new Task_Error(`Deploy safely canceled due to build failure. See the error above.`);
|
|
179
178
|
}
|
|
179
|
+
// Verify build output exists
|
|
180
|
+
if (!existsSync(build_dir)) {
|
|
181
|
+
throw new Task_Error(`Directory to deploy does not exist after building: ${build_dir}`);
|
|
182
|
+
}
|
|
180
183
|
// Copy the build
|
|
181
184
|
await Promise.all(readdirSync(build_dir).map((path) => cp(join(build_dir, path), join(resolved_deploy_dir, path), { recursive: true })));
|
|
182
185
|
// At this point, `dist/` is ready to be committed and deployed!
|
|
@@ -69,7 +69,7 @@ export const esbuild_plugin_svelte = (options) => {
|
|
|
69
69
|
const convert_svelte_message_to_esbuild = (path, source, { message, start, end }) => {
|
|
70
70
|
let location = null;
|
|
71
71
|
if (start && end) {
|
|
72
|
-
const lineText = source.split(/\r\n|\r|\n/g)[start.line - 1];
|
|
72
|
+
const lineText = source.split(/\r\n|\r|\n/g)[start.line - 1] ?? '';
|
|
73
73
|
const lineEnd = start.line === end.line ? end.column : lineText.length;
|
|
74
74
|
location = {
|
|
75
75
|
file: path,
|
package/dist/gen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gen.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,uBAAuB,CAAC;AAGlD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,2BAA2B,CAAC;AAGvD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,wBAAwB,CAAC;AAGpD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAe,KAAK,oBAAoB,EAAE,KAAK,WAAW,EAAC,MAAM,cAAc,CAAC;AACvF,OAAO,EACN,UAAU,EAGV,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,WAAW,CAAC;AAE3C,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAC3C,eAAO,MAAM,gBAAgB,QAAoC,CAAC;AAElE,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,OAA0C,CAAC;AAEtF,MAAM,WAAW,UAAU;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;CACvB;AACD,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,uBAAuB,GAAG,yBAAyB,CAAC;AAE3F,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzB,KAAK,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACvB;AAED,MAAM,MAAM,yBAAyB,GAAG,CACvC,GAAG,EAAE,WAAW,KACZ,uBAAuB,GAAG,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,uBAAuB,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC;AAE9F,MAAM,MAAM,GAAG,GAAG,YAAY,GAAG,UAAU,CAAC;AAE5C,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"gen.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/gen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,uBAAuB,CAAC;AAGlD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,2BAA2B,CAAC;AAGvD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,wBAAwB,CAAC;AAGpD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAe,KAAK,oBAAoB,EAAE,KAAK,WAAW,EAAC,MAAM,cAAc,CAAC;AACvF,OAAO,EACN,UAAU,EAGV,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,WAAW,CAAC;AAE3C,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAC3C,eAAO,MAAM,gBAAgB,QAAoC,CAAC;AAElE,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,OAA0C,CAAC;AAEtF,MAAM,WAAW,UAAU;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;CACvB;AACD,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,uBAAuB,GAAG,yBAAyB,CAAC;AAE3F,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzB,KAAK,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACvB;AAED,MAAM,MAAM,yBAAyB,GAAG,CACvC,GAAG,EAAE,WAAW,KACZ,uBAAuB,GAAG,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,uBAAuB,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC;AAE9F,MAAM,MAAM,GAAG,GAAG,YAAY,GAAG,UAAU,CAAC;AAE5C,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;AAG1F,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,YAAY,CAAC;IACvB,YAAY,CAAC,EAAE,gBAAgB,CAAC;CAGhC;AAED,MAAM,WAAW,WAAW;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,aAAa,EAAE,oBAAoB,CAAC;IACpC,KAAK,EAAE,KAAK,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,eAAe,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAGD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,YAAY,GAAG,IAAI,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC;AAClF,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAGhB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACtC,SAAS,EAAE,KAAK,CAAC,6BAA6B,CAAC,CAAC;IAChD,QAAQ,EAAE,KAAK,CAAC,6BAA6B,CAAC,CAAC;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,MAAM,qBAAqB,GAAG,6BAA6B,GAAG,6BAA6B,CAAC;AAClG,MAAM,WAAW,6BAA6B;IAC7C,EAAE,EAAE,IAAI,CAAC;IACT,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,6BAA6B;IAC7C,EAAE,EAAE,KAAK,CAAC;IACV,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,aAAa,GAAI,WAAW,OAAO,EAAE,YAAY,cAAc,KAAG,UAK9E,CAAC;AAmCF,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,KAAG,MA2BtD,CAAC;AAYF,MAAM,MAAM,mBAAmB,GAC5B;IACA,IAAI,EAAE,QAAQ,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,KAAK,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;CACpB,GACD;IACA,IAAI,EAAE,QAAQ,CAAC;IACf,gBAAgB,EAAE,IAAI,CAAC;IACvB,MAAM,EAAE,IAAI,CAAC;IACb,WAAW,EAAE,IAAI,CAAC;CACjB,CAAC;AAEL,eAAO,MAAM,mBAAmB,GAC/B,aAAa,WAAW,KACtB,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAKnC,CAAC;AAEH,eAAO,MAAM,kBAAkB,GAAU,MAAM,QAAQ,KAAG,OAAO,CAAC,mBAAmB,CAgBpF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC7B,aAAa,WAAW,EACxB,sBAAsB,KAAK,CAAC,mBAAmB,CAAC,EAChD,KAAK,MAAM,KACT,OAAO,CAAC,IAAI,CAsBd,CAAC;AAEF,MAAM,WAAW,cAAc;IAC9B,oBAAoB,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACjD,gCAAgC,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC3E,oBAAoB,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;CACjD;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAC,EAAE,qBAAqB,CAAC,CAAC;AAC1F,MAAM,MAAM,qBAAqB,GAC9B;IACA,IAAI,EAAE,sBAAsB,CAAC;IAC7B,oBAAoB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACxC,oBAAoB,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACjD,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtB,GACD;IACA,IAAI,EAAE,iCAAiC,CAAC;IACxC,+BAA+B,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACnD,oBAAoB,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACjD,gCAAgC,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC3E,oBAAoB,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACjD,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtB,CAAC;AAEL;;GAEG;AACH,eAAO,MAAM,aAAa,GACzB,aAAa,KAAK,CAAC,UAAU,CAAC,EAC9B,WAAW,KAAK,CAAC,OAAO,CAAC,EACzB,QAAQ,UAAU,EAClB,UAAU,OAAO,KACf,oBA2DF,CAAC;AAEF,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,GAAG,CAAC;CACT;AAED,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC,cAAc,CAAC,CAAC;AAE9D,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACpC,cAAc,EAAE,cAAc,CAAC;CAC/B;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAC,EAAE,qBAAqB,CAAC,CAAC;AAC3F,MAAM,MAAM,qBAAqB,GAAG,oBAAoB,CAAC,mBAAmB,CAAC,CAAC;AAE9E,eAAO,MAAM,aAAa,GACzB,gBAAgB,cAAc,EAC9B,UAAU,OAAO,KACf,OAAO,CAAC,oBAAoB,CAc9B,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,GAAG,IAAI,cAMrE,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,KAAK,GAAG,KAAG,UACE,CAAC"}
|