@_linked/server 2.15.0 → 2.16.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.
@@ -0,0 +1,66 @@
1
+ /**
2
+ * A linked package found in the app's dependency tree, wherever it is
3
+ * installed: a workspace member, a localized checkout, or a registry install.
4
+ */
5
+ export interface LinkedDependency {
6
+ packageName: string;
7
+ /** Realpath of the package root. */
8
+ path: string;
9
+ }
10
+ /**
11
+ * A package whose backend providers the server loads at boot: one whose
12
+ * package.json declares `"linkedPackage": true`. That flag is the only test —
13
+ * not the package's scope. `@_linked/localize` is in the `@_linked` scope and
14
+ * is a CLI tool, not a linked package; a custom-scope package with the flag is
15
+ * a linked package. The CLI's workspace discovery uses the same flag.
16
+ *
17
+ * The legacy `"lincd": true` flag is deliberately NOT enough. Those packages
18
+ * import the old `lincd` core, and loading them at boot is what produced
19
+ * "Multiple versions of LINCD are loaded" before boot loading was narrowed to
20
+ * workspace members.
21
+ */
22
+ export declare function isLinkedPackageJson(json: any): boolean;
23
+ /**
24
+ * Find an installed package the way Node does: `<dir>/node_modules/<name>`,
25
+ * then each parent directory's `node_modules`. Returns null when it is not
26
+ * installed (an optional dependency, say) rather than throwing.
27
+ */
28
+ export declare function readInstalledPackage(name: string, fromDir: string): {
29
+ root: string;
30
+ json: any;
31
+ } | null;
32
+ /**
33
+ * Every linked package reachable from the app's `dependencies`, each one AFTER
34
+ * the linked packages it depends on. Providers register middleware in the
35
+ * order they are indexed, so a package's middleware must not run before that
36
+ * of a package it builds on (e.g. anything reading `request.linkedAuth` after
37
+ * @_linked/auth's JWT middleware).
38
+ *
39
+ * Only linked packages are recursed into — the same rule the workspace-only
40
+ * filter used — so the walk reads a few dozen package.json files, not the
41
+ * whole of node_modules. Each package's dependencies are resolved from its
42
+ * REAL location, so a symlinked workspace or localized checkout resolves its
43
+ * own dependencies the way Node will when it loads it.
44
+ */
45
+ export declare function discoverLinkedDependencies(appDir: string, appPackageJson: any): LinkedDependency[];
46
+ /**
47
+ * Whether the dev SSR runner BUNDLES `pkgName` (it is in Vite's
48
+ * `ssr.noExternal`) rather than handing it to Node.
49
+ *
50
+ * This decides which loader a package's backend must be loaded with. A module
51
+ * has to be evaluated by the same loader that evaluates the package everywhere
52
+ * else in the SSR graph, or it runs twice:
53
+ *
54
+ * - `vite.ssrLoadModule('<external pkg>/backend')` transforms that entry and
55
+ * its relative imports even though the package is external, while the app's
56
+ * bare imports of the same package are evaluated by Node — two copies of the
57
+ * package's module state.
58
+ * - a plain `import()` of a BUNDLED package goes to Node, while the app's
59
+ * imports of it are evaluated by Vite — the same split the other way round.
60
+ *
61
+ * Vite matches `noExternal` entries against the bare package name, so a
62
+ * subpath (`pkg/backend`) is covered by an entry for `pkg`.
63
+ */
64
+ export declare function isBundledBySsr(pkgName: string, noExternal: unknown): boolean;
65
+ /** The dev SSR runner's resolved `noExternal`, wherever this Vite keeps it. */
66
+ export declare function ssrNoExternalOf(vite: any): unknown;
@@ -0,0 +1,123 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ /**
4
+ * A package whose backend providers the server loads at boot: one whose
5
+ * package.json declares `"linkedPackage": true`. That flag is the only test —
6
+ * not the package's scope. `@_linked/localize` is in the `@_linked` scope and
7
+ * is a CLI tool, not a linked package; a custom-scope package with the flag is
8
+ * a linked package. The CLI's workspace discovery uses the same flag.
9
+ *
10
+ * The legacy `"lincd": true` flag is deliberately NOT enough. Those packages
11
+ * import the old `lincd` core, and loading them at boot is what produced
12
+ * "Multiple versions of LINCD are loaded" before boot loading was narrowed to
13
+ * workspace members.
14
+ */
15
+ export function isLinkedPackageJson(json) {
16
+ if (!json || typeof json.name !== 'string')
17
+ return false;
18
+ return json.linkedPackage === true;
19
+ }
20
+ /**
21
+ * Find an installed package the way Node does: `<dir>/node_modules/<name>`,
22
+ * then each parent directory's `node_modules`. Returns null when it is not
23
+ * installed (an optional dependency, say) rather than throwing.
24
+ */
25
+ export function readInstalledPackage(name, fromDir) {
26
+ let dir = path.resolve(fromDir);
27
+ while (true) {
28
+ const root = path.join(dir, 'node_modules', name);
29
+ const pkgJsonPath = path.join(root, 'package.json');
30
+ if (fs.existsSync(pkgJsonPath)) {
31
+ try {
32
+ return { root, json: JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) };
33
+ }
34
+ catch (_a) {
35
+ return null;
36
+ }
37
+ }
38
+ const parent = path.dirname(dir);
39
+ if (parent === dir)
40
+ return null;
41
+ dir = parent;
42
+ }
43
+ }
44
+ /**
45
+ * Every linked package reachable from the app's `dependencies`, each one AFTER
46
+ * the linked packages it depends on. Providers register middleware in the
47
+ * order they are indexed, so a package's middleware must not run before that
48
+ * of a package it builds on (e.g. anything reading `request.linkedAuth` after
49
+ * @_linked/auth's JWT middleware).
50
+ *
51
+ * Only linked packages are recursed into — the same rule the workspace-only
52
+ * filter used — so the walk reads a few dozen package.json files, not the
53
+ * whole of node_modules. Each package's dependencies are resolved from its
54
+ * REAL location, so a symlinked workspace or localized checkout resolves its
55
+ * own dependencies the way Node will when it loads it.
56
+ */
57
+ export function discoverLinkedDependencies(appDir, appPackageJson) {
58
+ var _a;
59
+ const out = [];
60
+ const seen = new Set();
61
+ if (appPackageJson === null || appPackageJson === void 0 ? void 0 : appPackageJson.name)
62
+ seen.add(appPackageJson.name);
63
+ const visit = (name, fromDir) => {
64
+ var _a;
65
+ if (seen.has(name))
66
+ return;
67
+ seen.add(name);
68
+ const installed = readInstalledPackage(name, fromDir);
69
+ if (!installed || !isLinkedPackageJson(installed.json))
70
+ return;
71
+ let realRoot = installed.root;
72
+ try {
73
+ realRoot = fs.realpathSync(installed.root);
74
+ }
75
+ catch (_b) { }
76
+ for (const dep of Object.keys((_a = installed.json.dependencies) !== null && _a !== void 0 ? _a : {})) {
77
+ visit(dep, realRoot);
78
+ }
79
+ out.push({ packageName: name, path: realRoot });
80
+ };
81
+ for (const name of Object.keys((_a = appPackageJson === null || appPackageJson === void 0 ? void 0 : appPackageJson.dependencies) !== null && _a !== void 0 ? _a : {})) {
82
+ visit(name, appDir);
83
+ }
84
+ return out;
85
+ }
86
+ /**
87
+ * Whether the dev SSR runner BUNDLES `pkgName` (it is in Vite's
88
+ * `ssr.noExternal`) rather than handing it to Node.
89
+ *
90
+ * This decides which loader a package's backend must be loaded with. A module
91
+ * has to be evaluated by the same loader that evaluates the package everywhere
92
+ * else in the SSR graph, or it runs twice:
93
+ *
94
+ * - `vite.ssrLoadModule('<external pkg>/backend')` transforms that entry and
95
+ * its relative imports even though the package is external, while the app's
96
+ * bare imports of the same package are evaluated by Node — two copies of the
97
+ * package's module state.
98
+ * - a plain `import()` of a BUNDLED package goes to Node, while the app's
99
+ * imports of it are evaluated by Vite — the same split the other way round.
100
+ *
101
+ * Vite matches `noExternal` entries against the bare package name, so a
102
+ * subpath (`pkg/backend`) is covered by an entry for `pkg`.
103
+ */
104
+ export function isBundledBySsr(pkgName, noExternal) {
105
+ if (noExternal === true)
106
+ return true;
107
+ const entries = Array.isArray(noExternal)
108
+ ? noExternal
109
+ : noExternal
110
+ ? [noExternal]
111
+ : [];
112
+ return entries.some((entry) => typeof entry === 'string'
113
+ ? entry === pkgName
114
+ : entry instanceof RegExp
115
+ ? entry.test(pkgName)
116
+ : false);
117
+ }
118
+ /** The dev SSR runner's resolved `noExternal`, wherever this Vite keeps it. */
119
+ export function ssrNoExternalOf(vite) {
120
+ var _a, _b, _c, _d, _e, _f, _g;
121
+ return ((_c = (_b = (_a = vite === null || vite === void 0 ? void 0 : vite.config) === null || _a === void 0 ? void 0 : _a.ssr) === null || _b === void 0 ? void 0 : _b.noExternal) !== null && _c !== void 0 ? _c : (_g = (_f = (_e = (_d = vite === null || vite === void 0 ? void 0 : vite.config) === null || _d === void 0 ? void 0 : _d.environments) === null || _e === void 0 ? void 0 : _e.ssr) === null || _f === void 0 ? void 0 : _f.resolve) === null || _g === void 0 ? void 0 : _g.noExternal);
122
+ }
123
+ //# sourceMappingURL=linkedDependencies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linkedDependencies.js","sourceRoot":"","sources":["../../../src/utils/linkedDependencies.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\n/**\n * A linked package found in the app's dependency tree, wherever it is\n * installed: a workspace member, a localized checkout, or a registry install.\n */\nexport interface LinkedDependency {\n packageName: string;\n /** Realpath of the package root. */\n path: string;\n}\n\n/**\n * A package whose backend providers the server loads at boot: one whose\n * package.json declares `\"linkedPackage\": true`. That flag is the only test —\n * not the package's scope. `@_linked/localize` is in the `@_linked` scope and\n * is a CLI tool, not a linked package; a custom-scope package with the flag is\n * a linked package. The CLI's workspace discovery uses the same flag.\n *\n * The legacy `\"lincd\": true` flag is deliberately NOT enough. Those packages\n * import the old `lincd` core, and loading them at boot is what produced\n * \"Multiple versions of LINCD are loaded\" before boot loading was narrowed to\n * workspace members.\n */\nexport function isLinkedPackageJson(json: any): boolean {\n if (!json || typeof json.name !== 'string') return false;\n return json.linkedPackage === true;\n}\n\n/**\n * Find an installed package the way Node does: `<dir>/node_modules/<name>`,\n * then each parent directory's `node_modules`. Returns null when it is not\n * installed (an optional dependency, say) rather than throwing.\n */\nexport function readInstalledPackage(\n name: string,\n fromDir: string\n): { root: string; json: any } | null {\n let dir = path.resolve(fromDir);\n while (true) {\n const root = path.join(dir, 'node_modules', name);\n const pkgJsonPath = path.join(root, 'package.json');\n if (fs.existsSync(pkgJsonPath)) {\n try {\n return { root, json: JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) };\n } catch {\n return null;\n }\n }\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/**\n * Every linked package reachable from the app's `dependencies`, each one AFTER\n * the linked packages it depends on. Providers register middleware in the\n * order they are indexed, so a package's middleware must not run before that\n * of a package it builds on (e.g. anything reading `request.linkedAuth` after\n * @_linked/auth's JWT middleware).\n *\n * Only linked packages are recursed into — the same rule the workspace-only\n * filter used — so the walk reads a few dozen package.json files, not the\n * whole of node_modules. Each package's dependencies are resolved from its\n * REAL location, so a symlinked workspace or localized checkout resolves its\n * own dependencies the way Node will when it loads it.\n */\nexport function discoverLinkedDependencies(\n appDir: string,\n appPackageJson: any\n): LinkedDependency[] {\n const out: LinkedDependency[] = [];\n const seen = new Set<string>();\n if (appPackageJson?.name) seen.add(appPackageJson.name);\n\n const visit = (name: string, fromDir: string) => {\n if (seen.has(name)) return;\n seen.add(name);\n const installed = readInstalledPackage(name, fromDir);\n if (!installed || !isLinkedPackageJson(installed.json)) return;\n let realRoot = installed.root;\n try {\n realRoot = fs.realpathSync(installed.root);\n } catch {}\n for (const dep of Object.keys(installed.json.dependencies ?? {})) {\n visit(dep, realRoot);\n }\n out.push({ packageName: name, path: realRoot });\n };\n\n for (const name of Object.keys(appPackageJson?.dependencies ?? {})) {\n visit(name, appDir);\n }\n return out;\n}\n\n/**\n * Whether the dev SSR runner BUNDLES `pkgName` (it is in Vite's\n * `ssr.noExternal`) rather than handing it to Node.\n *\n * This decides which loader a package's backend must be loaded with. A module\n * has to be evaluated by the same loader that evaluates the package everywhere\n * else in the SSR graph, or it runs twice:\n *\n * - `vite.ssrLoadModule('<external pkg>/backend')` transforms that entry and\n * its relative imports even though the package is external, while the app's\n * bare imports of the same package are evaluated by Node — two copies of the\n * package's module state.\n * - a plain `import()` of a BUNDLED package goes to Node, while the app's\n * imports of it are evaluated by Vite — the same split the other way round.\n *\n * Vite matches `noExternal` entries against the bare package name, so a\n * subpath (`pkg/backend`) is covered by an entry for `pkg`.\n */\nexport function isBundledBySsr(pkgName: string, noExternal: unknown): boolean {\n if (noExternal === true) return true;\n const entries = Array.isArray(noExternal)\n ? noExternal\n : noExternal\n ? [noExternal]\n : [];\n return entries.some((entry) =>\n typeof entry === 'string'\n ? entry === pkgName\n : entry instanceof RegExp\n ? entry.test(pkgName)\n : false\n );\n}\n\n/** The dev SSR runner's resolved `noExternal`, wherever this Vite keeps it. */\nexport function ssrNoExternalOf(vite: any): unknown {\n return (\n vite?.config?.ssr?.noExternal ??\n vite?.config?.environments?.ssr?.resolve?.noExternal\n );\n}\n"]}
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/linked-fw/server.git"
12
12
  },
13
- "version": "2.15.0",
13
+ "version": "2.16.0",
14
14
  "packageManager": "npm@11.20.0",
15
15
  "linkedPackage": true,
16
16
  "type": "module",
@@ -68,7 +68,7 @@
68
68
  "@changesets/cli": "^3.0.0",
69
69
  "@jest/globals": "^29.7.0",
70
70
  "@types/jest": "^29.5.14",
71
- "@types/node": "^20.12.7",
71
+ "@types/node": "^24.0.0",
72
72
  "@types/react-dom": "^19.3.0",
73
73
  "copyfiles": "^2.4.1",
74
74
  "cross-env": "^7.0.3",