@_linked/server 2.14.3 → 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"]}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Boot-time shape materialization: write the shapes this process registered into
3
+ * its app-data dataset as SHACL, via core `syncShapes`.
4
+ *
5
+ * Kept apart from `LinkedServer` so the orphan policy can be tested without
6
+ * booting a server.
7
+ */
8
+ /** The orphan policies a booting server may run with. */
9
+ export type BootOrphanScope = 'all' | 'none';
10
+ /** The subset of core's `syncShapes` this module calls. */
11
+ export type SyncShapesFn = (ds: unknown, options: {
12
+ orphanScope: BootOrphanScope;
13
+ }) => Promise<Array<() => Promise<void>>>;
14
+ /**
15
+ * The orphan policy for boot-time sync.
16
+ *
17
+ * `'none'` unless the operator sets `LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true`.
18
+ *
19
+ * Boot sync must not delete store shapes the process did not register, because
20
+ * the registry is routinely a subset of what the dataset holds: shapes are
21
+ * deep-importable one file at a time, an app's backend may import no shapes at
22
+ * all, and an app-data dataset also holds shapes other writers put there (Create
23
+ * Now's `bindShape` / `enableCapability`). With pruning on, an app whose backend
24
+ * imports no shapes deletes every NodeShape in its app-data on each boot.
25
+ * `'ownedNamespaces'` is deliberately not offered: a namespace is a whole
26
+ * package, so a deep import of one shape file still prunes the rest of it.
27
+ */
28
+ export declare function bootOrphanScope(env?: Record<string, string | undefined>): BootOrphanScope;
29
+ /**
30
+ * Plan and run a sync of every registered shape into `ds`, in batches of 8 so
31
+ * Fuseki is not flooded. The orphan policy is always passed explicitly — an app
32
+ * may be running a core whose `syncShapes` still defaults to pruning.
33
+ *
34
+ * Logs one line naming the policy that ran.
35
+ */
36
+ export declare function materializeShapes(ds: unknown, syncShapes: SyncShapesFn, { env, log, }?: {
37
+ env?: Record<string, string | undefined>;
38
+ log?: (message: string) => void;
39
+ }): Promise<{
40
+ count: number;
41
+ orphanScope: BootOrphanScope;
42
+ }>;
@@ -1 +1,47 @@
1
+ /**
2
+ * Boot-time shape materialization: write the shapes this process registered into
3
+ * its app-data dataset as SHACL, via core `syncShapes`.
4
+ *
5
+ * Kept apart from `LinkedServer` so the orphan policy can be tested without
6
+ * booting a server.
7
+ */
8
+ /**
9
+ * The orphan policy for boot-time sync.
10
+ *
11
+ * `'none'` unless the operator sets `LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true`.
12
+ *
13
+ * Boot sync must not delete store shapes the process did not register, because
14
+ * the registry is routinely a subset of what the dataset holds: shapes are
15
+ * deep-importable one file at a time, an app's backend may import no shapes at
16
+ * all, and an app-data dataset also holds shapes other writers put there (Create
17
+ * Now's `bindShape` / `enableCapability`). With pruning on, an app whose backend
18
+ * imports no shapes deletes every NodeShape in its app-data on each boot.
19
+ * `'ownedNamespaces'` is deliberately not offered: a namespace is a whole
20
+ * package, so a deep import of one shape file still prunes the rest of it.
21
+ */
22
+ export function bootOrphanScope(env = process.env) {
23
+ return env.LINKED_SYNC_SHAPES_PRUNE_ORPHANS === 'true' ? 'all' : 'none';
24
+ }
25
+ /**
26
+ * Plan and run a sync of every registered shape into `ds`, in batches of 8 so
27
+ * Fuseki is not flooded. The orphan policy is always passed explicitly — an app
28
+ * may be running a core whose `syncShapes` still defaults to pruning.
29
+ *
30
+ * Logs one line naming the policy that ran.
31
+ */
32
+ export async function materializeShapes(ds, syncShapes, { env = process.env, log = console.log, } = {}) {
33
+ const orphanScope = bootOrphanScope(env);
34
+ const thunks = await syncShapes(ds, { orphanScope });
35
+ for (let i = 0; i < thunks.length; i += 8) {
36
+ await Promise.all(thunks.slice(i, i + 8).map((run) => run()));
37
+ }
38
+ log(orphanScope === 'all'
39
+ ? `[LinkedServer] synced shapes into app-data with orphanScope 'all' ` +
40
+ `(LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true): ${thunks.length} operation(s), ` +
41
+ `store shapes not registered in this process were deleted`
42
+ : `[LinkedServer] synced shapes into app-data with orphanScope 'none': ` +
43
+ `${thunks.length} shape(s) written, no store shapes deleted ` +
44
+ `(set LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true to prune)`);
45
+ return { count: thunks.length, orphanScope };
46
+ }
1
47
  //# sourceMappingURL=syncShapes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncShapes.js","sourceRoot":"","sources":["../../../src/utils/syncShapes.ts"],"names":[],"mappings":"","sourcesContent":[""]}
1
+ {"version":3,"file":"syncShapes.js","sourceRoot":"","sources":["../../../src/utils/syncShapes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA0C,OAAO,CAAC,GAAG;IAErD,OAAO,GAAG,CAAC,gCAAgC,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;AAC1E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAW,EACX,UAAwB,EACxB,EACE,GAAG,GAAG,OAAO,CAAC,GAAG,EACjB,GAAG,GAAG,OAAO,CAAC,GAAG,MAIf,EAAE;IAEN,MAAM,WAAW,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,EAAE,EAAE,EAAC,WAAW,EAAC,CAAC,CAAC;IACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,GAAG,CACD,WAAW,KAAK,KAAK;QACnB,CAAC,CAAC,oEAAoE;YAClE,4CAA4C,MAAM,CAAC,MAAM,iBAAiB;YAC1E,0DAA0D;QAC9D,CAAC,CAAC,sEAAsE;YACpE,GAAG,MAAM,CAAC,MAAM,6CAA6C;YAC7D,sDAAsD,CAC7D,CAAC;IACF,OAAO,EAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAC,CAAC;AAC7C,CAAC","sourcesContent":["/**\n * Boot-time shape materialization: write the shapes this process registered into\n * its app-data dataset as SHACL, via core `syncShapes`.\n *\n * Kept apart from `LinkedServer` so the orphan policy can be tested without\n * booting a server.\n */\n\n/** The orphan policies a booting server may run with. */\nexport type BootOrphanScope = 'all' | 'none';\n\n/** The subset of core's `syncShapes` this module calls. */\nexport type SyncShapesFn = (\n ds: unknown,\n options: {orphanScope: BootOrphanScope},\n) => Promise<Array<() => Promise<void>>>;\n\n/**\n * The orphan policy for boot-time sync.\n *\n * `'none'` unless the operator sets `LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true`.\n *\n * Boot sync must not delete store shapes the process did not register, because\n * the registry is routinely a subset of what the dataset holds: shapes are\n * deep-importable one file at a time, an app's backend may import no shapes at\n * all, and an app-data dataset also holds shapes other writers put there (Create\n * Now's `bindShape` / `enableCapability`). With pruning on, an app whose backend\n * imports no shapes deletes every NodeShape in its app-data on each boot.\n * `'ownedNamespaces'` is deliberately not offered: a namespace is a whole\n * package, so a deep import of one shape file still prunes the rest of it.\n */\nexport function bootOrphanScope(\n env: Record<string, string | undefined> = process.env,\n): BootOrphanScope {\n return env.LINKED_SYNC_SHAPES_PRUNE_ORPHANS === 'true' ? 'all' : 'none';\n}\n\n/**\n * Plan and run a sync of every registered shape into `ds`, in batches of 8 so\n * Fuseki is not flooded. The orphan policy is always passed explicitly — an app\n * may be running a core whose `syncShapes` still defaults to pruning.\n *\n * Logs one line naming the policy that ran.\n */\nexport async function materializeShapes(\n ds: unknown,\n syncShapes: SyncShapesFn,\n {\n env = process.env,\n log = console.log,\n }: {\n env?: Record<string, string | undefined>;\n log?: (message: string) => void;\n } = {},\n): Promise<{count: number; orphanScope: BootOrphanScope}> {\n const orphanScope = bootOrphanScope(env);\n const thunks = await syncShapes(ds, {orphanScope});\n for (let i = 0; i < thunks.length; i += 8) {\n await Promise.all(thunks.slice(i, i + 8).map((run) => run()));\n }\n log(\n orphanScope === 'all'\n ? `[LinkedServer] synced shapes into app-data with orphanScope 'all' ` +\n `(LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true): ${thunks.length} operation(s), ` +\n `store shapes not registered in this process were deleted`\n : `[LinkedServer] synced shapes into app-data with orphanScope 'none': ` +\n `${thunks.length} shape(s) written, no store shapes deleted ` +\n `(set LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true to prune)`,\n );\n return {count: thunks.length, orphanScope};\n}\n"]}
package/package.json CHANGED
@@ -10,8 +10,8 @@
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/linked-fw/server.git"
12
12
  },
13
- "version": "2.14.3",
14
- "packageManager": "npm@11.19.1",
13
+ "version": "2.16.0",
14
+ "packageManager": "npm@11.20.0",
15
15
  "linkedPackage": true,
16
16
  "type": "module",
17
17
  "main": "lib/esm/index.js",
@@ -64,11 +64,11 @@
64
64
  "webpack-hot-middleware": "^2.26.1"
65
65
  },
66
66
  "devDependencies": {
67
- "@changesets/changelog-github": "^0.5.2",
68
- "@changesets/cli": "^2.29.8",
67
+ "@changesets/changelog-github": "^1.0.0",
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",