@posx/core 5.5.395 → 5.5.396
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/CLAUDE.md +23 -23
- package/LICENSE +21 -21
- package/README.md +85 -85
- package/build/index.d.ts +3 -0
- package/build/index.js +1 -1
- package/dev/.stfolder/syncthing-folder-9a95b7.txt +5 -0
- package/dev/98894488.xlsx +0 -0
- package/dev/HappyThaiSembawang.csv +336 -0
- package/dev/KB/create-new-model.md +34 -0
- package/dev/KB/markdown-lint.md +14 -0
- package/dev/KB/readmefirst.md +6 -0
- package/dev/Merchants/HappyThaiSembawang.csv +400 -0
- package/dev/Merchants/HappyThaiSembawang.xlsx +0 -0
- package/dev/Product_Import_Template.xlsx +0 -0
- package/dev/XPOS Invoice Module.pdf +232 -0
- package/dev/escpos/receipt.bin +0 -0
- package/dev/escpos/receipt.hex +1 -0
- package/dev/escpos/receipt.json +1 -0
- package/dev/escpos-cli-usage.md +103 -0
- package/dev/harbor-harness-deployment.md +78 -0
- package/dev/nginx-harbor-harness.conf +84 -0
- package/dev/px-cli.md +99 -0
- package/dev/test-logs/2026-02.md +5 -0
- package/dev/tmp/xpos_product_import(1).csv +338 -0
- package/dev/tmp/xpos_product_import_fixed.csv +338 -0
- package/dev//344/272/247/345/223/201/345/257/274/345/205/245/346/250/241/346/235/277.xlsx +0 -0
- package/jest.config.cjs +36 -36
- package/jest.setup.cjs +80 -80
- package/package.json +1 -1
- package/package.publish.json +121 -121
- package/px/.env.example +3 -0
- package/px/commands/category.ts +47 -0
- package/px/commands/product.ts +51 -0
- package/px/http.ts +40 -0
- package/px/index.ts +35 -0
- package/tsdown.config.ts +21 -21
- package/vite.config.ts +86 -86
- package/memo/technical-docs/01_ARCHITECTURE.md +0 -147
- package/memo/technical-docs/02_CORE_BUSINESS.md +0 -292
- package/memo/technical-docs/03_UI_COMPONENTS.md +0 -59
- package/memo/technical-docs/04_VIEWS.md +0 -82
- package/memo/technical-docs/05_DATA_LAYER.md +0 -375
- package/memo/technical-docs/06_CROSS_PLATFORM.md +0 -246
- package/memo/technical-docs/07_SIMILARITY_INDEX.md +0 -195
- package/memo/technical-docs/CHECKPOINT.md +0 -46
- package/memo/technical-docs/PROJECT_OVERVIEW.md +0 -122
- package/memo/technical-docs/TECHNICAL_DOCS_PLAN.md +0 -77
package/jest.setup.cjs
CHANGED
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
// Jest setup file to polyfill File and Blob APIs
|
|
2
|
-
// This ensures consistent behavior across different Node.js versions and test environments
|
|
3
|
-
|
|
4
|
-
// Polyfill Blob.text() if not available
|
|
5
|
-
if (typeof Blob !== 'undefined' && !Blob.prototype.text) {
|
|
6
|
-
Blob.prototype.text = async function() {
|
|
7
|
-
const reader = new FileReader();
|
|
8
|
-
return new Promise((resolve, reject) => {
|
|
9
|
-
reader.onload = () => resolve(reader.result);
|
|
10
|
-
reader.onerror = reject;
|
|
11
|
-
reader.readAsText(this);
|
|
12
|
-
});
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// Polyfill FileReader if not available
|
|
17
|
-
if (typeof FileReader === 'undefined') {
|
|
18
|
-
global.FileReader = class FileReader {
|
|
19
|
-
result = null;
|
|
20
|
-
error = null;
|
|
21
|
-
onload = null;
|
|
22
|
-
onerror = null;
|
|
23
|
-
|
|
24
|
-
readAsText(blob) {
|
|
25
|
-
try {
|
|
26
|
-
// For our polyfilled Blob
|
|
27
|
-
if (blob.parts) {
|
|
28
|
-
this.result = blob.parts.join('');
|
|
29
|
-
} else if (typeof blob === 'string') {
|
|
30
|
-
this.result = blob;
|
|
31
|
-
} else {
|
|
32
|
-
this.result = String(blob);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (this.onload) {
|
|
36
|
-
setTimeout(() => this.onload({ target: this }), 0);
|
|
37
|
-
}
|
|
38
|
-
} catch (err) {
|
|
39
|
-
this.error = err;
|
|
40
|
-
if (this.onerror) {
|
|
41
|
-
setTimeout(() => this.onerror({ target: this }), 0);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Polyfill Blob if not available (for Node.js < 18)
|
|
49
|
-
if (typeof Blob === 'undefined') {
|
|
50
|
-
global.Blob = class Blob {
|
|
51
|
-
constructor(parts = [], options = {}) {
|
|
52
|
-
this.parts = parts;
|
|
53
|
-
this.type = options.type || '';
|
|
54
|
-
this.size = parts.reduce((acc, part) => {
|
|
55
|
-
return acc + (typeof part === 'string' ? part.length : part.length || 0);
|
|
56
|
-
}, 0);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async text() {
|
|
60
|
-
return this.parts.join('');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async arrayBuffer() {
|
|
64
|
-
const text = await this.text();
|
|
65
|
-
const buffer = Buffer.from(text);
|
|
66
|
-
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Polyfill File if not available (for Node.js < 18)
|
|
72
|
-
if (typeof File === 'undefined') {
|
|
73
|
-
global.File = class File extends global.Blob {
|
|
74
|
-
constructor(bits, name, options = {}) {
|
|
75
|
-
super(bits, options);
|
|
76
|
-
this.name = name;
|
|
77
|
-
this.lastModified = options.lastModified || Date.now();
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
}
|
|
1
|
+
// Jest setup file to polyfill File and Blob APIs
|
|
2
|
+
// This ensures consistent behavior across different Node.js versions and test environments
|
|
3
|
+
|
|
4
|
+
// Polyfill Blob.text() if not available
|
|
5
|
+
if (typeof Blob !== 'undefined' && !Blob.prototype.text) {
|
|
6
|
+
Blob.prototype.text = async function() {
|
|
7
|
+
const reader = new FileReader();
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
reader.onload = () => resolve(reader.result);
|
|
10
|
+
reader.onerror = reject;
|
|
11
|
+
reader.readAsText(this);
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Polyfill FileReader if not available
|
|
17
|
+
if (typeof FileReader === 'undefined') {
|
|
18
|
+
global.FileReader = class FileReader {
|
|
19
|
+
result = null;
|
|
20
|
+
error = null;
|
|
21
|
+
onload = null;
|
|
22
|
+
onerror = null;
|
|
23
|
+
|
|
24
|
+
readAsText(blob) {
|
|
25
|
+
try {
|
|
26
|
+
// For our polyfilled Blob
|
|
27
|
+
if (blob.parts) {
|
|
28
|
+
this.result = blob.parts.join('');
|
|
29
|
+
} else if (typeof blob === 'string') {
|
|
30
|
+
this.result = blob;
|
|
31
|
+
} else {
|
|
32
|
+
this.result = String(blob);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (this.onload) {
|
|
36
|
+
setTimeout(() => this.onload({ target: this }), 0);
|
|
37
|
+
}
|
|
38
|
+
} catch (err) {
|
|
39
|
+
this.error = err;
|
|
40
|
+
if (this.onerror) {
|
|
41
|
+
setTimeout(() => this.onerror({ target: this }), 0);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Polyfill Blob if not available (for Node.js < 18)
|
|
49
|
+
if (typeof Blob === 'undefined') {
|
|
50
|
+
global.Blob = class Blob {
|
|
51
|
+
constructor(parts = [], options = {}) {
|
|
52
|
+
this.parts = parts;
|
|
53
|
+
this.type = options.type || '';
|
|
54
|
+
this.size = parts.reduce((acc, part) => {
|
|
55
|
+
return acc + (typeof part === 'string' ? part.length : part.length || 0);
|
|
56
|
+
}, 0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async text() {
|
|
60
|
+
return this.parts.join('');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async arrayBuffer() {
|
|
64
|
+
const text = await this.text();
|
|
65
|
+
const buffer = Buffer.from(text);
|
|
66
|
+
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Polyfill File if not available (for Node.js < 18)
|
|
72
|
+
if (typeof File === 'undefined') {
|
|
73
|
+
global.File = class File extends global.Blob {
|
|
74
|
+
constructor(bits, name, options = {}) {
|
|
75
|
+
super(bits, options);
|
|
76
|
+
this.name = name;
|
|
77
|
+
this.lastModified = options.lastModified || Date.now();
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
package/package.json
CHANGED
package/package.publish.json
CHANGED
|
@@ -1,121 +1,121 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@posx/core",
|
|
3
|
-
"version": "5.5.
|
|
4
|
-
"description": "POSX core libraries",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./build/index.js",
|
|
7
|
-
"module": "./build/index.js",
|
|
8
|
-
"types": "./build/index.d.ts",
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"import": "./build/index.js",
|
|
12
|
-
"types": "./build/index.d.ts"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"files": [
|
|
16
|
-
"build"
|
|
17
|
-
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"up": "node ./scripts/upload-to-s3.cjs --trace-warnings",
|
|
20
|
-
"start": "tsdown --watch",
|
|
21
|
-
"build": "tsdown",
|
|
22
|
-
"build:dts": "tsdown --dts-only",
|
|
23
|
-
"post:build": "node ./scripts/clean-build.cjs",
|
|
24
|
-
"mvm": "mv build/*.map scripts/sourcemaps/ && mv build/**/*.map scripts/sourcemaps/ && find build -name '*.map' -type f -delete",
|
|
25
|
-
"build:demo": "vite build --mode demo",
|
|
26
|
-
"test": "jest --maxWorkers=2",
|
|
27
|
-
"test:watch": "jest --watch",
|
|
28
|
-
"publish": "node ./scripts/publish.cjs",
|
|
29
|
-
"p": "pnpm i && npm test && npm run up:p && npm run build && npm run post:build && npm run publish && rm -rf build",
|
|
30
|
-
"debug": "npm run build && npm run post:build",
|
|
31
|
-
"coverage": "jest --coverage",
|
|
32
|
-
"trypublish": "npm publish || true",
|
|
33
|
-
"gen:index": "node ./scriptswc/generateIndex.cjs",
|
|
34
|
-
"up:p": "npm version patch && git push",
|
|
35
|
-
"up:m": "npm version minor && git push",
|
|
36
|
-
"docs": "typedoc --out docs src --excludePrivate --exclude '**/demo/**/*' --exclude '**/*+(interface|function|enum).ts'",
|
|
37
|
-
"see": "node ./scripts/lookup-sm.cjs",
|
|
38
|
-
"escpos": "node ./scripts/generate-escpos-bytes.mjs"
|
|
39
|
-
},
|
|
40
|
-
"repository": {
|
|
41
|
-
"type": "git"
|
|
42
|
-
},
|
|
43
|
-
"author": "Steven Lee",
|
|
44
|
-
"license": "UNLICENSED",
|
|
45
|
-
"bugs": {
|
|
46
|
-
"url": ""
|
|
47
|
-
},
|
|
48
|
-
"homepage": "",
|
|
49
|
-
"keywords": [
|
|
50
|
-
"library",
|
|
51
|
-
"starter",
|
|
52
|
-
"es6"
|
|
53
|
-
],
|
|
54
|
-
"devDependencies": {
|
|
55
|
-
"@capacitor/core": "5.4.2",
|
|
56
|
-
"@litepos/autoquery": "5.0.6",
|
|
57
|
-
"@types/bcryptjs": "2.4.6",
|
|
58
|
-
"@types/jest": "30.0.0",
|
|
59
|
-
"@types/lodash": "^4.17.12",
|
|
60
|
-
"@types/node": "20.8.2",
|
|
61
|
-
"@types/uuid": "9.0.4",
|
|
62
|
-
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
|
63
|
-
"@typescript-eslint/parser": "^4.33.0",
|
|
64
|
-
"@vitest/coverage-v8": "^2.1.0",
|
|
65
|
-
"ajv": "8.17.1",
|
|
66
|
-
"aws-sdk": "2.1550.0",
|
|
67
|
-
"colors": "1.4.0",
|
|
68
|
-
"eslint": "^7.32.0",
|
|
69
|
-
"fs-extra": "11.1.1",
|
|
70
|
-
"glob": "10.3.10",
|
|
71
|
-
"jest": "30.2.0",
|
|
72
|
-
"jest-environment-jsdom": "30.2.0",
|
|
73
|
-
"jsdom": "27.1.0",
|
|
74
|
-
"prompt-sync": "4.2.0",
|
|
75
|
-
"rollup-plugin-obfuscator": "^1.1.0",
|
|
76
|
-
"sqlite3": "5.1.6",
|
|
77
|
-
"terser": "^5.36.0",
|
|
78
|
-
"ts-jest": "29.4.5",
|
|
79
|
-
"tsdown": "0.16.1",
|
|
80
|
-
"typedoc": "^0.26.0",
|
|
81
|
-
"typescript": "^5.6.3",
|
|
82
|
-
"vite": "^5.4.11",
|
|
83
|
-
"vite-plugin-bundle-obfuscator": "1.8.0",
|
|
84
|
-
"vite-plugin-dts": "^4.3.0",
|
|
85
|
-
"vitest": "^2.1.0",
|
|
86
|
-
"xlsx": "^0.18.5"
|
|
87
|
-
},
|
|
88
|
-
"dependencies": {
|
|
89
|
-
"@awesome-cordova-plugins/core": "6.6.0",
|
|
90
|
-
"@awesome-cordova-plugins/network-interface": "6.6.0",
|
|
91
|
-
"@microsoft/signalr": "7.0.11",
|
|
92
|
-
"axios": "1.5.1",
|
|
93
|
-
"bcryptjs": "2.4.3",
|
|
94
|
-
"buffer": "6.0.3",
|
|
95
|
-
"crypto-browserify": "3.12.0",
|
|
96
|
-
"crypto-js": "4.2.0",
|
|
97
|
-
"dayjs": "^1.11.10",
|
|
98
|
-
"dexie": "3.2.4",
|
|
99
|
-
"esc-pos-encoder-ionic": "1.1.3",
|
|
100
|
-
"handlebars": "4.7.8",
|
|
101
|
-
"lodash": "^4.17.21",
|
|
102
|
-
"nanoid": "3.3.4",
|
|
103
|
-
"save": "2.9.0",
|
|
104
|
-
"selecttransform": "1.6.1",
|
|
105
|
-
"sockets-for-cordova": "1.0.0",
|
|
106
|
-
"stream-browserify": "3.0.0",
|
|
107
|
-
"uuid": "9.0.1",
|
|
108
|
-
"vm-browserify": "1.1.2",
|
|
109
|
-
"wcwidth": "1.0.1",
|
|
110
|
-
"zod": "3.23.8"
|
|
111
|
-
},
|
|
112
|
-
"peerDependencies": {
|
|
113
|
-
"@litepos/autoquery": "^5.0.6",
|
|
114
|
-
"rxjs": "^7.0.0"
|
|
115
|
-
},
|
|
116
|
-
"peerDependenciesMeta": {
|
|
117
|
-
"rxjs": {
|
|
118
|
-
"optional": true
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@posx/core",
|
|
3
|
+
"version": "5.5.396",
|
|
4
|
+
"description": "POSX core libraries",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./build/index.js",
|
|
7
|
+
"module": "./build/index.js",
|
|
8
|
+
"types": "./build/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./build/index.js",
|
|
12
|
+
"types": "./build/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"build"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"up": "node ./scripts/upload-to-s3.cjs --trace-warnings",
|
|
20
|
+
"start": "tsdown --watch",
|
|
21
|
+
"build": "tsdown",
|
|
22
|
+
"build:dts": "tsdown --dts-only",
|
|
23
|
+
"post:build": "node ./scripts/clean-build.cjs",
|
|
24
|
+
"mvm": "mv build/*.map scripts/sourcemaps/ && mv build/**/*.map scripts/sourcemaps/ && find build -name '*.map' -type f -delete",
|
|
25
|
+
"build:demo": "vite build --mode demo",
|
|
26
|
+
"test": "jest --maxWorkers=2",
|
|
27
|
+
"test:watch": "jest --watch",
|
|
28
|
+
"publish": "node ./scripts/publish.cjs",
|
|
29
|
+
"p": "pnpm i && npm test && npm run up:p && npm run build && npm run post:build && npm run publish && rm -rf build",
|
|
30
|
+
"debug": "npm run build && npm run post:build",
|
|
31
|
+
"coverage": "jest --coverage",
|
|
32
|
+
"trypublish": "npm publish || true",
|
|
33
|
+
"gen:index": "node ./scriptswc/generateIndex.cjs",
|
|
34
|
+
"up:p": "npm version patch && git push",
|
|
35
|
+
"up:m": "npm version minor && git push",
|
|
36
|
+
"docs": "typedoc --out docs src --excludePrivate --exclude '**/demo/**/*' --exclude '**/*+(interface|function|enum).ts'",
|
|
37
|
+
"see": "node ./scripts/lookup-sm.cjs",
|
|
38
|
+
"escpos": "node ./scripts/generate-escpos-bytes.mjs"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git"
|
|
42
|
+
},
|
|
43
|
+
"author": "Steven Lee",
|
|
44
|
+
"license": "UNLICENSED",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": ""
|
|
47
|
+
},
|
|
48
|
+
"homepage": "",
|
|
49
|
+
"keywords": [
|
|
50
|
+
"library",
|
|
51
|
+
"starter",
|
|
52
|
+
"es6"
|
|
53
|
+
],
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@capacitor/core": "5.4.2",
|
|
56
|
+
"@litepos/autoquery": "5.0.6",
|
|
57
|
+
"@types/bcryptjs": "2.4.6",
|
|
58
|
+
"@types/jest": "30.0.0",
|
|
59
|
+
"@types/lodash": "^4.17.12",
|
|
60
|
+
"@types/node": "20.8.2",
|
|
61
|
+
"@types/uuid": "9.0.4",
|
|
62
|
+
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
|
63
|
+
"@typescript-eslint/parser": "^4.33.0",
|
|
64
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
65
|
+
"ajv": "8.17.1",
|
|
66
|
+
"aws-sdk": "2.1550.0",
|
|
67
|
+
"colors": "1.4.0",
|
|
68
|
+
"eslint": "^7.32.0",
|
|
69
|
+
"fs-extra": "11.1.1",
|
|
70
|
+
"glob": "10.3.10",
|
|
71
|
+
"jest": "30.2.0",
|
|
72
|
+
"jest-environment-jsdom": "30.2.0",
|
|
73
|
+
"jsdom": "27.1.0",
|
|
74
|
+
"prompt-sync": "4.2.0",
|
|
75
|
+
"rollup-plugin-obfuscator": "^1.1.0",
|
|
76
|
+
"sqlite3": "5.1.6",
|
|
77
|
+
"terser": "^5.36.0",
|
|
78
|
+
"ts-jest": "29.4.5",
|
|
79
|
+
"tsdown": "0.16.1",
|
|
80
|
+
"typedoc": "^0.26.0",
|
|
81
|
+
"typescript": "^5.6.3",
|
|
82
|
+
"vite": "^5.4.11",
|
|
83
|
+
"vite-plugin-bundle-obfuscator": "1.8.0",
|
|
84
|
+
"vite-plugin-dts": "^4.3.0",
|
|
85
|
+
"vitest": "^2.1.0",
|
|
86
|
+
"xlsx": "^0.18.5"
|
|
87
|
+
},
|
|
88
|
+
"dependencies": {
|
|
89
|
+
"@awesome-cordova-plugins/core": "6.6.0",
|
|
90
|
+
"@awesome-cordova-plugins/network-interface": "6.6.0",
|
|
91
|
+
"@microsoft/signalr": "7.0.11",
|
|
92
|
+
"axios": "1.5.1",
|
|
93
|
+
"bcryptjs": "2.4.3",
|
|
94
|
+
"buffer": "6.0.3",
|
|
95
|
+
"crypto-browserify": "3.12.0",
|
|
96
|
+
"crypto-js": "4.2.0",
|
|
97
|
+
"dayjs": "^1.11.10",
|
|
98
|
+
"dexie": "3.2.4",
|
|
99
|
+
"esc-pos-encoder-ionic": "1.1.3",
|
|
100
|
+
"handlebars": "4.7.8",
|
|
101
|
+
"lodash": "^4.17.21",
|
|
102
|
+
"nanoid": "3.3.4",
|
|
103
|
+
"save": "2.9.0",
|
|
104
|
+
"selecttransform": "1.6.1",
|
|
105
|
+
"sockets-for-cordova": "1.0.0",
|
|
106
|
+
"stream-browserify": "3.0.0",
|
|
107
|
+
"uuid": "9.0.1",
|
|
108
|
+
"vm-browserify": "1.1.2",
|
|
109
|
+
"wcwidth": "1.0.1",
|
|
110
|
+
"zod": "3.23.8"
|
|
111
|
+
},
|
|
112
|
+
"peerDependencies": {
|
|
113
|
+
"@litepos/autoquery": "^5.0.6",
|
|
114
|
+
"rxjs": "^7.0.0"
|
|
115
|
+
},
|
|
116
|
+
"peerDependenciesMeta": {
|
|
117
|
+
"rxjs": {
|
|
118
|
+
"optional": true
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
package/px/.env.example
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { http } from '../http'
|
|
2
|
+
import type { ICategory } from '../../src/types/product.type'
|
|
3
|
+
import { QueryBuilder } from '@litepos/autoquery'
|
|
4
|
+
|
|
5
|
+
const MODULE = 'categories'
|
|
6
|
+
const METHOD = 'category'
|
|
7
|
+
|
|
8
|
+
export async function list(opts: Record<string, string> = {}) {
|
|
9
|
+
const builder = new QueryBuilder<ICategory>().includeTotal(true)
|
|
10
|
+
for (const [key, value] of Object.entries(opts))
|
|
11
|
+
builder.addRawParam(key, value)
|
|
12
|
+
const data = await http.autoquery(MODULE, builder.build())
|
|
13
|
+
console.log(JSON.stringify(data, null, 2))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function get(id: string) {
|
|
17
|
+
if (!id) { console.error('--id is required'); return }
|
|
18
|
+
const data = await http.get(`${MODULE}/${id}`) as ICategory
|
|
19
|
+
console.log(JSON.stringify(data, null, 2))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function add(opts: Record<string, string>) {
|
|
23
|
+
if (!opts.name) { console.error('--name is required'); return }
|
|
24
|
+
const body: Partial<ICategory> = { name: opts.name }
|
|
25
|
+
if (opts.type) body.type = opts.type as any
|
|
26
|
+
if (opts.color) body.color = opts.color
|
|
27
|
+
if (opts.icon) body.icon = opts.icon
|
|
28
|
+
const data = await http.put(`${MODULE}/${METHOD}`, body)
|
|
29
|
+
console.log(JSON.stringify(data, null, 2))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function update(id: string, opts: Record<string, string>) {
|
|
33
|
+
if (!id) { console.error('--id is required'); return }
|
|
34
|
+
const body: Partial<ICategory> = {}
|
|
35
|
+
if (opts.name) body.name = opts.name
|
|
36
|
+
if (opts.type) body.type = opts.type as any
|
|
37
|
+
if (opts.color) body.color = opts.color
|
|
38
|
+
if (opts.icon) body.icon = opts.icon
|
|
39
|
+
const data = await http.patch(`${MODULE}/${id}`, body)
|
|
40
|
+
console.log(JSON.stringify(data, null, 2))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function del(id: string) {
|
|
44
|
+
if (!id) { console.error('--id is required'); return }
|
|
45
|
+
const data = await http.del(`${MODULE}/${id}`)
|
|
46
|
+
console.log(JSON.stringify(data, null, 2))
|
|
47
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { http } from '../http'
|
|
2
|
+
import type { IItem } from '../../src/types/product.type'
|
|
3
|
+
import { QueryBuilder } from '@litepos/autoquery'
|
|
4
|
+
|
|
5
|
+
const MODULE = 'products'
|
|
6
|
+
const METHOD = 'product'
|
|
7
|
+
|
|
8
|
+
export async function list(opts: Record<string, string> = {}) {
|
|
9
|
+
const builder = new QueryBuilder<IItem>().includeTotal(true)
|
|
10
|
+
for (const [key, value] of Object.entries(opts))
|
|
11
|
+
builder.addRawParam(key, value)
|
|
12
|
+
const data = await http.autoquery(MODULE, builder.build())
|
|
13
|
+
console.log(JSON.stringify(data, null, 2))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function get(id: string) {
|
|
17
|
+
if (!id) { console.error('--id is required'); return }
|
|
18
|
+
const data = await http.get(`${MODULE}/${id}`) as IItem
|
|
19
|
+
console.log(JSON.stringify(data, null, 2))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function add(opts: Record<string, string>) {
|
|
23
|
+
if (!opts.name) { console.error('--name is required'); return }
|
|
24
|
+
const body: Partial<IItem> = { name: opts.name }
|
|
25
|
+
if (opts.price) body.price = parseFloat(opts.price)
|
|
26
|
+
if (opts.category_uid) body.category_uid = opts.category_uid
|
|
27
|
+
if (opts.sku) body.sku = opts.sku
|
|
28
|
+
if (opts.type) body.type = opts.type as any
|
|
29
|
+
if (opts.barcode) body.barcode = opts.barcode
|
|
30
|
+
const data = await http.put(`${MODULE}/${METHOD}`, body)
|
|
31
|
+
console.log(JSON.stringify(data, null, 2))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function update(id: string, opts: Record<string, string>) {
|
|
35
|
+
if (!id) { console.error('--id is required'); return }
|
|
36
|
+
const body: Partial<IItem> = {}
|
|
37
|
+
if (opts.name) body.name = opts.name
|
|
38
|
+
if (opts.price) body.price = parseFloat(opts.price)
|
|
39
|
+
if (opts.category_uid) body.category_uid = opts.category_uid
|
|
40
|
+
if (opts.sku) body.sku = opts.sku
|
|
41
|
+
if (opts.type) body.type = opts.type as any
|
|
42
|
+
if (opts.barcode) body.barcode = opts.barcode
|
|
43
|
+
const data = await http.patch(`${MODULE}/${id}`, body)
|
|
44
|
+
console.log(JSON.stringify(data, null, 2))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function del(id: string) {
|
|
48
|
+
if (!id) { console.error('--id is required'); return }
|
|
49
|
+
const data = await http.del(`${MODULE}/${id}`)
|
|
50
|
+
console.log(JSON.stringify(data, null, 2))
|
|
51
|
+
}
|
package/px/http.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { IServiceOptions } from '../src/types/misc.type'
|
|
2
|
+
|
|
3
|
+
const BASE_URL = process.env.PX_BASE_URL || 'https://lite-dev.posx.ai'
|
|
4
|
+
const API_KEY = process.env.PX_API_KEY || ''
|
|
5
|
+
const MERCHANT_UID = process.env.PX_MERCHANT_UID || ''
|
|
6
|
+
|
|
7
|
+
if (!API_KEY) { console.error('PX_API_KEY is required'); process.exit(1) }
|
|
8
|
+
if (!MERCHANT_UID) { console.error('PX_MERCHANT_UID is required'); process.exit(1) }
|
|
9
|
+
|
|
10
|
+
export const options: Pick<IServiceOptions, 'base_url' | 'merchant_uid'> = {
|
|
11
|
+
base_url: BASE_URL,
|
|
12
|
+
merchant_uid: MERCHANT_UID,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const headers = { 'x-api-key': API_KEY, 'Content-Type': 'application/json' }
|
|
16
|
+
const base = `${BASE_URL}/api/v5/merchants/${MERCHANT_UID}`
|
|
17
|
+
const sapiBase = `${BASE_URL}/sapi/v5/merchants/${MERCHANT_UID}`
|
|
18
|
+
|
|
19
|
+
async function request(method: string, url: string, body?: any) {
|
|
20
|
+
const res = await fetch(url, {
|
|
21
|
+
method,
|
|
22
|
+
headers,
|
|
23
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
24
|
+
})
|
|
25
|
+
const data = await res.json()
|
|
26
|
+
if (!res.ok) throw new Error(`${res.status}: ${JSON.stringify(data)}`)
|
|
27
|
+
return data
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const http = {
|
|
31
|
+
get: (path: string) => request('GET', `${base}/${path}`),
|
|
32
|
+
put: (path: string, body: any) => request('PUT', `${base}/${path}`, body),
|
|
33
|
+
patch: (path: string, body: any) => request('PATCH', `${base}/${path}`, body),
|
|
34
|
+
del: (path: string) => request('DELETE', `${base}/${path}`),
|
|
35
|
+
// AutoQuery endpoint using sapi base
|
|
36
|
+
autoquery: (path: string, params: Record<string, string>) => {
|
|
37
|
+
const qs = new URLSearchParams(params).toString()
|
|
38
|
+
return request('GET', `${sapiBase}/${path}${qs ? `?${qs}` : ''}`)
|
|
39
|
+
},
|
|
40
|
+
}
|
package/px/index.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as category from './commands/category'
|
|
2
|
+
import * as product from './commands/product'
|
|
3
|
+
|
|
4
|
+
const args = process.argv.slice(2)
|
|
5
|
+
const entity = args[0]
|
|
6
|
+
const action = args[1]
|
|
7
|
+
|
|
8
|
+
// Parse --key value pairs into an object
|
|
9
|
+
function parseOpts(args: string[]): Record<string, string> {
|
|
10
|
+
const opts: Record<string, string> = {}
|
|
11
|
+
for (let i = 2; i < args.length; i += 2)
|
|
12
|
+
if (args[i]?.startsWith('--')) opts[args[i].slice(2)] = args[i + 1] || ''
|
|
13
|
+
return opts
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const opts = parseOpts(args)
|
|
17
|
+
const commands: Record<string, Record<string, any>> = { category, product }
|
|
18
|
+
const ACTIONS = ['list', 'get', 'add', 'update', 'delete']
|
|
19
|
+
|
|
20
|
+
if (!entity || !commands[entity]) {
|
|
21
|
+
console.log('Usage: bun px/index.ts <category|product> <list|get|add|update|delete> [--key value ...]')
|
|
22
|
+
process.exit(1)
|
|
23
|
+
}
|
|
24
|
+
if (!action || !ACTIONS.includes(action)) {
|
|
25
|
+
console.log(`Actions: ${ACTIONS.join(', ')}`)
|
|
26
|
+
process.exit(1)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const mod = commands[entity]
|
|
30
|
+
const fn = action === 'delete' ? 'del' : action
|
|
31
|
+
|
|
32
|
+
if (fn === 'get' || fn === 'del') mod[fn](opts.id)
|
|
33
|
+
else if (fn === 'update') mod[fn](opts.id, opts)
|
|
34
|
+
else if (fn === 'add') mod[fn](opts)
|
|
35
|
+
else mod[fn](opts)
|
package/tsdown.config.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { defineConfig } from 'tsdown'
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
entry: 'src/index.ts',
|
|
5
|
-
outDir: 'build',
|
|
6
|
-
format: 'esm',
|
|
7
|
-
platform: 'neutral',
|
|
8
|
-
target: 'es2020',
|
|
9
|
-
clean: true,
|
|
10
|
-
sourcemap: true,
|
|
11
|
-
minify: true,
|
|
12
|
-
dts: {
|
|
13
|
-
resolve: true,
|
|
14
|
-
},
|
|
15
|
-
external: [
|
|
16
|
-
'@litepos/autoquery',
|
|
17
|
-
'lodash',
|
|
18
|
-
'rxjs',
|
|
19
|
-
'axios',
|
|
20
|
-
],
|
|
21
|
-
})
|
|
1
|
+
import { defineConfig } from 'tsdown'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: 'src/index.ts',
|
|
5
|
+
outDir: 'build',
|
|
6
|
+
format: 'esm',
|
|
7
|
+
platform: 'neutral',
|
|
8
|
+
target: 'es2020',
|
|
9
|
+
clean: true,
|
|
10
|
+
sourcemap: true,
|
|
11
|
+
minify: true,
|
|
12
|
+
dts: {
|
|
13
|
+
resolve: true,
|
|
14
|
+
},
|
|
15
|
+
external: [
|
|
16
|
+
'@litepos/autoquery',
|
|
17
|
+
'lodash',
|
|
18
|
+
'rxjs',
|
|
19
|
+
'axios',
|
|
20
|
+
],
|
|
21
|
+
})
|