@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.
Files changed (47) hide show
  1. package/CLAUDE.md +23 -23
  2. package/LICENSE +21 -21
  3. package/README.md +85 -85
  4. package/build/index.d.ts +3 -0
  5. package/build/index.js +1 -1
  6. package/dev/.stfolder/syncthing-folder-9a95b7.txt +5 -0
  7. package/dev/98894488.xlsx +0 -0
  8. package/dev/HappyThaiSembawang.csv +336 -0
  9. package/dev/KB/create-new-model.md +34 -0
  10. package/dev/KB/markdown-lint.md +14 -0
  11. package/dev/KB/readmefirst.md +6 -0
  12. package/dev/Merchants/HappyThaiSembawang.csv +400 -0
  13. package/dev/Merchants/HappyThaiSembawang.xlsx +0 -0
  14. package/dev/Product_Import_Template.xlsx +0 -0
  15. package/dev/XPOS Invoice Module.pdf +232 -0
  16. package/dev/escpos/receipt.bin +0 -0
  17. package/dev/escpos/receipt.hex +1 -0
  18. package/dev/escpos/receipt.json +1 -0
  19. package/dev/escpos-cli-usage.md +103 -0
  20. package/dev/harbor-harness-deployment.md +78 -0
  21. package/dev/nginx-harbor-harness.conf +84 -0
  22. package/dev/px-cli.md +99 -0
  23. package/dev/test-logs/2026-02.md +5 -0
  24. package/dev/tmp/xpos_product_import(1).csv +338 -0
  25. package/dev/tmp/xpos_product_import_fixed.csv +338 -0
  26. package/dev//344/272/247/345/223/201/345/257/274/345/205/245/346/250/241/346/235/277.xlsx +0 -0
  27. package/jest.config.cjs +36 -36
  28. package/jest.setup.cjs +80 -80
  29. package/package.json +1 -1
  30. package/package.publish.json +121 -121
  31. package/px/.env.example +3 -0
  32. package/px/commands/category.ts +47 -0
  33. package/px/commands/product.ts +51 -0
  34. package/px/http.ts +40 -0
  35. package/px/index.ts +35 -0
  36. package/tsdown.config.ts +21 -21
  37. package/vite.config.ts +86 -86
  38. package/memo/technical-docs/01_ARCHITECTURE.md +0 -147
  39. package/memo/technical-docs/02_CORE_BUSINESS.md +0 -292
  40. package/memo/technical-docs/03_UI_COMPONENTS.md +0 -59
  41. package/memo/technical-docs/04_VIEWS.md +0 -82
  42. package/memo/technical-docs/05_DATA_LAYER.md +0 -375
  43. package/memo/technical-docs/06_CROSS_PLATFORM.md +0 -246
  44. package/memo/technical-docs/07_SIMILARITY_INDEX.md +0 -195
  45. package/memo/technical-docs/CHECKPOINT.md +0 -46
  46. package/memo/technical-docs/PROJECT_OVERVIEW.md +0 -122
  47. 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posx/core",
3
- "version": "5.5.395",
3
+ "version": "5.5.396",
4
4
  "description": "POSX core libraries",
5
5
  "main": "./build/index.js",
6
6
  "author": "Steven Lee",
@@ -1,121 +1,121 @@
1
- {
2
- "name": "@posx/core",
3
- "version": "5.5.395",
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
+ }
@@ -0,0 +1,3 @@
1
+ PX_BASE_URL=https://lite-dev.posx.ai
2
+ PX_API_KEY=your_api_key
3
+ PX_MERCHANT_UID=your_merchant_uid
@@ -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
+ })