@murumets-ee/imports 0.114.0 → 0.116.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/dist/en-BzNzkbKr.mjs +1 -0
- package/dist/et-361yFmNl.mjs +1 -0
- package/dist/i18n.d.mts +18 -0
- package/dist/i18n.mjs +1 -0
- package/dist/plugin.d.mts +8 -2
- package/dist/plugin.mjs +1 -1
- package/dist/ru-Dv1mnZaJ.mjs +1 -0
- package/package.json +12 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={Imports:{showcase:{"price-list-imports":{title:`Price list imports`,summary:`Load supplier prices and stock from CSV or Excel.`,body:`Bring in your suppliers' price lists and stock levels from CSV or Excel files. Match the file's columns to your fields once per supplier, and every later file from that supplier goes in the same way. You see a preview first, and any rows that could not be read are listed.`}}}};export{e as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={Imports:{showcase:{"price-list-imports":{title:`Hinnakirjade import`,summary:`Tarnijate hinnad ja laoseis CSV- või Exceli-failist.`,body:`Too tarnijate hinnakirjad ja laoseisud sisse CSV- või Exceli-failidest. Seo faili veerud oma väljadega iga tarnija puhul üks kord ning kõik selle tarnija järgmised failid lähevad sisse samamoodi. Enne näed eelvaadet ning read, mida ei õnnestunud lugeda, tuuakse eraldi välja.`}}}};export{e as default};
|
package/dist/i18n.d.mts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/i18n.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Load `@murumets-ee/imports` interface messages for a given locale.
|
|
4
|
+
*
|
|
5
|
+
* `./i18n` is this package's own tsdown entry, never the package root and
|
|
6
|
+
* never inlined into `dist/plugin.mjs`: `plugin.ts` declares this loader as
|
|
7
|
+
* `shared.messages`, so it is loaded by every `lumi` command at config time,
|
|
8
|
+
* and the loader is ultimately called from next-intl's `getRequestConfig` —
|
|
9
|
+
* both ends have to stay clean of `server-only`. The messages JSON stays
|
|
10
|
+
* behind a dynamic import inside the loader, so declaring the slot costs a
|
|
11
|
+
* function reference and reads no file.
|
|
12
|
+
*
|
|
13
|
+
* The catalogue holds the `Imports` namespace — today the plugin's
|
|
14
|
+
* "Discover Lumi" showcase copy (`Imports.showcase.<id>.*`).
|
|
15
|
+
*/
|
|
16
|
+
declare function getImportsMessages(locale: string): Promise<Record<string, unknown>>;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { getImportsMessages };
|
package/dist/i18n.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
async function e(e){switch(e){case`et`:return(await import(`./et-361yFmNl.mjs`)).default;case`ru`:return(await import(`./ru-Dv1mnZaJ.mjs`)).default;default:return(await import(`./en-BzNzkbKr.mjs`)).default}}export{e as getImportsMessages};
|
package/dist/plugin.d.mts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { n as EsClientResolver, r as FilePathResolver, s as PairSink, t as EnsureIndexResolver } from "./worker-CRH_PjKO.mjs";
|
|
2
2
|
import { ImportsStorageFactory } from "./storage.mjs";
|
|
3
|
+
import { ShowcaseEntry } from "@murumets-ee/core";
|
|
3
4
|
import { IndexConfig } from "@murumets-ee/search-elasticsearch";
|
|
4
5
|
|
|
5
6
|
//#region src/plugin.d.ts
|
|
7
|
+
/** What this plugin lets a business do, for the admin's "Discover Lumi" page. */
|
|
8
|
+
declare const IMPORTS_SHOWCASE: readonly ShowcaseEntry[];
|
|
6
9
|
interface ImportsPluginOptions {
|
|
7
10
|
/**
|
|
8
11
|
* Elasticsearch client resolver. Lazy so consumers can construct
|
|
@@ -141,7 +144,10 @@ interface ImportsPluginOptions {
|
|
|
141
144
|
}
|
|
142
145
|
declare function imports(options: ImportsPluginOptions): Pick<import("@murumets-ee/core").Plugin, "server" | "adminUi" | "postInstall" | "requires" | "contributes"> & {
|
|
143
146
|
readonly name: "@murumets-ee/imports";
|
|
144
|
-
readonly shared?:
|
|
147
|
+
readonly shared?: {
|
|
148
|
+
readonly messages: (locale: string) => Promise<Record<string, unknown>>;
|
|
149
|
+
readonly showcase: readonly ShowcaseEntry[];
|
|
150
|
+
};
|
|
145
151
|
};
|
|
146
152
|
//#endregion
|
|
147
|
-
export { ImportsPluginOptions, imports };
|
|
153
|
+
export { IMPORTS_SHOWCASE, ImportsPluginOptions, imports };
|
package/dist/plugin.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,o as t,u as n}from"./transform-Cu5YEoOl.mjs";import{registerImportsStorageFactory as r}from"./storage.mjs";import{i,t as a}from"./carmaker-feed-B-QYr3PI.mjs";import{n as o,t as s}from"./mapping-transform-CO4MNhMe.mjs";import{createImportsRunDeadListener as c,createRunImportHandler as l,importsRunJob as u}from"./worker.mjs";import{definePlugin as d}from"@murumets-ee/core";import{z as f}from"zod";import{inArray as p}from"drizzle-orm";import{createIndex as m,dropIndex as h,readAliasTargets as g,reindex as _}from"@murumets-ee/search-elasticsearch";import{
|
|
1
|
+
import{n as e,o as t,u as n}from"./transform-Cu5YEoOl.mjs";import{registerImportsStorageFactory as r}from"./storage.mjs";import{i,t as a}from"./carmaker-feed-B-QYr3PI.mjs";import{n as o,t as s}from"./mapping-transform-CO4MNhMe.mjs";import{createImportsRunDeadListener as c,createRunImportHandler as l,importsRunJob as u}from"./worker.mjs";import{definePlugin as d}from"@murumets-ee/core";import{z as f}from"zod";import{inArray as p}from"drizzle-orm";import{createIndex as m,dropIndex as h,readAliasTargets as g,reindex as _}from"@murumets-ee/search-elasticsearch";import{getImportsMessages as v}from"@murumets-ee/imports/i18n";import{defineJob as y}from"@murumets-ee/queue/client";const b=f.object({toIndex:f.string().min(1).optional(),allowExisting:f.boolean().optional(),dropOld:f.boolean().optional(),batchSize:f.number().int().min(1).max(1e4).optional()}),x=y({name:`imports:reindex`,description:`Copy the aliased parts index into a new physical index built with the current mapping, then atomically swap the alias onto it.`,schema:b,defaultRetries:0}),S=[`pending`,`running`];async function C(e){let t=e.getTable();return e.count({where:p(t.status,[...S])})}function w(e){let t=/^(.*)_v(\d+)$/.exec(e);if(!t)return null;let[,n,r]=t;if(n===void 0||r===void 0)return null;let i=Number.parseInt(r,10)+1;return Number.isSafeInteger(i)?`${n}_v${i}`:null}function T(e){let{esClient:t,esIndex:n,indexConfig:r,importRuns:i,logger:a}=e;return async e=>{let o=a?.child({jobId:e.id,type:`imports:reindex`});if(r===void 0)throw Error(`imports:reindex requires imports({ indexConfig }) — the destination index must be created with the settings + mappings this app intends, otherwise Elasticsearch auto-creates it with inferred text mappings and the reindex silently discards the mapping change it exists to ship. Pass your app's index config (for the parts catalog: partsIndexConfig from @murumets-ee/search-elasticsearch).`);let s=b.parse(e.payload);if(i!==void 0){let e=await C(i);if(e>0)throw Error(`imports:reindex: ${e} feed import(s) are pending or running. An import writes through the alias "${n}", which points at the SOURCE index for the whole copy, so anything it writes now would be lost at the swap. Wait for them to finish (or cancel them) and re-run.`)}let c=await t(),l=Object.keys(await g(c,n)).sort();if(l.length>1)throw Error(`imports:reindex: alias "${n}" points at ${l.length} indices (${l.join(`, `)}). Reindexing would copy only one of them and then swap, leaving the alias on a mix of stale and fresh data. Resolve the alias to a single index first, then re-run. An interrupted reindex cannot cause this — swapAlias sends its remove with must_exist, so a stale swap fails instead of adding a second target. Look for a manual alias edit, another tool writing to this cluster, or a swap issued before that guard existed.`);let u=l[0];if(u===void 0)throw Error(`imports:reindex: alias "${n}" does not exist, so there is nothing to reindex. Run an import first — imports({ ensureIndex }) bootstraps the alias on the import path.`);let d=s.toIndex??w(u);if(d===null)throw Error(`imports:reindex: alias "${n}" points at "${u}", which does not follow the "<name>_v<N>" convention, so the next version cannot be derived. Pass an explicit destination (\`lumi imports:reindex --to <index>\`).`);let f=d;if(f===u)throw Error(`imports:reindex: destination "${f}" is the index the alias already points at. Reindexing an index onto itself would copy every document over itself and swap the alias onto where it already is. Pass a different --to.`);let p=await c.indices.exists({index:f});if(p&&s.allowExisting!==!0)throw Error(`imports:reindex: destination "${f}" already exists. If it is debris from a failed run, re-run with --resume to continue into it (bulkUpsert is idempotent by _id, so already-copied documents are simply overwritten); if it was created by hand with a different mapping, delete it or pass --to <another-index> instead. Refusing to reuse it silently, because doing so would apply ITS mapping rather than the one you changed.`);e.updateProgress({phase:p?`resuming`:`creating`,fromIndex:u,toIndex:f,processed:0,total:0}),p?o?.warn({fromIndex:u,toIndex:f},`imports:reindex: resuming into an existing destination index (--resume); its EXISTING mapping applies, not indexConfig`):(o?.info({fromIndex:u,toIndex:f},`imports:reindex: creating destination index`),await m(c,{name:f,config:r})),o?.info({fromIndex:u,toIndex:f},`imports:reindex: copying documents`);let v=await _({client:c,alias:n,fromIndex:u,toIndex:f,refreshOnCompletion:!0,...s.batchSize!==void 0&&{batchSize:s.batchSize},...i!==void 0&&{beforeSwap:async()=>{let e=await C(i);if(e>0)throw Error(`imports:reindex: ${e} feed import(s) started while the copy was running. Their documents went to the source index through the alias and are NOT in "${f}", so swapping now would drop them. The alias is untouched and "${f}" is left in place — wait for the import(s), then re-run with --resume to finish.`)}},reportProgress:async({processed:t,total:n})=>{e.updateProgress({phase:`copying`,fromIndex:u,toIndex:f,processed:t,total:n})}});e.updateProgress({phase:`swapped`,fromIndex:u,toIndex:f,processed:v.processed,total:v.processed+v.skipped,skipped:v.skipped}),o?.info({fromIndex:u,toIndex:f,processed:v.processed,skipped:v.skipped},`imports:reindex: alias swapped onto destination`),s.dropOld===!0&&(await h(c,u),o?.info({fromIndex:u},`imports:reindex: dropped source index (--drop-old)`))}}const E=v,D=[{id:`price-list-imports`,area:`selling`,status:`ready`,titleKey:`Imports.showcase.price-list-imports.title`,title:`Price list imports`,summaryKey:`Imports.showcase.price-list-imports.summary`,summary:`Load supplier prices and stock from CSV or Excel.`,bodyKey:`Imports.showcase.price-list-imports.body`,body:`Bring in your suppliers' price lists and stock levels from CSV or Excel files. Match the file's columns to your fields once per supplier, and every later file from that supplier goes in the same way. You see a preview first, and any rows that could not be read are listed.`,tryIt:`/admin/commerce/imports`,packages:[`imports`,`commerce`]}],O=Symbol.for(`@murumets-ee/imports:dead-listener-unsubscribe`);function k(f){let p=f.esIndex,m=f.storageBucket===null?null:{bucket:f.storageBucket??`lumi-imports`,orphanTtlHours:f.orphanUploadTtlHours??2,processedRetentionDays:f.processedRetentionDays??7};return f.storage&&r(f.storage),d({name:`@murumets-ee/imports`,shared:{messages:E,showcase:D},server:{entities:[n,t],init:async t=>{let r=e();if(r.has(`commerce:mapping`)||r.register(s,o),r.has(`commerce:carmaker-feed`)||r.register(a,i),!t.plugins.all().some(e=>e.name===`@murumets-ee/queue`)){t.logger.warn(`imports: queue() plugin not in plugins array — imports:run jobs will not be processed`);return}let d=await import(`@murumets-ee/queue/client`),h=t.getClient(n);d.registerJob(u,l({importRuns:h,transforms:r,esClient:f.esClient,esIndex:p,...f.ensureIndex!==void 0&&{ensureIndex:f.ensureIndex},...f.resolveFilePath!==void 0&&{resolveFilePath:f.resolveFilePath},...f.pairSink!==void 0&&{pairSink:f.pairSink},logger:t.logger.child({pkg:`imports`})})),d.registerJob(x,T({esClient:f.esClient,esIndex:p,importRuns:h,...f.indexConfig!==void 0&&{indexConfig:f.indexConfig},logger:t.logger.child({pkg:`imports`})}));let g=globalThis[O];g&&g();let _=d.addQueueTerminalListener(c({importRuns:h,logger:t.logger.child({pkg:`imports`})}));if(globalThis[O]=_,m!==null){let{cleanupOrphanedImports:e,cleanupProcessedImports:n}=await import(`./cleanup-DDljBe0Z.mjs`).then(e=>e.r),{runAsCli:r}=await import(`@murumets-ee/core`),i=t.logger.child({pkg:`imports`,job:`cleanup`});d.registerJob(d.defineJob({name:`imports:cleanup-orphaned-uploads`,description:`Delete toolkit_files rows in the imports bucket with no matching import_run reference and createdAt older than the configured TTL.`,schedule:`@every 1h`}),async()=>{await r(()=>e({app:t,bucket:m.bucket,ttlHours:m.orphanTtlHours,logger:i}))}),d.registerJob(d.defineJob({name:`imports:cleanup-processed-uploads`,description:`Delete toolkit_files rows for import_runs in terminal status (succeeded/failed/cancelled) whose finishedAt is older than the configured retention window. The import_run row itself is preserved.`,schedule:`@every 1h`}),async()=>{await r(()=>n({app:t,bucket:m.bucket,retentionDays:m.processedRetentionDays,logger:i}))})}t.logger.info({esIndex:p,cleanup:m===null?{enabled:!1}:{enabled:!0,...m}},`Imports plugin initialized`)}}})}export{D as IMPORTS_SHOWCASE,k as imports};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={Imports:{showcase:{"price-list-imports":{title:`Импорт прайс-листов`,summary:`Цены и остатки поставщиков из файлов CSV или Excel.`,body:`Загружайте прайс-листы и остатки поставщиков из файлов CSV или Excel. Сопоставьте столбцы файла с вашими полями один раз для каждого поставщика — и все следующие его файлы загрузятся так же. Сначала вы видите предпросмотр, а строки, которые не удалось прочитать, показываются отдельно.`}}}};export{e as default};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@murumets-ee/imports",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.116.0",
|
|
4
4
|
"license": "Elastic-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -43,6 +43,10 @@
|
|
|
43
43
|
"./realtime": {
|
|
44
44
|
"types": "./dist/realtime/index.d.mts",
|
|
45
45
|
"import": "./dist/realtime/index.mjs"
|
|
46
|
+
},
|
|
47
|
+
"./i18n": {
|
|
48
|
+
"types": "./dist/i18n.d.mts",
|
|
49
|
+
"import": "./dist/i18n.mjs"
|
|
46
50
|
}
|
|
47
51
|
},
|
|
48
52
|
"files": [
|
|
@@ -53,13 +57,13 @@
|
|
|
53
57
|
"drizzle-orm": "^0.45.2",
|
|
54
58
|
"exceljs": "^4.4.0",
|
|
55
59
|
"zod": "^3.24.1",
|
|
56
|
-
"@murumets-ee/core": "0.
|
|
57
|
-
"@murumets-ee/db": "0.
|
|
58
|
-
"@murumets-ee/entity": "0.
|
|
59
|
-
"@murumets-ee/logging": "0.
|
|
60
|
-
"@murumets-ee/queue": "0.
|
|
61
|
-
"@murumets-ee/search-elasticsearch": "0.
|
|
62
|
-
"@murumets-ee/storage": "0.
|
|
60
|
+
"@murumets-ee/core": "0.116.0",
|
|
61
|
+
"@murumets-ee/db": "0.116.0",
|
|
62
|
+
"@murumets-ee/entity": "0.116.0",
|
|
63
|
+
"@murumets-ee/logging": "0.116.0",
|
|
64
|
+
"@murumets-ee/queue": "0.116.0",
|
|
65
|
+
"@murumets-ee/search-elasticsearch": "0.116.0",
|
|
66
|
+
"@murumets-ee/storage": "0.116.0"
|
|
63
67
|
},
|
|
64
68
|
"devDependencies": {
|
|
65
69
|
"@types/node": "^20.19.42",
|