@trackunit/migrations 0.0.3

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/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ## 0.0.3 (2026-05-06)
2
+
3
+ This was a version bump only for migrations to align it with other projects, there were no code changes.
4
+
5
+ ## 0.0.2 (2026-05-04)
6
+
7
+ This was a version bump only for migrations to align it with other projects, there were no code changes.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # @trackunit/migrations
2
+
3
+ Coordinator library that discovers installed `@trackunit/*` packages, updates their versions, and runs pending migrations.
4
+
5
+ ## How it works
6
+
7
+ Each published `@trackunit/*` package can declare a `"migrations"` field in its `package.json` pointing to a `migrations.json` manifest. This library recursively scans `node_modules` (including nested/transitive dependencies) to find all such packages, then follows a two-step flow modeled after `nx migrate`:
8
+
9
+ 1. **`migrate`** -- discovers packages, updates `package.json` versions, writes pending migrations to `trackunit-migrations.json`
10
+ 2. **`run-migrations`** -- executes the pending migrations from that file, then clears the list
11
+
12
+ Version state is tracked in `trackunit-migrations.json` so migrations that have already been applied are not re-run.
13
+
14
+ ```mermaid
15
+ flowchart TD
16
+ subgraph step1 [Step 1: nx g @trackunit/migrations:migrate]
17
+ scan["Recursively scan node_modules\nfor @trackunit/* packages"]
18
+ scan --> checkField{"package.json\nhas migrations field?"}
19
+ checkField -->|No| skipPkg[Skip package]
20
+ checkField -->|Yes| loadManifest["Load migrations.json"]
21
+ loadManifest --> readState["Read trackunit-migrations.json\nfor migratedVersions state"]
22
+ readState --> filterVersion{"migration.version >\nlastMigratedVersion?"}
23
+ filterVersion -->|No| skipMigration[Skip migration]
24
+ filterVersion -->|Yes| collect["Add to pending list"]
25
+ end
26
+
27
+ subgraph updatePkg [Version Updates]
28
+ scan --> compareDeps{"Installed version >\npackage.json version?"}
29
+ compareDeps -->|Yes| bumpDep["Update version in\nconsumer package.json"]
30
+ compareDeps -->|No| noop[No change]
31
+ end
32
+
33
+ collect --> writeFile["Write trackunit-migrations.json\nwith pending migrations +\nupdated migratedVersions"]
34
+
35
+ subgraph step2 [Step 2: nx g @trackunit/migrations:run-migrations]
36
+ readFile["Read trackunit-migrations.json"] --> loop["For each pending migration"]
37
+ loop --> importImpl["Import migration implementation"]
38
+ importImpl --> execute["Execute migration against NX Tree\n(AST transforms, file rewrites)"]
39
+ execute --> clearPending["Clear pending list\nPreserve migratedVersions"]
40
+ end
41
+
42
+ writeFile --> review["Developer reviews\ntrackunit-migrations.json"]
43
+ review --> readFile
44
+ ```
45
+
46
+ ```mermaid
47
+ flowchart LR
48
+ subgraph nodeModules ["node_modules (recursive scan)"]
49
+ rc["@trackunit/react-components\nv2.0.0\nmigrations: ./migrations.json"]
50
+ rcc["@trackunit/react-chart-components\nv1.20.0\nmigrations: ./migrations.json"]
51
+ su["@trackunit/shared-utils\nv1.13.0\n(no migrations field)"]
52
+ nested["some-lib/node_modules/\n@trackunit/react-components\nv1.21.8 (older, deduped out)"]
53
+ end
54
+
55
+ subgraph migrationsFiles [migrations.json per package]
56
+ rcMig["react-components/migrations.json\n- rename-button-kind (v1.22.0)\n- button-required-variant (v2.0.0)"]
57
+ rccMig["react-chart-components/migrations.json\n- chart-api-v2 (v1.20.0)"]
58
+ end
59
+
60
+ subgraph output [trackunit-migrations.json]
61
+ pending["migrations:\n- rename-button-kind\n- button-required-variant\n- chart-api-v2\n\nmigratedVersions:\n react-components: 2.0.0\n react-chart-components: 1.20.0"]
62
+ end
63
+
64
+ rc --> rcMig
65
+ rcc --> rccMig
66
+ su -.->|skipped| nodeModules
67
+ nested -.->|deduped| rc
68
+ rcMig --> pending
69
+ rccMig --> pending
70
+ ```
71
+
72
+ ## Usage
73
+
74
+ ### Step 1: Discover and collect migrations
75
+
76
+ ```bash
77
+ nx g @trackunit/migrations:migrate
78
+ ```
79
+
80
+ This updates `@trackunit/*` dependency versions in your `package.json` and writes a `trackunit-migrations.json` file listing any pending migrations. Review the file before proceeding.
81
+
82
+ ### Step 2: Run pending migrations
83
+
84
+ ```bash
85
+ nx g @trackunit/migrations:run-migrations
86
+ ```
87
+
88
+ This executes each migration listed in `trackunit-migrations.json` and clears the pending list. You can delete the file afterward or keep it for version tracking.
89
+
90
+ ### Internal monorepo
91
+
92
+ ```bash
93
+ yarn nx g @trackunit/migrations:migrate
94
+ yarn nx g @trackunit/migrations:run-migrations
95
+ ```
96
+
97
+ For more info and a full guide on Iris App SDK Development, please visit our [Developer Hub](https://developers.trackunit.com/).
98
+
99
+ ## Trackunit
100
+
101
+ This package was developed by Trackunit ApS.
102
+
103
+ Trackunit is the leading SaaS-based IoT solution for the construction industry, offering an ecosystem of hardware, fleet management software & telematics.
104
+
105
+ ![The Trackunit logo](https://trackunit.com/wp-content/uploads/2022/03/top-logo.svg)
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "generators": {
4
+ "migrate": {
5
+ "factory": "./src/generators/migrate/generator",
6
+ "schema": "./src/generators/migrate/schema.json",
7
+ "description": "Discover @trackunit packages, update package.json versions, and write pending migrations to trackunit-migrations.json"
8
+ },
9
+ "run-migrations": {
10
+ "factory": "./src/generators/run-migrations/generator",
11
+ "schema": "./src/generators/run-migrations/schema.json",
12
+ "description": "Execute pending migrations from trackunit-migrations.json"
13
+ }
14
+ }
15
+ }
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@trackunit/migrations",
3
+ "version": "0.0.3",
4
+ "repository": "https://github.com/Trackunit/manager",
5
+ "license": "SEE LICENSE IN LICENSE.txt",
6
+ "engines": {
7
+ "node": ">=24.x"
8
+ },
9
+ "main": "src/index.js",
10
+ "generators": "./generators.json",
11
+ "dependencies": {
12
+ "@nx/devkit": "22.6.5",
13
+ "tslib": "^2.6.2",
14
+ "semver": "7.5.4"
15
+ },
16
+ "types": "./src/index.d.ts",
17
+ "type": "commonjs"
18
+ }
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.migrate = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const devkit_1 = require("@nx/devkit");
6
+ const path_1 = tslib_1.__importDefault(require("path"));
7
+ const discover_packages_1 = require("../utils/discover-packages");
8
+ const TRACKUNIT_MIGRATIONS_FILE = "trackunit-migrations.json";
9
+ const getLastMigratedVersion = (tree, packageName) => {
10
+ if (!tree.exists(TRACKUNIT_MIGRATIONS_FILE))
11
+ return undefined;
12
+ try {
13
+ const file = (0, devkit_1.readJson)(tree, TRACKUNIT_MIGRATIONS_FILE);
14
+ return file.migratedVersions[packageName];
15
+ }
16
+ catch {
17
+ return undefined;
18
+ }
19
+ };
20
+ const getCurrentDepVersion = (tree, packageName) => {
21
+ const packageJson = (0, devkit_1.readJson)(tree, "package.json");
22
+ return packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName];
23
+ };
24
+ const updateDepVersion = (tree, pkg) => {
25
+ const currentDep = getCurrentDepVersion(tree, pkg.name);
26
+ if (currentDep === undefined || (0, discover_packages_1.compareVersions)(pkg.version, currentDep.replace(/^[~^]/, "")) <= 0) {
27
+ return;
28
+ }
29
+ (0, devkit_1.updateJson)(tree, "package.json", json => {
30
+ if (json.dependencies?.[pkg.name] !== undefined) {
31
+ json.dependencies[pkg.name] = pkg.version;
32
+ }
33
+ if (json.devDependencies?.[pkg.name] !== undefined) {
34
+ json.devDependencies[pkg.name] = pkg.version;
35
+ }
36
+ return json;
37
+ });
38
+ devkit_1.logger.info(` Updated ${pkg.name} to ${pkg.version} in package.json`);
39
+ };
40
+ /**
41
+ * Returns an implementation path that is relative to the workspace root
42
+ * (`process.cwd()`) when the resolved path lives inside the workspace,
43
+ * so that `trackunit-migrations.json` is portable between machines and
44
+ * checkouts. Falls back to the absolute path for migrations sourced from
45
+ * outside the workspace (e.g. node_modules in a non-default location).
46
+ */
47
+ const portableImplementationPath = (absolutePath, workspaceRoot) => {
48
+ const relative = path_1.default.relative(workspaceRoot, absolutePath);
49
+ if (relative === "" || relative.startsWith("..") || path_1.default.isAbsolute(relative)) {
50
+ return absolutePath;
51
+ }
52
+ return relative.split(path_1.default.sep).join("/");
53
+ };
54
+ const collectPendingMigrations = (pkg, lastMigrated, migrationsJson, workspaceRoot) => {
55
+ const pending = [];
56
+ const migrationsDir = path_1.default.dirname(pkg.migrationsJsonPath);
57
+ for (const [name, entry] of Object.entries(migrationsJson.generators ?? {})) {
58
+ if (lastMigrated !== undefined && (0, discover_packages_1.compareVersions)(entry.version, lastMigrated) <= 0) {
59
+ continue;
60
+ }
61
+ const absoluteImpl = path_1.default.resolve(migrationsDir, entry.implementation);
62
+ pending.push({
63
+ package: pkg.name,
64
+ name,
65
+ version: entry.version,
66
+ description: entry.description,
67
+ implementation: portableImplementationPath(absoluteImpl, workspaceRoot),
68
+ });
69
+ }
70
+ return pending;
71
+ };
72
+ /**
73
+ * Step 1: Discovers installed @trackunit packages, compares versions
74
+ * against the consumer's package.json, updates dependency versions,
75
+ * and writes trackunit-migrations.json with pending migrations.
76
+ *
77
+ * Usage: nx g @trackunit/migrations:migrate
78
+ * Then: nx g @trackunit/migrations:run-migrations
79
+ */
80
+ const migrate = (tree, options) => {
81
+ const packages = (0, discover_packages_1.discoverTrackunitPackages)();
82
+ const workspaceRoot = process.cwd();
83
+ devkit_1.logger.info(`Discovered ${packages.length} @trackunit/* package(s) with migrations`);
84
+ const allPending = [];
85
+ const migratedVersions = {};
86
+ if (tree.exists(TRACKUNIT_MIGRATIONS_FILE)) {
87
+ try {
88
+ const existing = (0, devkit_1.readJson)(tree, TRACKUNIT_MIGRATIONS_FILE);
89
+ Object.assign(migratedVersions, existing.migratedVersions);
90
+ }
91
+ catch {
92
+ // start fresh
93
+ }
94
+ }
95
+ for (const pkg of packages) {
96
+ updateDepVersion(tree, pkg);
97
+ const migrationsJson = (0, discover_packages_1.loadMigrationsJson)(pkg.migrationsJsonPath);
98
+ if (!migrationsJson?.generators || Object.keys(migrationsJson.generators).length === 0) {
99
+ if (options.verbose) {
100
+ devkit_1.logger.info(` ${pkg.name}: no migrations defined`);
101
+ }
102
+ migratedVersions[pkg.name] = pkg.version;
103
+ continue;
104
+ }
105
+ const lastMigrated = getLastMigratedVersion(tree, pkg.name);
106
+ if (options.verbose) {
107
+ devkit_1.logger.info(` ${pkg.name}: installed=${pkg.version}, lastMigrated=${lastMigrated ?? "none"}`);
108
+ }
109
+ const pending = collectPendingMigrations(pkg, lastMigrated, migrationsJson, workspaceRoot);
110
+ allPending.push(...pending);
111
+ if (pending.length > 0) {
112
+ devkit_1.logger.info(`${pkg.name}@${pkg.version}: ${pending.length} pending migration(s)`);
113
+ // Defer advancing migratedVersions until run-migrations has applied
114
+ // these entries. Otherwise a second `migrate` run would read this
115
+ // version back as `lastMigrated` and silently drop the queued migrations.
116
+ continue;
117
+ }
118
+ migratedVersions[pkg.name] = pkg.version;
119
+ }
120
+ const output = {
121
+ migratedVersions,
122
+ migrations: allPending,
123
+ };
124
+ tree.write(TRACKUNIT_MIGRATIONS_FILE, JSON.stringify(output, null, 2));
125
+ if (allPending.length === 0) {
126
+ devkit_1.logger.info("No pending migrations. trackunit-migrations.json updated.");
127
+ }
128
+ else {
129
+ devkit_1.logger.info(`\nWrote ${allPending.length} pending migration(s) to ${TRACKUNIT_MIGRATIONS_FILE}`);
130
+ devkit_1.logger.info("Review the file, then run: nx g @trackunit/migrations:run-migrations");
131
+ }
132
+ };
133
+ exports.migrate = migrate;
134
+ exports.default = exports.migrate;
135
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "verbose": {
6
+ "type": "boolean",
7
+ "description": "Show detailed version comparison for each discovered package",
8
+ "default": false
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,3 @@
1
+ module.exports = function failingMigration() {
2
+ throw new Error("Intentional test failure");
3
+ };
@@ -0,0 +1 @@
1
+ module.exports = function noopMigration() {};
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runMigrations = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const devkit_1 = require("@nx/devkit");
6
+ const path_1 = tslib_1.__importDefault(require("path"));
7
+ const discover_packages_1 = require("../utils/discover-packages");
8
+ /**
9
+ * Resolves a stored implementation path. Relative paths in
10
+ * `trackunit-migrations.json` are interpreted as relative to the
11
+ * workspace root (`process.cwd()`); absolute paths are used as-is.
12
+ * This keeps the on-disk file portable between checkouts while
13
+ * preserving back-compat with absolute paths.
14
+ */
15
+ const resolveImplementationPath = (storedPath) => path_1.default.isAbsolute(storedPath) ? storedPath : path_1.default.resolve(process.cwd(), storedPath);
16
+ const TRACKUNIT_MIGRATIONS_FILE = "trackunit-migrations.json";
17
+ const runMigration = async (tree, migration, verbose) => {
18
+ try {
19
+ const resolvedPath = resolveImplementationPath(migration.implementation);
20
+ const migrationModule = await Promise.resolve(`${resolvedPath}`).then(s => tslib_1.__importStar(require(s)));
21
+ const migrationFn = migrationModule.default ?? migrationModule;
22
+ if (typeof migrationFn !== "function") {
23
+ devkit_1.logger.warn(` "${migration.name}" does not export a function, skipping`);
24
+ return false;
25
+ }
26
+ if (verbose) {
27
+ devkit_1.logger.info(` Running: ${migration.name} (v${migration.version}) from ${migration.package}`);
28
+ }
29
+ migrationFn(tree);
30
+ return true;
31
+ }
32
+ catch (error) {
33
+ devkit_1.logger.error(` Failed "${migration.name}": ${error instanceof Error ? error.message : String(error)}`);
34
+ return false;
35
+ }
36
+ };
37
+ /**
38
+ * Step 2: Reads trackunit-migrations.json and executes each pending
39
+ * migration. Successful migrations are removed from the pending list
40
+ * and bump `migratedVersions[package]` to their version. Failures
41
+ * stay in the list so they remain retryable on the next run without
42
+ * having to go through `migrate` again.
43
+ *
44
+ * Usage: nx g @trackunit/migrations:run-migrations
45
+ */
46
+ const runMigrations = async (tree, options) => {
47
+ if (!tree.exists(TRACKUNIT_MIGRATIONS_FILE)) {
48
+ devkit_1.logger.error(`${TRACKUNIT_MIGRATIONS_FILE} not found. Run "nx g @trackunit/migrations:migrate" first.`);
49
+ return;
50
+ }
51
+ const file = (0, devkit_1.readJson)(tree, TRACKUNIT_MIGRATIONS_FILE);
52
+ if (file.migrations.length === 0) {
53
+ devkit_1.logger.info("No pending migrations to run.");
54
+ return;
55
+ }
56
+ devkit_1.logger.info(`Running ${file.migrations.length} migration(s)...\n`);
57
+ let succeeded = 0;
58
+ const remaining = [];
59
+ const migratedVersions = { ...file.migratedVersions };
60
+ for (const migration of file.migrations) {
61
+ devkit_1.logger.info(`${migration.package}: ${migration.name} - ${migration.description}`);
62
+ const ok = await runMigration(tree, migration, options.verbose ?? false);
63
+ if (ok) {
64
+ succeeded++;
65
+ const current = migratedVersions[migration.package];
66
+ if (current === undefined || (0, discover_packages_1.compareVersions)(migration.version, current) > 0) {
67
+ migratedVersions[migration.package] = migration.version;
68
+ }
69
+ }
70
+ else {
71
+ remaining.push(migration);
72
+ }
73
+ }
74
+ const updated = {
75
+ migratedVersions,
76
+ migrations: remaining,
77
+ };
78
+ tree.write(TRACKUNIT_MIGRATIONS_FILE, JSON.stringify(updated, null, 2));
79
+ await (0, devkit_1.formatFiles)(tree);
80
+ devkit_1.logger.info(`\n${succeeded} migration(s) applied successfully.`);
81
+ if (remaining.length > 0) {
82
+ devkit_1.logger.warn(`${remaining.length} migration(s) failed and remain in ${TRACKUNIT_MIGRATIONS_FILE}. Fix the cause and re-run "nx g @trackunit/migrations:run-migrations".`);
83
+ }
84
+ else {
85
+ devkit_1.logger.info(`\nYou can delete ${TRACKUNIT_MIGRATIONS_FILE} or keep it for version tracking.`);
86
+ }
87
+ };
88
+ exports.runMigrations = runMigrations;
89
+ exports.default = exports.runMigrations;
90
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "verbose": {
6
+ "type": "boolean",
7
+ "description": "Show detailed output for each migration",
8
+ "default": false
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.discoverTrackunitPackages = exports.discoverWorkspaceTrackunitPackages = exports.compareVersions = exports.loadMigrationsJson = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs_1 = require("fs");
6
+ const path_1 = tslib_1.__importDefault(require("path"));
7
+ const readPackageJson = (pkgJsonPath) => {
8
+ try {
9
+ const raw = (0, fs_1.readFileSync)(pkgJsonPath, "utf-8");
10
+ return JSON.parse(raw);
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ };
16
+ const resolveMigrationsPath = (pkgDir, pkgJson) => {
17
+ const migrations = pkgJson.migrations;
18
+ if (migrations === undefined)
19
+ return undefined;
20
+ const migrationsFile = typeof migrations === "string" ? migrations : migrations.migrations;
21
+ const resolved = path_1.default.resolve(pkgDir, migrationsFile);
22
+ return (0, fs_1.existsSync)(resolved) ? resolved : undefined;
23
+ };
24
+ /** Compares two semver strings, returning negative/zero/positive like Array.sort comparators. */
25
+ /** Loads and parses a migrations.json file from disk. */
26
+ const loadMigrationsJson = (migrationsPath) => {
27
+ try {
28
+ const raw = (0, fs_1.readFileSync)(migrationsPath, "utf-8");
29
+ return JSON.parse(raw);
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ };
35
+ exports.loadMigrationsJson = loadMigrationsJson;
36
+ const semver_1 = require("semver");
37
+ /**
38
+ * Compare two semver strings, returning negative/zero/positive like Array.sort comparators.
39
+ *
40
+ * @param a - The first semver string to compare.
41
+ * @param b - The second semver string to compare.
42
+ * @returns {number} A negative number if a < b, 0 if a === b, and a positive number if a > b.
43
+ */
44
+ const compareVersions = (a, b) => (0, semver_1.compare)(a, b);
45
+ exports.compareVersions = compareVersions;
46
+ const scanScopeDir = (scopeDir) => {
47
+ if (!(0, fs_1.existsSync)(scopeDir))
48
+ return [];
49
+ const entries = (0, fs_1.readdirSync)(scopeDir, { withFileTypes: true });
50
+ const found = [];
51
+ for (const entry of entries) {
52
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
53
+ continue;
54
+ const pkgDir = path_1.default.join(scopeDir, entry.name);
55
+ const pkgJsonPath = path_1.default.join(pkgDir, "package.json");
56
+ const pkgJson = readPackageJson(pkgJsonPath);
57
+ if (!pkgJson)
58
+ continue;
59
+ const migrationsJsonPath = resolveMigrationsPath(pkgDir, pkgJson);
60
+ if (migrationsJsonPath) {
61
+ found.push({
62
+ name: pkgJson.name ?? `@trackunit/${entry.name}`,
63
+ version: pkgJson.version,
64
+ migrationsJsonPath,
65
+ });
66
+ }
67
+ }
68
+ return found;
69
+ };
70
+ const MANAGER_REPO_PACKAGE_NAME = "@trackunit/manager";
71
+ const WORKSPACE_SCAN_ROOTS = ["libs", "apps"];
72
+ const isManagerMonorepo = (cwd) => {
73
+ const rootPkgJson = readPackageJson(path_1.default.join(cwd, "package.json"));
74
+ return rootPkgJson?.name === MANAGER_REPO_PACKAGE_NAME;
75
+ };
76
+ const walkForPackageJsons = (rootDir, onPackageJson) => {
77
+ if (!(0, fs_1.existsSync)(rootDir))
78
+ return;
79
+ const stack = [rootDir];
80
+ while (stack.length > 0) {
81
+ const dir = stack.pop();
82
+ if (dir === undefined)
83
+ continue;
84
+ let entries;
85
+ try {
86
+ entries = (0, fs_1.readdirSync)(dir, { withFileTypes: true });
87
+ }
88
+ catch {
89
+ continue;
90
+ }
91
+ for (const entry of entries) {
92
+ if (entry.name === "node_modules" || entry.name.startsWith("."))
93
+ continue;
94
+ const entryPath = path_1.default.join(dir, entry.name);
95
+ if (entry.isDirectory()) {
96
+ stack.push(entryPath);
97
+ }
98
+ else if (entry.isFile() && entry.name === "package.json") {
99
+ onPackageJson(entryPath);
100
+ }
101
+ }
102
+ }
103
+ };
104
+ /**
105
+ * In-monorepo fallback discovery: scans libs/ and apps/ for
106
+ * package.json files whose name starts with "@trackunit/" and that
107
+ * declare a "migrations" field. Used only when the engine is run
108
+ * inside the manager monorepo (root package.json name is
109
+ * "@trackunit/manager"), since internal workspace projects are not
110
+ * symlinked under node_modules/@trackunit/* by Yarn.
111
+ */
112
+ const discoverWorkspaceTrackunitPackages = (cwd = process.cwd()) => {
113
+ const found = [];
114
+ for (const root of WORKSPACE_SCAN_ROOTS) {
115
+ walkForPackageJsons(path_1.default.join(cwd, root), pkgJsonPath => {
116
+ const pkgJson = readPackageJson(pkgJsonPath);
117
+ if (!pkgJson?.name || !pkgJson.name.startsWith("@trackunit/"))
118
+ return;
119
+ const migrationsJsonPath = resolveMigrationsPath(path_1.default.dirname(pkgJsonPath), pkgJson);
120
+ if (!migrationsJsonPath)
121
+ return;
122
+ found.push({
123
+ name: pkgJson.name,
124
+ version: pkgJson.version,
125
+ migrationsJsonPath,
126
+ });
127
+ });
128
+ }
129
+ return found;
130
+ };
131
+ exports.discoverWorkspaceTrackunitPackages = discoverWorkspaceTrackunitPackages;
132
+ /**
133
+ * Recursively walks node_modules directories looking for @trackunit/*
134
+ * packages that declare a "migrations" field. Scans root node_modules
135
+ * and nested node_modules inside every package. When the same package
136
+ * is found at multiple levels, the highest semver version is kept.
137
+ *
138
+ * Works with all package managers (npm, yarn, pnpm, bun).
139
+ *
140
+ * When invoked inside the manager monorepo (root package.json name
141
+ * is "@trackunit/manager"), additionally scans libs/ and apps/ for
142
+ * workspace packages that ship migrations, since those are not
143
+ * linked under node_modules/@trackunit/.
144
+ */
145
+ const discoverTrackunitPackages = () => {
146
+ const cwd = process.cwd();
147
+ const rootNodeModules = path_1.default.resolve(cwd, "node_modules");
148
+ const allFound = [];
149
+ const visited = new Set();
150
+ const walkNodeModules = (nodeModulesDir) => {
151
+ if (!(0, fs_1.existsSync)(nodeModulesDir))
152
+ return;
153
+ const canonical = path_1.default.resolve(nodeModulesDir);
154
+ if (visited.has(canonical))
155
+ return;
156
+ visited.add(canonical);
157
+ const trackunitScope = path_1.default.join(nodeModulesDir, "@trackunit");
158
+ allFound.push(...scanScopeDir(trackunitScope));
159
+ const topEntries = (0, fs_1.readdirSync)(nodeModulesDir, {
160
+ withFileTypes: true,
161
+ });
162
+ for (const entry of topEntries) {
163
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
164
+ continue;
165
+ const entryPath = path_1.default.join(nodeModulesDir, entry.name);
166
+ if (entry.name.startsWith("@")) {
167
+ const scopeEntries = (0, fs_1.readdirSync)(entryPath, {
168
+ withFileTypes: true,
169
+ });
170
+ for (const scopeEntry of scopeEntries) {
171
+ if (!scopeEntry.isDirectory() && !scopeEntry.isSymbolicLink())
172
+ continue;
173
+ walkNodeModules(path_1.default.join(entryPath, scopeEntry.name, "node_modules"));
174
+ }
175
+ }
176
+ else {
177
+ walkNodeModules(path_1.default.join(entryPath, "node_modules"));
178
+ }
179
+ }
180
+ };
181
+ walkNodeModules(rootNodeModules);
182
+ if (isManagerMonorepo(cwd)) {
183
+ allFound.push(...(0, exports.discoverWorkspaceTrackunitPackages)(cwd));
184
+ }
185
+ const byName = new Map();
186
+ for (const pkg of allFound) {
187
+ const existing = byName.get(pkg.name);
188
+ if (!existing || (0, exports.compareVersions)(pkg.version, existing.version) > 0) {
189
+ byName.set(pkg.name, pkg);
190
+ }
191
+ }
192
+ return [...byName.values()];
193
+ };
194
+ exports.discoverTrackunitPackages = discoverTrackunitPackages;
195
+ //# sourceMappingURL=discover-packages.js.map
package/src/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runMigrations = exports.migrate = void 0;
4
+ var generator_1 = require("./generators/migrate/generator");
5
+ Object.defineProperty(exports, "migrate", { enumerable: true, get: function () { return generator_1.migrate; } });
6
+ var generator_2 = require("./generators/run-migrations/generator");
7
+ Object.defineProperty(exports, "runMigrations", { enumerable: true, get: function () { return generator_2.runMigrations; } });
8
+ //# sourceMappingURL=index.js.map