@open-mercato/core 0.4.11-develop.1416.adda6008da → 0.4.11-develop.1436.ff2df4355a
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/modules/configs/cli.js +245 -2
- package/dist/modules/configs/cli.js.map +2 -2
- package/dist/modules/configs/lib/cache-cli.js +143 -0
- package/dist/modules/configs/lib/cache-cli.js.map +7 -0
- package/dist/modules/query_index/lib/engine.js +1 -1
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/package.json +3 -3
- package/src/modules/configs/cli.ts +290 -2
- package/src/modules/configs/lib/cache-cli.ts +174 -0
- package/src/modules/customers/i18n/de.json +6 -0
- package/src/modules/customers/i18n/en.json +6 -0
- package/src/modules/customers/i18n/es.json +6 -0
- package/src/modules/customers/i18n/pl.json +6 -0
- package/src/modules/query_index/lib/engine.ts +1 -1
|
@@ -1,6 +1,221 @@
|
|
|
1
|
+
import { runWithCacheTenant } from "@open-mercato/cache";
|
|
1
2
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
2
3
|
import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
|
|
3
4
|
import { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, NOTIFICATIONS_DELIVERY_CONFIG_KEY } from "../notifications/lib/deliveryConfig.js";
|
|
5
|
+
import { Tenant } from "../directory/data/entities.js";
|
|
6
|
+
import {
|
|
7
|
+
collectCacheStats,
|
|
8
|
+
executeCachePurge,
|
|
9
|
+
previewCachePurge
|
|
10
|
+
} from "./lib/cache-cli.js";
|
|
11
|
+
function parseArgs(rest) {
|
|
12
|
+
const args = {};
|
|
13
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
14
|
+
const part = rest[i];
|
|
15
|
+
if (!part?.startsWith("--")) continue;
|
|
16
|
+
const [rawKey, rawValue] = part.slice(2).split("=");
|
|
17
|
+
if (!rawKey) continue;
|
|
18
|
+
if (rawValue !== void 0) {
|
|
19
|
+
args[rawKey] = rawValue;
|
|
20
|
+
} else if (i + 1 < rest.length && !rest[i + 1].startsWith("--")) {
|
|
21
|
+
args[rawKey] = rest[i + 1];
|
|
22
|
+
i += 1;
|
|
23
|
+
} else {
|
|
24
|
+
args[rawKey] = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return args;
|
|
28
|
+
}
|
|
29
|
+
function stringOption(args, ...keys) {
|
|
30
|
+
for (const key of keys) {
|
|
31
|
+
const raw = args[key];
|
|
32
|
+
if (typeof raw !== "string") continue;
|
|
33
|
+
const trimmed = raw.trim();
|
|
34
|
+
if (trimmed.length > 0) return trimmed;
|
|
35
|
+
}
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
function flagEnabled(args, ...keys) {
|
|
39
|
+
for (const key of keys) {
|
|
40
|
+
const raw = args[key];
|
|
41
|
+
if (raw === void 0) continue;
|
|
42
|
+
if (raw === true) return true;
|
|
43
|
+
if (typeof raw === "string") {
|
|
44
|
+
const parsed = parseBooleanToken(raw);
|
|
45
|
+
return parsed === null ? true : parsed;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
function splitListOption(raw) {
|
|
51
|
+
if (!raw) return [];
|
|
52
|
+
const seen = /* @__PURE__ */ new Set();
|
|
53
|
+
const values = [];
|
|
54
|
+
for (const item of raw.split(",")) {
|
|
55
|
+
const trimmed = item.trim();
|
|
56
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
57
|
+
seen.add(trimmed);
|
|
58
|
+
values.push(trimmed);
|
|
59
|
+
}
|
|
60
|
+
return values;
|
|
61
|
+
}
|
|
62
|
+
async function resolveCacheScopes(em, args) {
|
|
63
|
+
const explicitTenantId = stringOption(args, "tenant", "tenantId");
|
|
64
|
+
const globalOnly = flagEnabled(args, "global");
|
|
65
|
+
const allTenants = flagEnabled(args, "all-tenants", "allTenants");
|
|
66
|
+
if (explicitTenantId && globalOnly) {
|
|
67
|
+
throw new Error("Cannot combine `--tenant` with `--global`.");
|
|
68
|
+
}
|
|
69
|
+
if (explicitTenantId && allTenants) {
|
|
70
|
+
throw new Error("Cannot combine `--tenant` with `--all-tenants`.");
|
|
71
|
+
}
|
|
72
|
+
if (globalOnly && allTenants) {
|
|
73
|
+
throw new Error("Cannot combine `--global` with `--all-tenants`.");
|
|
74
|
+
}
|
|
75
|
+
if (explicitTenantId) {
|
|
76
|
+
return [{ label: `tenant:${explicitTenantId}`, tenantId: explicitTenantId }];
|
|
77
|
+
}
|
|
78
|
+
if (globalOnly) {
|
|
79
|
+
return [{ label: "global", tenantId: null }];
|
|
80
|
+
}
|
|
81
|
+
if (!allTenants) {
|
|
82
|
+
return [{ label: "global", tenantId: null }];
|
|
83
|
+
}
|
|
84
|
+
const tenants = await em.find(Tenant, { deletedAt: null }, { orderBy: { name: "asc" } });
|
|
85
|
+
const scopes = [{ label: "global", tenantId: null }];
|
|
86
|
+
const seen = /* @__PURE__ */ new Set();
|
|
87
|
+
for (const tenant of tenants) {
|
|
88
|
+
const tenantId = typeof tenant.id === "string" ? tenant.id : "";
|
|
89
|
+
if (!tenantId || seen.has(tenantId)) continue;
|
|
90
|
+
seen.add(tenantId);
|
|
91
|
+
scopes.push({ label: `tenant:${tenantId}`, tenantId });
|
|
92
|
+
}
|
|
93
|
+
return scopes;
|
|
94
|
+
}
|
|
95
|
+
function resolveCachePurgeRequest(args) {
|
|
96
|
+
if (flagEnabled(args, "all")) return { kind: "all" };
|
|
97
|
+
const segment = stringOption(args, "segment");
|
|
98
|
+
if (segment) return { kind: "segment", segment };
|
|
99
|
+
const tags = splitListOption(stringOption(args, "tag", "tags"));
|
|
100
|
+
if (tags.length > 0) return { kind: "tags", tags };
|
|
101
|
+
const keys = splitListOption(stringOption(args, "key", "keys"));
|
|
102
|
+
if (keys.length > 0) return { kind: "keys", keys };
|
|
103
|
+
const ids = splitListOption(stringOption(args, "id", "ids"));
|
|
104
|
+
if (ids.length > 0) return { kind: "ids", ids };
|
|
105
|
+
const pattern = stringOption(args, "pattern");
|
|
106
|
+
if (pattern) return { kind: "pattern", pattern };
|
|
107
|
+
throw new Error(
|
|
108
|
+
"Choose a purge target: `--all`, `--segment <id>`, `--tag <tag1,tag2>`, `--key <key1,key2>`, `--id <token1,token2>`, or `--pattern <glob>`."
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
function printCacheHelp() {
|
|
112
|
+
console.log("\u{1F9F9} Cache CLI");
|
|
113
|
+
console.log("");
|
|
114
|
+
console.log("\u{1F680} Usage:");
|
|
115
|
+
console.log(" yarn mercato configs cache stats [--tenant <id> | --global | --all-tenants] [--json]");
|
|
116
|
+
console.log(" yarn mercato configs cache purge --all [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]");
|
|
117
|
+
console.log(" yarn mercato configs cache purge --segment <segment> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]");
|
|
118
|
+
console.log(" yarn mercato configs cache purge --tag <tag1,tag2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]");
|
|
119
|
+
console.log(" yarn mercato configs cache purge --key <key1,key2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]");
|
|
120
|
+
console.log(" yarn mercato configs cache purge --id <token1,token2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]");
|
|
121
|
+
console.log(" yarn mercato configs cache purge --pattern <glob> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]");
|
|
122
|
+
console.log(" yarn mercato configs cache structural [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]");
|
|
123
|
+
console.log("");
|
|
124
|
+
console.log("\u2139\uFE0F Notes:");
|
|
125
|
+
console.log(" `stats` mirrors the cache admin page segment overview for CRUD/widget caches.");
|
|
126
|
+
console.log(" `purge --id` removes every key whose name contains the provided token (for example a user id or entity id).");
|
|
127
|
+
console.log(" `structural` targets navigation caches (`nav:*`) and is the recommended post-step after module/sidebar structure changes.");
|
|
128
|
+
console.log(" When no scope flag is supplied, this command uses the global cache scope only.");
|
|
129
|
+
}
|
|
130
|
+
async function disposeContainer(container) {
|
|
131
|
+
const disposable = container;
|
|
132
|
+
if (typeof disposable.dispose === "function") {
|
|
133
|
+
await disposable.dispose();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function runCacheStats(args) {
|
|
137
|
+
const json = flagEnabled(args, "json");
|
|
138
|
+
const container = await createRequestContainer();
|
|
139
|
+
try {
|
|
140
|
+
const em = container.resolve("em");
|
|
141
|
+
const cache = container.resolve("cache");
|
|
142
|
+
const scopes = await resolveCacheScopes(em, args);
|
|
143
|
+
const results = [];
|
|
144
|
+
for (const scope of scopes) {
|
|
145
|
+
const stats = await runWithCacheTenant(scope.tenantId, async () => collectCacheStats(cache));
|
|
146
|
+
results.push({ scope: scope.label, ...stats });
|
|
147
|
+
}
|
|
148
|
+
if (json) {
|
|
149
|
+
console.log(JSON.stringify(results, null, 2));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
for (const result of results) {
|
|
153
|
+
console.log(`\u{1F50E} [cache] scope=${result.scope} totalKeys=${result.totalKeys} generatedAt=${result.generatedAt}`);
|
|
154
|
+
if (result.segments.length === 0) {
|
|
155
|
+
console.log(" \u2205 segments: none");
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
for (const segment of result.segments) {
|
|
159
|
+
console.log(` \u2022 ${segment.segment} (${segment.keyCount})${segment.path ? ` ${segment.path}` : ""}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} finally {
|
|
163
|
+
await disposeContainer(container);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function runCachePurge(args) {
|
|
167
|
+
const json = flagEnabled(args, "json");
|
|
168
|
+
const quiet = flagEnabled(args, "quiet");
|
|
169
|
+
const dryRun = flagEnabled(args, "dry-run", "dryRun");
|
|
170
|
+
const request = resolveCachePurgeRequest(args);
|
|
171
|
+
const container = await createRequestContainer();
|
|
172
|
+
try {
|
|
173
|
+
const em = container.resolve("em");
|
|
174
|
+
const cache = container.resolve("cache");
|
|
175
|
+
const scopes = await resolveCacheScopes(em, args);
|
|
176
|
+
const results = [];
|
|
177
|
+
for (const scope of scopes) {
|
|
178
|
+
const result = await runWithCacheTenant(
|
|
179
|
+
scope.tenantId,
|
|
180
|
+
async () => dryRun ? previewCachePurge(cache, request) : executeCachePurge(cache, request)
|
|
181
|
+
);
|
|
182
|
+
results.push({
|
|
183
|
+
scope: scope.label,
|
|
184
|
+
dryRun,
|
|
185
|
+
request,
|
|
186
|
+
deleted: result.deleted,
|
|
187
|
+
keyCount: result.keys.length,
|
|
188
|
+
keys: result.keys,
|
|
189
|
+
note: result.note
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (json) {
|
|
193
|
+
console.log(JSON.stringify(results, null, 2));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (quiet) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
for (const result of results) {
|
|
200
|
+
console.log(`${result.dryRun ? "\u{1F9EA}" : "\u{1F9F9}"} [cache] scope=${result.scope} deleted=${result.deleted}${result.dryRun ? " (dry-run)" : ""}`);
|
|
201
|
+
if (result.note) console.log(` \u2139\uFE0F note: ${result.note}`);
|
|
202
|
+
if (result.keys.length > 0) {
|
|
203
|
+
for (const key of result.keys) {
|
|
204
|
+
console.log(` \u2022 ${key}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} finally {
|
|
209
|
+
await disposeContainer(container);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async function runStructuralCachePurge(args) {
|
|
213
|
+
const nextArgs = {
|
|
214
|
+
...args,
|
|
215
|
+
pattern: "nav:*"
|
|
216
|
+
};
|
|
217
|
+
await runCachePurge(nextArgs);
|
|
218
|
+
}
|
|
4
219
|
function envDisablesAutoIndexing() {
|
|
5
220
|
const raw = process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING;
|
|
6
221
|
if (!raw) return false;
|
|
@@ -49,11 +264,39 @@ const restoreDefaults = {
|
|
|
49
264
|
const help = {
|
|
50
265
|
command: "help",
|
|
51
266
|
async run() {
|
|
52
|
-
console.log("
|
|
267
|
+
console.log("\u2699\uFE0F Configs CLI");
|
|
268
|
+
console.log("");
|
|
269
|
+
console.log("\u{1F680} Usage: yarn mercato configs restore-defaults");
|
|
53
270
|
console.log(" Ensures global module configuration defaults exist.");
|
|
271
|
+
console.log("");
|
|
272
|
+
printCacheHelp();
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
const cacheCommand = {
|
|
276
|
+
command: "cache",
|
|
277
|
+
async run(rest) {
|
|
278
|
+
const [subcommand, ...subRest] = rest;
|
|
279
|
+
const args = parseArgs(subRest);
|
|
280
|
+
if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
281
|
+
printCacheHelp();
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (subcommand === "stats") {
|
|
285
|
+
await runCacheStats(args);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (subcommand === "purge") {
|
|
289
|
+
await runCachePurge(args);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (subcommand === "structural") {
|
|
293
|
+
await runStructuralCachePurge(args);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
throw new Error(`Unknown cache subcommand "${subcommand}".`);
|
|
54
297
|
}
|
|
55
298
|
};
|
|
56
|
-
var cli_default = [restoreDefaults, help];
|
|
299
|
+
var cli_default = [restoreDefaults, cacheCommand, help];
|
|
57
300
|
export {
|
|
58
301
|
cli_default as default
|
|
59
302
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/configs/cli.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { ModuleConfigService } from './lib/module-config-service'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, NOTIFICATIONS_DELIVERY_CONFIG_KEY } from '../notifications/lib/deliveryConfig'\n\nfunction envDisablesAutoIndexing(): boolean {\n const raw = process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING\n if (!raw) return false\n return parseBooleanToken(raw) === true\n}\n\nconst restoreDefaults: ModuleCli = {\n command: 'restore-defaults',\n async run() {\n const container = await createRequestContainer()\n try {\n let service: ModuleConfigService\n try {\n service = (container.resolve('moduleConfigService') as ModuleConfigService)\n } catch {\n console.error('[configs] moduleConfigService is not registered in the container.')\n return\n }\n\n const disabledByEnv = envDisablesAutoIndexing()\n const defaultEnabled = !disabledByEnv\n await service.restoreDefaults(\n [\n {\n moduleId: 'vector',\n name: 'auto_index_enabled',\n value: defaultEnabled,\n },\n {\n moduleId: 'notifications',\n name: NOTIFICATIONS_DELIVERY_CONFIG_KEY,\n value: DEFAULT_NOTIFICATION_DELIVERY_CONFIG,\n },\n ],\n { force: true },\n )\n console.log(\n `[configs] Vector auto-indexing default set to ${defaultEnabled ? 'enabled' : 'disabled'}${\n disabledByEnv ? ' (forced by DISABLE_VECTOR_SEARCH_AUTOINDEXING)' : ''\n }.`,\n )\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n },\n}\n\nconst help: ModuleCli = {\n command: 'help',\n async run() {\n console.log('Usage: yarn mercato configs restore-defaults')\n console.log(' Ensures global module configuration defaults exist.')\n },\n}\n\nexport default [restoreDefaults, help]\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { runWithCacheTenant, type CacheStrategy } from '@open-mercato/cache'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { ModuleConfigService } from './lib/module-config-service'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, NOTIFICATIONS_DELIVERY_CONFIG_KEY } from '../notifications/lib/deliveryConfig'\nimport { Tenant } from '../directory/data/entities'\nimport {\n collectCacheStats,\n executeCachePurge,\n previewCachePurge,\n type CachePurgeRequest,\n} from './lib/cache-cli'\n\ntype ParsedArgs = Record<string, string | boolean>\n\ntype CacheScope = {\n label: string\n tenantId: string | null\n}\n\nfunction parseArgs(rest: string[]): ParsedArgs {\n const args: ParsedArgs = {}\n for (let i = 0; i < rest.length; i += 1) {\n const part = rest[i]\n if (!part?.startsWith('--')) continue\n const [rawKey, rawValue] = part.slice(2).split('=')\n if (!rawKey) continue\n if (rawValue !== undefined) {\n args[rawKey] = rawValue\n } else if (i + 1 < rest.length && !rest[i + 1]!.startsWith('--')) {\n args[rawKey] = rest[i + 1]!\n i += 1\n } else {\n args[rawKey] = true\n }\n }\n return args\n}\n\nfunction stringOption(args: ParsedArgs, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const raw = args[key]\n if (typeof raw !== 'string') continue\n const trimmed = raw.trim()\n if (trimmed.length > 0) return trimmed\n }\n return undefined\n}\n\nfunction flagEnabled(args: ParsedArgs, ...keys: string[]): boolean {\n for (const key of keys) {\n const raw = args[key]\n if (raw === undefined) continue\n if (raw === true) return true\n if (typeof raw === 'string') {\n const parsed = parseBooleanToken(raw)\n return parsed === null ? true : parsed\n }\n }\n return false\n}\n\nfunction splitListOption(raw: string | undefined): string[] {\n if (!raw) return []\n const seen = new Set<string>()\n const values: string[] = []\n for (const item of raw.split(',')) {\n const trimmed = item.trim()\n if (!trimmed || seen.has(trimmed)) continue\n seen.add(trimmed)\n values.push(trimmed)\n }\n return values\n}\n\nasync function resolveCacheScopes(\n em: EntityManager,\n args: ParsedArgs,\n): Promise<CacheScope[]> {\n const explicitTenantId = stringOption(args, 'tenant', 'tenantId')\n const globalOnly = flagEnabled(args, 'global')\n const allTenants = flagEnabled(args, 'all-tenants', 'allTenants')\n\n if (explicitTenantId && globalOnly) {\n throw new Error('Cannot combine `--tenant` with `--global`.')\n }\n if (explicitTenantId && allTenants) {\n throw new Error('Cannot combine `--tenant` with `--all-tenants`.')\n }\n if (globalOnly && allTenants) {\n throw new Error('Cannot combine `--global` with `--all-tenants`.')\n }\n\n if (explicitTenantId) {\n return [{ label: `tenant:${explicitTenantId}`, tenantId: explicitTenantId }]\n }\n\n if (globalOnly) {\n return [{ label: 'global', tenantId: null }]\n }\n\n if (!allTenants) {\n return [{ label: 'global', tenantId: null }]\n }\n\n const tenants = await em.find(Tenant, { deletedAt: null }, { orderBy: { name: 'asc' } })\n const scopes: CacheScope[] = [{ label: 'global', tenantId: null }]\n const seen = new Set<string>()\n for (const tenant of tenants) {\n const tenantId = typeof tenant.id === 'string' ? tenant.id : ''\n if (!tenantId || seen.has(tenantId)) continue\n seen.add(tenantId)\n scopes.push({ label: `tenant:${tenantId}`, tenantId })\n }\n return scopes\n}\n\nfunction resolveCachePurgeRequest(args: ParsedArgs): CachePurgeRequest {\n if (flagEnabled(args, 'all')) return { kind: 'all' }\n\n const segment = stringOption(args, 'segment')\n if (segment) return { kind: 'segment', segment }\n\n const tags = splitListOption(stringOption(args, 'tag', 'tags'))\n if (tags.length > 0) return { kind: 'tags', tags }\n\n const keys = splitListOption(stringOption(args, 'key', 'keys'))\n if (keys.length > 0) return { kind: 'keys', keys }\n\n const ids = splitListOption(stringOption(args, 'id', 'ids'))\n if (ids.length > 0) return { kind: 'ids', ids }\n\n const pattern = stringOption(args, 'pattern')\n if (pattern) return { kind: 'pattern', pattern }\n\n throw new Error(\n 'Choose a purge target: `--all`, `--segment <id>`, `--tag <tag1,tag2>`, `--key <key1,key2>`, `--id <token1,token2>`, or `--pattern <glob>`.',\n )\n}\n\nfunction printCacheHelp() {\n console.log('\uD83E\uDDF9 Cache CLI')\n console.log('')\n console.log('\uD83D\uDE80 Usage:')\n console.log(' yarn mercato configs cache stats [--tenant <id> | --global | --all-tenants] [--json]')\n console.log(' yarn mercato configs cache purge --all [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --segment <segment> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --tag <tag1,tag2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --key <key1,key2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --id <token1,token2> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache purge --pattern <glob> [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log(' yarn mercato configs cache structural [--tenant <id> | --global | --all-tenants] [--dry-run] [--json]')\n console.log('')\n console.log('\u2139\uFE0F Notes:')\n console.log(' `stats` mirrors the cache admin page segment overview for CRUD/widget caches.')\n console.log(' `purge --id` removes every key whose name contains the provided token (for example a user id or entity id).')\n console.log(' `structural` targets navigation caches (`nav:*`) and is the recommended post-step after module/sidebar structure changes.')\n console.log(' When no scope flag is supplied, this command uses the global cache scope only.')\n}\n\nasync function disposeContainer(container: unknown) {\n const disposable = container as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n}\n\nasync function runCacheStats(args: ParsedArgs) {\n const json = flagEnabled(args, 'json')\n const container = await createRequestContainer()\n try {\n const em = container.resolve('em') as EntityManager\n const cache = container.resolve('cache') as CacheStrategy\n const scopes = await resolveCacheScopes(em, args)\n const results = []\n for (const scope of scopes) {\n const stats = await runWithCacheTenant(scope.tenantId, async () => collectCacheStats(cache))\n results.push({ scope: scope.label, ...stats })\n }\n\n if (json) {\n console.log(JSON.stringify(results, null, 2))\n return\n }\n\n for (const result of results) {\n console.log(`\uD83D\uDD0E [cache] scope=${result.scope} totalKeys=${result.totalKeys} generatedAt=${result.generatedAt}`)\n if (result.segments.length === 0) {\n console.log(' \u2205 segments: none')\n continue\n }\n for (const segment of result.segments) {\n console.log(` \u2022 ${segment.segment} (${segment.keyCount})${segment.path ? ` ${segment.path}` : ''}`)\n }\n }\n } finally {\n await disposeContainer(container)\n }\n}\n\nasync function runCachePurge(args: ParsedArgs) {\n const json = flagEnabled(args, 'json')\n const quiet = flagEnabled(args, 'quiet')\n const dryRun = flagEnabled(args, 'dry-run', 'dryRun')\n const request = resolveCachePurgeRequest(args)\n const container = await createRequestContainer()\n try {\n const em = container.resolve('em') as EntityManager\n const cache = container.resolve('cache') as CacheStrategy\n const scopes = await resolveCacheScopes(em, args)\n const results = []\n\n for (const scope of scopes) {\n const result = await runWithCacheTenant(scope.tenantId, async () =>\n dryRun ? previewCachePurge(cache, request) : executeCachePurge(cache, request)\n )\n results.push({\n scope: scope.label,\n dryRun,\n request,\n deleted: result.deleted,\n keyCount: result.keys.length,\n keys: result.keys,\n note: result.note,\n })\n }\n\n if (json) {\n console.log(JSON.stringify(results, null, 2))\n return\n }\n\n if (quiet) {\n return\n }\n\n for (const result of results) {\n console.log(`${result.dryRun ? '\uD83E\uDDEA' : '\uD83E\uDDF9'} [cache] scope=${result.scope} deleted=${result.deleted}${result.dryRun ? ' (dry-run)' : ''}`)\n if (result.note) console.log(` \u2139\uFE0F note: ${result.note}`)\n if (result.keys.length > 0) {\n for (const key of result.keys) {\n console.log(` \u2022 ${key}`)\n }\n }\n }\n } finally {\n await disposeContainer(container)\n }\n}\n\nasync function runStructuralCachePurge(args: ParsedArgs) {\n const nextArgs: ParsedArgs = {\n ...args,\n pattern: 'nav:*',\n }\n await runCachePurge(nextArgs)\n}\n\nfunction envDisablesAutoIndexing(): boolean {\n const raw = process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING\n if (!raw) return false\n return parseBooleanToken(raw) === true\n}\n\nconst restoreDefaults: ModuleCli = {\n command: 'restore-defaults',\n async run() {\n const container = await createRequestContainer()\n try {\n let service: ModuleConfigService\n try {\n service = (container.resolve('moduleConfigService') as ModuleConfigService)\n } catch {\n console.error('[configs] moduleConfigService is not registered in the container.')\n return\n }\n\n const disabledByEnv = envDisablesAutoIndexing()\n const defaultEnabled = !disabledByEnv\n await service.restoreDefaults(\n [\n {\n moduleId: 'vector',\n name: 'auto_index_enabled',\n value: defaultEnabled,\n },\n {\n moduleId: 'notifications',\n name: NOTIFICATIONS_DELIVERY_CONFIG_KEY,\n value: DEFAULT_NOTIFICATION_DELIVERY_CONFIG,\n },\n ],\n { force: true },\n )\n console.log(\n `[configs] Vector auto-indexing default set to ${defaultEnabled ? 'enabled' : 'disabled'}${\n disabledByEnv ? ' (forced by DISABLE_VECTOR_SEARCH_AUTOINDEXING)' : ''\n }.`,\n )\n } finally {\n const disposable = container as unknown as { dispose?: () => Promise<void> }\n if (typeof disposable.dispose === 'function') {\n await disposable.dispose()\n }\n }\n },\n}\n\nconst help: ModuleCli = {\n command: 'help',\n async run() {\n console.log('\u2699\uFE0F Configs CLI')\n console.log('')\n console.log('\uD83D\uDE80 Usage: yarn mercato configs restore-defaults')\n console.log(' Ensures global module configuration defaults exist.')\n console.log('')\n printCacheHelp()\n },\n}\n\nconst cacheCommand: ModuleCli = {\n command: 'cache',\n async run(rest) {\n const [subcommand, ...subRest] = rest\n const args = parseArgs(subRest)\n\n if (!subcommand || subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {\n printCacheHelp()\n return\n }\n\n if (subcommand === 'stats') {\n await runCacheStats(args)\n return\n }\n\n if (subcommand === 'purge') {\n await runCachePurge(args)\n return\n }\n\n if (subcommand === 'structural') {\n await runStructuralCachePurge(args)\n return\n }\n\n throw new Error(`Unknown cache subcommand \"${subcommand}\".`)\n },\n}\n\nexport default [restoreDefaults, cacheCommand, help]\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { collectCrudCacheStats, purgeCrudCacheSegment } from "@open-mercato/shared/lib/crud/cache-stats";
|
|
2
|
+
function normalizeUnique(values) {
|
|
3
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4
|
+
const normalized = [];
|
|
5
|
+
for (const value of values) {
|
|
6
|
+
const trimmed = value.trim();
|
|
7
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
8
|
+
seen.add(trimmed);
|
|
9
|
+
normalized.push(trimmed);
|
|
10
|
+
}
|
|
11
|
+
return normalized;
|
|
12
|
+
}
|
|
13
|
+
async function deleteKeys(cache, keys) {
|
|
14
|
+
let deleted = 0;
|
|
15
|
+
for (const key of keys) {
|
|
16
|
+
const removed = await cache.delete(key);
|
|
17
|
+
if (removed) deleted += 1;
|
|
18
|
+
}
|
|
19
|
+
return deleted;
|
|
20
|
+
}
|
|
21
|
+
async function resolveIdentifierKeys(cache, ids) {
|
|
22
|
+
const matches = /* @__PURE__ */ new Set();
|
|
23
|
+
for (const id of normalizeUnique(ids)) {
|
|
24
|
+
const keys = await cache.keys(`*${id}*`);
|
|
25
|
+
for (const key of keys) matches.add(key);
|
|
26
|
+
}
|
|
27
|
+
return Array.from(matches).sort((a, b) => a.localeCompare(b));
|
|
28
|
+
}
|
|
29
|
+
async function collectCacheStats(cache) {
|
|
30
|
+
const stats = await collectCrudCacheStats(cache);
|
|
31
|
+
return {
|
|
32
|
+
generatedAt: stats.generatedAt,
|
|
33
|
+
totalKeys: stats.totalKeys,
|
|
34
|
+
segments: stats.segments.map((segment) => ({
|
|
35
|
+
segment: segment.segment,
|
|
36
|
+
keyCount: segment.keyCount,
|
|
37
|
+
method: segment.method,
|
|
38
|
+
path: segment.path,
|
|
39
|
+
resource: segment.resource
|
|
40
|
+
}))
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function previewCachePurge(cache, request) {
|
|
44
|
+
if (request.kind === "all") {
|
|
45
|
+
const keys2 = (await cache.keys()).sort((a, b) => a.localeCompare(b));
|
|
46
|
+
return { deleted: keys2.length, keys: keys2, note: null };
|
|
47
|
+
}
|
|
48
|
+
if (request.kind === "segment") {
|
|
49
|
+
const stats = await collectCrudCacheStats(cache);
|
|
50
|
+
const target = stats.segments.find((segment) => segment.segment === request.segment);
|
|
51
|
+
return {
|
|
52
|
+
deleted: target?.keys.length ?? 0,
|
|
53
|
+
keys: (target?.keys ?? []).slice().sort((a, b) => a.localeCompare(b)),
|
|
54
|
+
note: target ? null : `Cache segment "${request.segment}" was not found in this scope.`
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (request.kind === "tags") {
|
|
58
|
+
return {
|
|
59
|
+
deleted: 0,
|
|
60
|
+
keys: [],
|
|
61
|
+
note: "Tag purges do not expose matching keys through the cache interface. Run without `--dry-run` to execute."
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (request.kind === "keys") {
|
|
65
|
+
const requested = normalizeUnique(request.keys);
|
|
66
|
+
const existingKeys = await cache.keys();
|
|
67
|
+
const existingSet = new Set(existingKeys);
|
|
68
|
+
const keys2 = requested.filter((key) => existingSet.has(key));
|
|
69
|
+
return {
|
|
70
|
+
deleted: keys2.length,
|
|
71
|
+
keys: keys2,
|
|
72
|
+
note: keys2.length === requested.length ? null : "Some requested keys were not present in this scope."
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (request.kind === "ids") {
|
|
76
|
+
const keys2 = await resolveIdentifierKeys(cache, request.ids);
|
|
77
|
+
return {
|
|
78
|
+
deleted: keys2.length,
|
|
79
|
+
keys: keys2,
|
|
80
|
+
note: keys2.length > 0 ? null : "No cache keys matched the requested identifier tokens."
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const keys = (await cache.keys(request.pattern)).sort((a, b) => a.localeCompare(b));
|
|
84
|
+
return {
|
|
85
|
+
deleted: keys.length,
|
|
86
|
+
keys,
|
|
87
|
+
note: keys.length > 0 ? null : `No cache keys matched pattern "${request.pattern}".`
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async function executeCachePurge(cache, request) {
|
|
91
|
+
if (request.kind === "all") {
|
|
92
|
+
const keys2 = (await cache.keys()).sort((a, b) => a.localeCompare(b));
|
|
93
|
+
const deleted2 = await cache.clear();
|
|
94
|
+
return { deleted: deleted2, keys: keys2, note: null };
|
|
95
|
+
}
|
|
96
|
+
if (request.kind === "segment") {
|
|
97
|
+
const result = await purgeCrudCacheSegment(cache, request.segment);
|
|
98
|
+
return {
|
|
99
|
+
deleted: result.deleted,
|
|
100
|
+
keys: result.keys.slice().sort((a, b) => a.localeCompare(b)),
|
|
101
|
+
note: result.keys.length > 0 ? null : `Cache segment "${request.segment}" was not found in this scope.`
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (request.kind === "tags") {
|
|
105
|
+
const deleted2 = await cache.deleteByTags(normalizeUnique(request.tags));
|
|
106
|
+
return {
|
|
107
|
+
deleted: deleted2,
|
|
108
|
+
keys: [],
|
|
109
|
+
note: "Tag purges report counts only because the cache interface does not expose tag-to-key listings."
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (request.kind === "keys") {
|
|
113
|
+
const preview = await previewCachePurge(cache, request);
|
|
114
|
+
const deleted2 = await deleteKeys(cache, preview.keys);
|
|
115
|
+
return {
|
|
116
|
+
deleted: deleted2,
|
|
117
|
+
keys: preview.keys,
|
|
118
|
+
note: preview.note
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (request.kind === "ids") {
|
|
122
|
+
const keys2 = await resolveIdentifierKeys(cache, request.ids);
|
|
123
|
+
const deleted2 = await deleteKeys(cache, keys2);
|
|
124
|
+
return {
|
|
125
|
+
deleted: deleted2,
|
|
126
|
+
keys: keys2,
|
|
127
|
+
note: keys2.length > 0 ? null : "No cache keys matched the requested identifier tokens."
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const keys = (await cache.keys(request.pattern)).sort((a, b) => a.localeCompare(b));
|
|
131
|
+
const deleted = await deleteKeys(cache, keys);
|
|
132
|
+
return {
|
|
133
|
+
deleted,
|
|
134
|
+
keys,
|
|
135
|
+
note: keys.length > 0 ? null : `No cache keys matched pattern "${request.pattern}".`
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export {
|
|
139
|
+
collectCacheStats,
|
|
140
|
+
executeCachePurge,
|
|
141
|
+
previewCachePurge
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=cache-cli.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/configs/lib/cache-cli.ts"],
|
|
4
|
+
"sourcesContent": ["import type { CacheStrategy } from '@open-mercato/cache'\nimport { collectCrudCacheStats, purgeCrudCacheSegment } from '@open-mercato/shared/lib/crud/cache-stats'\n\nexport type CachePurgeRequest =\n | { kind: 'all' }\n | { kind: 'segment'; segment: string }\n | { kind: 'tags'; tags: string[] }\n | { kind: 'keys'; keys: string[] }\n | { kind: 'ids'; ids: string[] }\n | { kind: 'pattern'; pattern: string }\n\nexport type CachePurgeResult = {\n deleted: number\n keys: string[]\n note: string | null\n}\n\nfunction normalizeUnique(values: string[]): string[] {\n const seen = new Set<string>()\n const normalized: string[] = []\n for (const value of values) {\n const trimmed = value.trim()\n if (!trimmed || seen.has(trimmed)) continue\n seen.add(trimmed)\n normalized.push(trimmed)\n }\n return normalized\n}\n\nasync function deleteKeys(cache: CacheStrategy, keys: string[]): Promise<number> {\n let deleted = 0\n for (const key of keys) {\n const removed = await cache.delete(key)\n if (removed) deleted += 1\n }\n return deleted\n}\n\nasync function resolveIdentifierKeys(cache: CacheStrategy, ids: string[]): Promise<string[]> {\n const matches = new Set<string>()\n for (const id of normalizeUnique(ids)) {\n const keys = await cache.keys(`*${id}*`)\n for (const key of keys) matches.add(key)\n }\n return Array.from(matches).sort((a, b) => a.localeCompare(b))\n}\n\nexport async function collectCacheStats(cache: CacheStrategy) {\n const stats = await collectCrudCacheStats(cache)\n return {\n generatedAt: stats.generatedAt,\n totalKeys: stats.totalKeys,\n segments: stats.segments.map((segment) => ({\n segment: segment.segment,\n keyCount: segment.keyCount,\n method: segment.method,\n path: segment.path,\n resource: segment.resource,\n })),\n }\n}\n\nexport async function previewCachePurge(\n cache: CacheStrategy,\n request: CachePurgeRequest,\n): Promise<CachePurgeResult> {\n if (request.kind === 'all') {\n const keys = (await cache.keys()).sort((a, b) => a.localeCompare(b))\n return { deleted: keys.length, keys, note: null }\n }\n\n if (request.kind === 'segment') {\n const stats = await collectCrudCacheStats(cache)\n const target = stats.segments.find((segment) => segment.segment === request.segment)\n return {\n deleted: target?.keys.length ?? 0,\n keys: (target?.keys ?? []).slice().sort((a, b) => a.localeCompare(b)),\n note: target ? null : `Cache segment \"${request.segment}\" was not found in this scope.`,\n }\n }\n\n if (request.kind === 'tags') {\n return {\n deleted: 0,\n keys: [],\n note: 'Tag purges do not expose matching keys through the cache interface. Run without `--dry-run` to execute.',\n }\n }\n\n if (request.kind === 'keys') {\n const requested = normalizeUnique(request.keys)\n const existingKeys = await cache.keys()\n const existingSet = new Set(existingKeys)\n const keys = requested.filter((key) => existingSet.has(key))\n return {\n deleted: keys.length,\n keys,\n note: keys.length === requested.length ? null : 'Some requested keys were not present in this scope.',\n }\n }\n\n if (request.kind === 'ids') {\n const keys = await resolveIdentifierKeys(cache, request.ids)\n return {\n deleted: keys.length,\n keys,\n note: keys.length > 0 ? null : 'No cache keys matched the requested identifier tokens.',\n }\n }\n\n const keys = (await cache.keys(request.pattern)).sort((a, b) => a.localeCompare(b))\n return {\n deleted: keys.length,\n keys,\n note: keys.length > 0 ? null : `No cache keys matched pattern \"${request.pattern}\".`,\n }\n}\n\nexport async function executeCachePurge(\n cache: CacheStrategy,\n request: CachePurgeRequest,\n): Promise<CachePurgeResult> {\n if (request.kind === 'all') {\n const keys = (await cache.keys()).sort((a, b) => a.localeCompare(b))\n const deleted = await cache.clear()\n return { deleted, keys, note: null }\n }\n\n if (request.kind === 'segment') {\n const result = await purgeCrudCacheSegment(cache, request.segment)\n return {\n deleted: result.deleted,\n keys: result.keys.slice().sort((a, b) => a.localeCompare(b)),\n note: result.keys.length > 0 ? null : `Cache segment \"${request.segment}\" was not found in this scope.`,\n }\n }\n\n if (request.kind === 'tags') {\n const deleted = await cache.deleteByTags(normalizeUnique(request.tags))\n return {\n deleted,\n keys: [],\n note: 'Tag purges report counts only because the cache interface does not expose tag-to-key listings.',\n }\n }\n\n if (request.kind === 'keys') {\n const preview = await previewCachePurge(cache, request)\n const deleted = await deleteKeys(cache, preview.keys)\n return {\n deleted,\n keys: preview.keys,\n note: preview.note,\n }\n }\n\n if (request.kind === 'ids') {\n const keys = await resolveIdentifierKeys(cache, request.ids)\n const deleted = await deleteKeys(cache, keys)\n return {\n deleted,\n keys,\n note: keys.length > 0 ? null : 'No cache keys matched the requested identifier tokens.',\n }\n }\n\n const keys = (await cache.keys(request.pattern)).sort((a, b) => a.localeCompare(b))\n const deleted = await deleteKeys(cache, keys)\n return {\n deleted,\n keys,\n note: keys.length > 0 ? null : `No cache keys matched pattern \"${request.pattern}\".`,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["keys", "deleted"]
|
|
7
|
+
}
|
|
@@ -631,7 +631,7 @@ class HybridQueryEngine {
|
|
|
631
631
|
};
|
|
632
632
|
const applyJoinSearchFilterOp = async (target, filter, _qualified, join) => {
|
|
633
633
|
if (!searchEnabled || !join.entityId) return false;
|
|
634
|
-
if (!["
|
|
634
|
+
if (!["like", "ilike"].includes(filter.op)) return false;
|
|
635
635
|
if (typeof filter.value !== "string" || filter.value.trim().length === 0) return false;
|
|
636
636
|
let searchAvailable = joinSearchAvailability.get(join.entityId);
|
|
637
637
|
if (searchAvailable === void 0) {
|