@jwilleke/ngdpbase-types 5.36.0 → 5.37.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/lib/packages/ngdpbase-types/testing.js +1 -1
- package/lib/src/managers/AddonsManager.d.ts +2 -19
- package/lib/src/managers/ConfigurationManager.d.ts +5 -6
- package/lib/src/utils/addonConfigLayer.d.ts +2 -4
- package/lib/src/utils/addonConfigLayer.js +10 -17
- package/lib/src/utils/addonsPathResolver.d.ts +17 -48
- package/lib/src/utils/addonsPathResolver.js +23 -83
- package/package.json +4 -5
|
@@ -48,7 +48,7 @@ export function openAddonTestDatabase(owner, options, declaredBy) {
|
|
|
48
48
|
checkAddonDatabaseRequest(owner, options);
|
|
49
49
|
if (declaredBy) {
|
|
50
50
|
const { slug, defaults } = declaredBy;
|
|
51
|
-
const layer = addonDatabaseLayer([{ slug, dir: '',
|
|
51
|
+
const layer = addonDatabaseLayer([{ slug, dir: '', enabled: true, defaults }], {});
|
|
52
52
|
const refused = layer.refused.get(slug);
|
|
53
53
|
if (refused)
|
|
54
54
|
throw new Error(`Add-on '${slug}' is not loaded: ${refused}`);
|
|
@@ -342,8 +342,6 @@ declare class AddonsManager extends BaseManager {
|
|
|
342
342
|
private addonsPaths;
|
|
343
343
|
/** Resolved absolute paths to addons directories */
|
|
344
344
|
private resolvedAddonsPaths;
|
|
345
|
-
/** #673: npm package globs (from `node_modules:` addons-path entries) to discover from node_modules. */
|
|
346
|
-
private npmAddonPatterns;
|
|
347
345
|
/** #1830: `<data>/addons`, where installed add-ons live; null when there is no data folder. */
|
|
348
346
|
private installedDir;
|
|
349
347
|
/** #1830: the installed add-ons verified this boot — the only ones loaded from `installedDir`. */
|
|
@@ -461,25 +459,10 @@ declare class AddonsManager extends BaseManager {
|
|
|
461
459
|
* Scan a single addons directory and register any add-ons found.
|
|
462
460
|
*/
|
|
463
461
|
private scanAddonsDirectory;
|
|
464
|
-
/**
|
|
465
|
-
* #673 (packaged model): discover addons published as npm packages, matching a
|
|
466
|
-
* `node_modules:<glob>` addons-path entry (e.g. `node_modules:@jwilleke/*-addon`).
|
|
467
|
-
* Each matching package is loaded through the same slug/module/register()
|
|
468
|
-
* contract as bundled and drop-in addons — only discovery differs.
|
|
469
|
-
*/
|
|
470
|
-
private scanNpmAddons;
|
|
471
|
-
/** Locate the node_modules directory (app root / cwd). */
|
|
472
|
-
private findNodeModules;
|
|
473
|
-
/** Expand a `@scope/glob` (or bare `glob`) pattern to package directories. */
|
|
474
|
-
private resolveNpmAddonDirs;
|
|
475
462
|
/**
|
|
476
463
|
* Load a single addon from its directory (index.js/ts + optional package.json
|
|
477
|
-
* `ngdpbase` manifest) and register it.
|
|
478
|
-
*
|
|
479
|
-
* no-slug identity fallback (#927): a directory addon's folder name is its
|
|
480
|
-
* identity verbatim, while an npm package strips the conventional `-addon`
|
|
481
|
-
* suffix. Duplicate ids are skipped — since npm discovery runs last, a
|
|
482
|
-
* bundled/drop-in addon wins.
|
|
464
|
+
* `ngdpbase` manifest) and register it. `sourceLabel` is for logging only.
|
|
465
|
+
* Duplicate ids are skipped, so the first directory scanned wins.
|
|
483
466
|
*/
|
|
484
467
|
private registerAddonFromDir;
|
|
485
468
|
/**
|
|
@@ -141,16 +141,15 @@ declare class ConfigurationManager extends BaseManager {
|
|
|
141
141
|
/**
|
|
142
142
|
* #672: refuse to start if any `ngdpbase.addons.<id>.enabled = true` key
|
|
143
143
|
* references an `<id>` that has no matching addon in any configured
|
|
144
|
-
* `addons-path`
|
|
145
|
-
*
|
|
144
|
+
* `addons-path` directory (bundled/drop-in) and is not named in
|
|
145
|
+
* `ngdpbase.addons.install`. A retired `node_modules:` entry is ignored (#2102).
|
|
146
146
|
* The discovery logic mirrors what `AddonsManager` does at runtime —
|
|
147
147
|
* directory/package present with `index.js` or `index.ts` — but without
|
|
148
148
|
* importing the modules (boot-time speed; `ConfigurationManager.initialize()`
|
|
149
|
-
* runs before any managers). The
|
|
150
|
-
*
|
|
149
|
+
* runs before any managers). The addons-path parsing AND the
|
|
150
|
+
* canonical-identity resolution are shared with
|
|
151
151
|
* `AddonsManager` via `utils/addonsPathResolver.ts` so the two can no
|
|
152
|
-
* longer drift out of sync (#924
|
|
153
|
-
* never boot as a result).
|
|
152
|
+
* longer drift out of sync (#924).
|
|
154
153
|
*
|
|
155
154
|
* Catches the failure mode that caused the 2026-05-10 geohazardwatch.com
|
|
156
155
|
* outage: the deploy configmap had `ngdpbase.addons.ve-geology.enabled =
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* shipped entry. The operator's file still wins over everything.
|
|
19
19
|
*
|
|
20
20
|
* Discovery follows `ngdpbase.managers.addons-manager.addons-path` exactly as
|
|
21
|
-
* AddonsManager does —
|
|
22
|
-
* and
|
|
21
|
+
* AddonsManager does — directory entries — so bundled
|
|
22
|
+
* and drop-in addons are treated alike. Only an addon whose
|
|
23
23
|
* `ngdpbase.addons.<slug>.enabled` is true in the shipped+custom view
|
|
24
24
|
* contributes; a disabled addon's opinions do not reach the catalog.
|
|
25
25
|
*
|
|
@@ -63,7 +63,6 @@ export declare const ADDONS_MANAGER_ENABLED_KEY = "ngdpbase.managers.addons-mana
|
|
|
63
63
|
export interface AddonDefaults {
|
|
64
64
|
slug: string;
|
|
65
65
|
dir: string;
|
|
66
|
-
source: 'directory' | 'npm';
|
|
67
66
|
/**
|
|
68
67
|
* Whether the addon is switched on. A turned-off addon's file is read for its
|
|
69
68
|
* system-categories alone (#1559); none of its other keys reach the merge.
|
|
@@ -83,7 +82,6 @@ export declare function isAddonEnabled(base: Record<string, unknown>, slug: stri
|
|
|
83
82
|
*/
|
|
84
83
|
export declare function discoverAddonDirs(base: Record<string, unknown>, cwd?: string, dataFolder?: string): Array<{
|
|
85
84
|
dir: string;
|
|
86
|
-
source: 'directory' | 'npm';
|
|
87
85
|
}>;
|
|
88
86
|
/**
|
|
89
87
|
* The defaults of every installed addon, enabled or not, in discovery order,
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* shipped entry. The operator's file still wins over everything.
|
|
19
19
|
*
|
|
20
20
|
* Discovery follows `ngdpbase.managers.addons-manager.addons-path` exactly as
|
|
21
|
-
* AddonsManager does —
|
|
22
|
-
* and
|
|
21
|
+
* AddonsManager does — directory entries — so bundled
|
|
22
|
+
* and drop-in addons are treated alike. Only an addon whose
|
|
23
23
|
* `ngdpbase.addons.<slug>.enabled` is true in the shipped+custom view
|
|
24
24
|
* contributes; a disabled addon's opinions do not reach the catalog.
|
|
25
25
|
*
|
|
@@ -61,7 +61,7 @@ import fs from 'fs';
|
|
|
61
61
|
import path from 'path';
|
|
62
62
|
import { isDeepStrictEqual } from 'util';
|
|
63
63
|
import { configFilePaths, deepMergeConfigs, deepMergeObjects, instanceDataFolder, isPlainObject, readConfigFilesSync } from './configFiles.js';
|
|
64
|
-
import { configuredInstallSlugs,
|
|
64
|
+
import { configuredInstallSlugs, installedAddonsDir, resolveAddonSlug, splitAddonsPath } from './addonsPathResolver.js';
|
|
65
65
|
import { lockedAddonDirsSync } from './addonLock.js';
|
|
66
66
|
import { LEGACY_SECRET_KEYS_KEY, SENSITIVE_VALUES_KEY } from './sensitiveValues.js';
|
|
67
67
|
import { parsePrivatestore } from './privateStorePath.js';
|
|
@@ -81,7 +81,7 @@ export function isAddonEnabled(base, slug) {
|
|
|
81
81
|
export function discoverAddonDirs(base, cwd = process.cwd(), dataFolder = instanceDataFolder()) {
|
|
82
82
|
if (base[ADDONS_MANAGER_ENABLED_KEY] === false)
|
|
83
83
|
return [];
|
|
84
|
-
const { directories
|
|
84
|
+
const { directories } = splitAddonsPath(base[ADDONS_PATH_KEY] ?? './addons');
|
|
85
85
|
const found = [];
|
|
86
86
|
for (const raw of directories) {
|
|
87
87
|
const root = path.resolve(cwd, raw);
|
|
@@ -95,20 +95,13 @@ export function discoverAddonDirs(base, cwd = process.cwd(), dataFolder = instan
|
|
|
95
95
|
for (const entry of entries) {
|
|
96
96
|
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'shared')
|
|
97
97
|
continue;
|
|
98
|
-
found.push({ dir: path.join(root, entry.name)
|
|
98
|
+
found.push({ dir: path.join(root, entry.name) });
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
const installed = installedAddonsDir(dataFolder, cwd);
|
|
102
102
|
if (!found.some((f) => path.dirname(f.dir) === installed)) {
|
|
103
103
|
for (const dir of lockedAddonDirsSync(installed, configuredInstallSlugs(base)))
|
|
104
|
-
found.push({ dir
|
|
105
|
-
}
|
|
106
|
-
const nmRoot = npmPatterns.length > 0 ? findNodeModulesDir(cwd) : null;
|
|
107
|
-
if (nmRoot) {
|
|
108
|
-
for (const pattern of npmPatterns) {
|
|
109
|
-
for (const dir of matchNpmPackageDirs(nmRoot, pattern))
|
|
110
|
-
found.push({ dir, source: 'npm' });
|
|
111
|
-
}
|
|
104
|
+
found.push({ dir });
|
|
112
105
|
}
|
|
113
106
|
return found;
|
|
114
107
|
}
|
|
@@ -122,8 +115,8 @@ export function discoverAddonDirs(base, cwd = process.cwd(), dataFolder = instan
|
|
|
122
115
|
*/
|
|
123
116
|
export function discoverAddonDefaults(base, cwd = process.cwd(), dataFolder = instanceDataFolder()) {
|
|
124
117
|
const out = [];
|
|
125
|
-
for (const { dir
|
|
126
|
-
const slug = resolveAddonSlug(dir
|
|
118
|
+
for (const { dir } of discoverAddonDirs(base, cwd, dataFolder)) {
|
|
119
|
+
const slug = resolveAddonSlug(dir);
|
|
127
120
|
const enabled = isAddonEnabled(base, slug);
|
|
128
121
|
const file = path.join(dir, 'config', 'default-config.json');
|
|
129
122
|
if (!fs.existsSync(file))
|
|
@@ -136,10 +129,10 @@ export function discoverAddonDefaults(base, cwd = process.cwd(), dataFolder = in
|
|
|
136
129
|
for (const [key, value] of Object.entries(raw))
|
|
137
130
|
if (!key.startsWith('_'))
|
|
138
131
|
defaults[key] = value;
|
|
139
|
-
out.push({ slug, dir,
|
|
132
|
+
out.push({ slug, dir, enabled, defaults });
|
|
140
133
|
}
|
|
141
134
|
catch (err) {
|
|
142
|
-
out.push({ slug, dir,
|
|
135
|
+
out.push({ slug, dir, enabled, defaults: {}, error: err instanceof Error ? err.message : String(err) });
|
|
143
136
|
}
|
|
144
137
|
}
|
|
145
138
|
return out;
|
|
@@ -1,30 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared parsing/discovery for the `ngdpbase.managers.addons-manager.addons-path`
|
|
3
|
-
* config value
|
|
3
|
+
* config value, used by `AddonsManager` (runtime discovery), `ConfigurationManager`
|
|
4
|
+
* (the #672 boot-time "does this enabled addon exist" check) and the add-on
|
|
5
|
+
* config layer, so the three cannot drift (#924).
|
|
4
6
|
*
|
|
5
|
-
* #
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* updated for #673's `node_modules:<glob>` packaged-addon syntax, but the
|
|
9
|
-
* validator's copy was not, so it resolved a `node_modules:` entry as a
|
|
10
|
-
* literal (non-existent) directory and treated every packaged addon as
|
|
11
|
-
* "unknown" — crashing boot the moment one was enabled. Consolidating the
|
|
12
|
-
* parsing/matching into one module removes the class of bug, not just this
|
|
13
|
-
* instance of it.
|
|
7
|
+
* #2102: the packaged model (`node_modules:<glob>` entries) is gone. A site
|
|
8
|
+
* config may still hold such an entry; it is ignored, never treated as a
|
|
9
|
+
* directory, and never stops the boot.
|
|
14
10
|
*/
|
|
15
|
-
/** Prefix marking an addons-path entry as an npm-package glob rather than a directory. */
|
|
16
|
-
export declare const NPM_ADDON_PREFIX = "node_modules:";
|
|
17
11
|
export interface SplitAddonsPath {
|
|
18
12
|
/** Plain directory entries (bundled/drop-in), not yet resolved to absolute. */
|
|
19
13
|
directories: string[];
|
|
20
|
-
/**
|
|
21
|
-
|
|
14
|
+
/** Retired `node_modules:` entries (#2102): ignored; AddonsManager warns about them once at start. */
|
|
15
|
+
retired: string[];
|
|
22
16
|
}
|
|
23
17
|
/**
|
|
24
18
|
* Coerce the raw config value (string or string[]) and split it into
|
|
25
|
-
* filesystem directory entries
|
|
19
|
+
* filesystem directory entries and retired `node_modules:` entries.
|
|
26
20
|
*/
|
|
27
21
|
export declare function splitAddonsPath(raw: unknown): SplitAddonsPath;
|
|
22
|
+
/** The warning for one retired `node_modules:` entry (#2102). */
|
|
23
|
+
export declare function retiredAddonsPathWarning(entry: string): string;
|
|
28
24
|
/** The config key naming the add-ons to install (#1830). */
|
|
29
25
|
export declare const ADDONS_INSTALL_KEY = "ngdpbase.addons.install";
|
|
30
26
|
/**
|
|
@@ -35,42 +31,15 @@ export declare const ADDONS_INSTALL_KEY = "ngdpbase.addons.install";
|
|
|
35
31
|
export declare function installedAddonsDir(dataFolder: string, cwd?: string): string;
|
|
36
32
|
/** The slugs `ngdpbase.addons.install` names, from a merged or shipped+custom view. */
|
|
37
33
|
export declare function configuredInstallSlugs(config: Record<string, unknown>): string[];
|
|
38
|
-
/** Locate the `node_modules` directory to search for packaged addons (resolved from cwd). */
|
|
39
|
-
export declare function findNodeModulesDir(cwd?: string): string | null;
|
|
40
|
-
/**
|
|
41
|
-
* Expand a `@scope/glob` (or bare `glob`) npm pattern to matching package
|
|
42
|
-
* directories under `nmRoot`.
|
|
43
|
-
*/
|
|
44
|
-
export declare function matchNpmPackageDirs(nmRoot: string, pattern: string): string[];
|
|
45
|
-
/**
|
|
46
|
-
* Best-effort addon identity for a packaged (npm) addon directory, derived
|
|
47
|
-
* from its package-directory name alone — no module import, matching the
|
|
48
|
-
* same boot-time-speed, no-module-loading design already accepted for the
|
|
49
|
-
* directory-scan case (see `ConfigurationManager.assertConfiguredAddonsExist`).
|
|
50
|
-
*
|
|
51
|
-
* Follows the documented packaged-addon naming convention
|
|
52
|
-
* (`@scope/<slug>-addon`, see docs/platform/deployment/addon-packaged.md):
|
|
53
|
-
* strips a trailing `-addon` suffix if present, else returns the folder name
|
|
54
|
-
* as-is.
|
|
55
|
-
*
|
|
56
|
-
* Caveat (same class as the existing directory-name heuristic): the addon's
|
|
57
|
-
* *real* identity is whatever its module exports as `name` at runtime. A
|
|
58
|
-
* package that doesn't follow the `-addon` naming convention, or whose
|
|
59
|
-
* module exports a different `name`, won't be matched precisely here — this
|
|
60
|
-
* is a validation-time approximation, not the source of truth.
|
|
61
|
-
*/
|
|
62
|
-
export declare function deriveAddonSlugFromPackageDirName(dirName: string): string;
|
|
63
34
|
/**
|
|
64
35
|
* Canonical identity for an addon directory (#927), resolved the SAME
|
|
65
36
|
* import-free way in every layer (discovery, `isEnabled`, the boot-time
|
|
66
|
-
* validator)
|
|
67
|
-
* config key, manifest slug) can no longer drift:
|
|
37
|
+
* validator):
|
|
68
38
|
*
|
|
69
|
-
* canonicalId = package.json `ngdpbase.slug` ??
|
|
39
|
+
* canonicalId = package.json `ngdpbase.slug` ?? the folder name verbatim
|
|
70
40
|
*
|
|
71
|
-
* Reads `package.json` statically — never imports the module
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* label validated against this at load time, not a source of identity.
|
|
41
|
+
* Reads `package.json` statically — never imports the module. The module's
|
|
42
|
+
* exported `name` is a display label validated against this at load time,
|
|
43
|
+
* not a source of identity.
|
|
75
44
|
*/
|
|
76
|
-
export declare function resolveAddonSlug(addonDir: string
|
|
45
|
+
export declare function resolveAddonSlug(addonDir: string): string;
|
|
@@ -1,36 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared parsing/discovery for the `ngdpbase.managers.addons-manager.addons-path`
|
|
3
|
-
* config value
|
|
3
|
+
* config value, used by `AddonsManager` (runtime discovery), `ConfigurationManager`
|
|
4
|
+
* (the #672 boot-time "does this enabled addon exist" check) and the add-on
|
|
5
|
+
* config layer, so the three cannot drift (#924).
|
|
4
6
|
*
|
|
5
|
-
* #
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* updated for #673's `node_modules:<glob>` packaged-addon syntax, but the
|
|
9
|
-
* validator's copy was not, so it resolved a `node_modules:` entry as a
|
|
10
|
-
* literal (non-existent) directory and treated every packaged addon as
|
|
11
|
-
* "unknown" — crashing boot the moment one was enabled. Consolidating the
|
|
12
|
-
* parsing/matching into one module removes the class of bug, not just this
|
|
13
|
-
* instance of it.
|
|
7
|
+
* #2102: the packaged model (`node_modules:<glob>` entries) is gone. A site
|
|
8
|
+
* config may still hold such an entry; it is ignored, never treated as a
|
|
9
|
+
* directory, and never stops the boot.
|
|
14
10
|
*/
|
|
15
11
|
import * as fs from 'fs';
|
|
16
12
|
import * as path from 'path';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
export const NPM_ADDON_PREFIX = 'node_modules:';
|
|
13
|
+
/** The retired packaged-model prefix, recognised only so the entry can be ignored with a warning. */
|
|
14
|
+
const RETIRED_PACKAGED_PREFIX = 'node_modules:';
|
|
20
15
|
/**
|
|
21
16
|
* Coerce the raw config value (string or string[]) and split it into
|
|
22
|
-
* filesystem directory entries
|
|
17
|
+
* filesystem directory entries and retired `node_modules:` entries.
|
|
23
18
|
*/
|
|
24
19
|
export function splitAddonsPath(raw) {
|
|
25
20
|
const entries = Array.isArray(raw) ? raw.map(String) : [String(raw)];
|
|
26
21
|
return {
|
|
27
|
-
directories: entries.filter(p => !p.startsWith(
|
|
28
|
-
|
|
29
|
-
.filter(p => p.startsWith(NPM_ADDON_PREFIX))
|
|
30
|
-
.map(p => p.slice(NPM_ADDON_PREFIX.length).trim())
|
|
31
|
-
.filter(Boolean)
|
|
22
|
+
directories: entries.filter(p => !p.trim().startsWith(RETIRED_PACKAGED_PREFIX)),
|
|
23
|
+
retired: entries.filter(p => p.trim().startsWith(RETIRED_PACKAGED_PREFIX))
|
|
32
24
|
};
|
|
33
25
|
}
|
|
26
|
+
/** The warning for one retired `node_modules:` entry (#2102). */
|
|
27
|
+
export function retiredAddonsPathWarning(entry) {
|
|
28
|
+
return `addons-path entry '${entry}' is no longer supported and is ignored; install add-ons with ngdpbase.addons.install`;
|
|
29
|
+
}
|
|
34
30
|
/** The config key naming the add-ons to install (#1830). */
|
|
35
31
|
export const ADDONS_INSTALL_KEY = 'ngdpbase.addons.install';
|
|
36
32
|
/**
|
|
@@ -46,69 +42,18 @@ export function configuredInstallSlugs(config) {
|
|
|
46
42
|
const map = config[ADDONS_INSTALL_KEY];
|
|
47
43
|
return map && typeof map === 'object' && !Array.isArray(map) ? Object.keys(map) : [];
|
|
48
44
|
}
|
|
49
|
-
/** Locate the `node_modules` directory to search for packaged addons (resolved from cwd). */
|
|
50
|
-
export function findNodeModulesDir(cwd = process.cwd()) {
|
|
51
|
-
const nm = path.resolve(cwd, 'node_modules');
|
|
52
|
-
return fs.existsSync(nm) && fs.statSync(nm).isDirectory() ? nm : null;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Expand a `@scope/glob` (or bare `glob`) npm pattern to matching package
|
|
56
|
-
* directories under `nmRoot`.
|
|
57
|
-
*/
|
|
58
|
-
export function matchNpmPackageDirs(nmRoot, pattern) {
|
|
59
|
-
let baseDir = nmRoot;
|
|
60
|
-
let nameGlob = pattern;
|
|
61
|
-
if (pattern.startsWith('@')) {
|
|
62
|
-
const slash = pattern.indexOf('/');
|
|
63
|
-
const scope = slash > 0 ? pattern.slice(0, slash) : pattern;
|
|
64
|
-
nameGlob = slash > 0 ? pattern.slice(slash + 1) : '*';
|
|
65
|
-
baseDir = path.join(nmRoot, scope);
|
|
66
|
-
}
|
|
67
|
-
if (!fs.existsSync(baseDir))
|
|
68
|
-
return [];
|
|
69
|
-
const out = [];
|
|
70
|
-
for (const entry of fs.readdirSync(baseDir, { withFileTypes: true })) {
|
|
71
|
-
if (entry.isDirectory() && !entry.name.startsWith('.') && minimatch(entry.name, nameGlob)) {
|
|
72
|
-
out.push(path.join(baseDir, entry.name));
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return out;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Best-effort addon identity for a packaged (npm) addon directory, derived
|
|
79
|
-
* from its package-directory name alone — no module import, matching the
|
|
80
|
-
* same boot-time-speed, no-module-loading design already accepted for the
|
|
81
|
-
* directory-scan case (see `ConfigurationManager.assertConfiguredAddonsExist`).
|
|
82
|
-
*
|
|
83
|
-
* Follows the documented packaged-addon naming convention
|
|
84
|
-
* (`@scope/<slug>-addon`, see docs/platform/deployment/addon-packaged.md):
|
|
85
|
-
* strips a trailing `-addon` suffix if present, else returns the folder name
|
|
86
|
-
* as-is.
|
|
87
|
-
*
|
|
88
|
-
* Caveat (same class as the existing directory-name heuristic): the addon's
|
|
89
|
-
* *real* identity is whatever its module exports as `name` at runtime. A
|
|
90
|
-
* package that doesn't follow the `-addon` naming convention, or whose
|
|
91
|
-
* module exports a different `name`, won't be matched precisely here — this
|
|
92
|
-
* is a validation-time approximation, not the source of truth.
|
|
93
|
-
*/
|
|
94
|
-
export function deriveAddonSlugFromPackageDirName(dirName) {
|
|
95
|
-
const suffix = '-addon';
|
|
96
|
-
return dirName.endsWith(suffix) ? dirName.slice(0, -suffix.length) : dirName;
|
|
97
|
-
}
|
|
98
45
|
/**
|
|
99
46
|
* Canonical identity for an addon directory (#927), resolved the SAME
|
|
100
47
|
* import-free way in every layer (discovery, `isEnabled`, the boot-time
|
|
101
|
-
* validator)
|
|
102
|
-
* config key, manifest slug) can no longer drift:
|
|
48
|
+
* validator):
|
|
103
49
|
*
|
|
104
|
-
* canonicalId = package.json `ngdpbase.slug` ??
|
|
50
|
+
* canonicalId = package.json `ngdpbase.slug` ?? the folder name verbatim
|
|
105
51
|
*
|
|
106
|
-
* Reads `package.json` statically — never imports the module
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* label validated against this at load time, not a source of identity.
|
|
52
|
+
* Reads `package.json` statically — never imports the module. The module's
|
|
53
|
+
* exported `name` is a display label validated against this at load time,
|
|
54
|
+
* not a source of identity.
|
|
110
55
|
*/
|
|
111
|
-
export function resolveAddonSlug(addonDir
|
|
56
|
+
export function resolveAddonSlug(addonDir) {
|
|
112
57
|
const dirName = path.basename(addonDir);
|
|
113
58
|
const pkgPath = path.join(addonDir, 'package.json');
|
|
114
59
|
try {
|
|
@@ -118,12 +63,7 @@ export function resolveAddonSlug(addonDir, source) {
|
|
|
118
63
|
return slug.trim();
|
|
119
64
|
}
|
|
120
65
|
catch {
|
|
121
|
-
/* no/invalid package.json — fall back to the folder
|
|
66
|
+
/* no/invalid package.json — fall back to the folder name */
|
|
122
67
|
}
|
|
123
|
-
|
|
124
|
-
// `@scope/<slug>-addon` publishing convention, so strip the conventional
|
|
125
|
-
// trailing `-addon`. A plain directory (bundled/drop-in) addon's folder
|
|
126
|
-
// name IS its identity verbatim — an addon may legitimately be named
|
|
127
|
-
// `something-addon` on disk — so never strip there.
|
|
128
|
-
return source === 'npm' ? deriveAddonSlugFromPackageDirName(dirName) : dirName;
|
|
68
|
+
return dirName;
|
|
129
69
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jwilleke/ngdpbase-types",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.37.0",
|
|
4
4
|
"description": "TypeScript types for ngdpbase add-ons, and a test kit of core's own values. A dev dependency, versioned with ngdpbase.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Jim Willeke",
|
|
@@ -41,15 +41,14 @@
|
|
|
41
41
|
"@types/multer": "^2.3.0",
|
|
42
42
|
"@types/node": "^24.19.2",
|
|
43
43
|
"better-sqlite3-multiple-ciphers": "^13.0.3",
|
|
44
|
-
"dotenv": "^18.0.
|
|
44
|
+
"dotenv": "^18.0.9",
|
|
45
45
|
"express": "^5.3.0",
|
|
46
46
|
"express-session": "^1.19.0",
|
|
47
47
|
"fs-extra": "^11.4.1",
|
|
48
48
|
"ipaddr.js": "^2.5.0",
|
|
49
|
-
"jose": "^6.2.
|
|
50
|
-
"linkedom": "^0.18.
|
|
49
|
+
"jose": "^6.2.13",
|
|
50
|
+
"linkedom": "^0.18.14",
|
|
51
51
|
"markdown-it": "^14.3.3",
|
|
52
|
-
"minimatch": "^10.2.6",
|
|
53
52
|
"minisearch": "7.2.0",
|
|
54
53
|
"multer": "^2.4.0",
|
|
55
54
|
"rrule-temporal": "2.2.8",
|