tally-simple 0.0.0-stage → 1.3.1
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/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/README.md +127 -3
- package/bin/tally-simple.js +175 -0
- package/docs/api.html +41 -0
- package/docs/api.md +20 -0
- package/docs/architecture.html +74 -0
- package/docs/architecture.md +65 -0
- package/docs/developer.html +57 -0
- package/docs/developer.md +41 -0
- package/docs/end-user.html +49 -0
- package/docs/end-user.md +34 -0
- package/docs/index.html +32 -0
- package/docs/repo-developer.html +91 -0
- package/docs/repo-developer.md +107 -0
- package/package.json +55 -6
- package/src/index.d.ts +54 -0
- package/src/index.js +8 -0
- package/src/v1/external-api/api.js +67 -0
- package/src/v1/external-api/api.json +5 -0
- package/src/v1/external-api/index.js +1 -0
- package/src/v1/index.js +1 -0
- package/src/v1/source.json +40 -0
- package/src/v1/traverse.js +103 -0
- package/src/v1/traverseObject/index.js +39 -0
- package/src/v2/external-api/api.js +67 -0
- package/src/v2/external-api/api.json +5 -0
- package/src/v2/external-api/index.js +1 -0
- package/src/v2/index.js +1 -0
- package/src/v2/source.json +40 -0
- package/src/v2/traverse.js +103 -0
- package/src/v2/traverseObject/index.js +39 -0
- package/src/v3/external-api/api.js +67 -0
- package/src/v3/external-api/api.json +6 -0
- package/src/v3/external-api/index.js +1 -0
- package/src/v3/index.js +1 -0
- package/src/v3/source.json +48 -0
- package/src/v3/traverse.js +103 -0
- package/src/v3/traverseObject/index.js +39 -0
package/docs/end-user.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[**View this document as HTML**](./end-user.html)
|
|
2
|
+
|
|
3
|
+
# End User Guide
|
|
4
|
+
|
|
5
|
+
This guide is for someone who wants to use the Tally package, not modify it.
|
|
6
|
+
|
|
7
|
+
## JavaScript
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import tally from "./src/index.js";
|
|
11
|
+
|
|
12
|
+
const units = await tally.masters.units.fetch("Mani9");
|
|
13
|
+
console.log(units);
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The company name is supplied by the caller:
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
tally.masters.units.fetch("My Company");
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The package builds the Tally request internally.
|
|
23
|
+
|
|
24
|
+
## What you receive
|
|
25
|
+
|
|
26
|
+
The Tally response is returned as XML text. This package does not force an XML-to-JSON format on the consumer.
|
|
27
|
+
|
|
28
|
+
You can parse or transform the response with the tool you prefer.
|
|
29
|
+
|
|
30
|
+
## Public API
|
|
31
|
+
|
|
32
|
+
See [API Reference](api.md) for the currently exposed operations.
|
|
33
|
+
|
|
34
|
+
If you are integrating this package into a larger application, continue with the [Application Developer Guide](developer.md).
|
package/docs/index.html
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Tally Public API — Documentation</title>
|
|
7
|
+
<style>
|
|
8
|
+
body{font-family:system-ui,-apple-system,Segoe UI,Arial,sans-serif;max-width:760px;margin:60px auto;padding:0 24px;line-height:1.6;color:#222}
|
|
9
|
+
h1{margin-bottom:6px}
|
|
10
|
+
p{color:#666}
|
|
11
|
+
ul{padding-left:20px}
|
|
12
|
+
li{margin:10px 0}
|
|
13
|
+
a{text-decoration:none}
|
|
14
|
+
a:hover{text-decoration:underline}
|
|
15
|
+
small{color:#888}
|
|
16
|
+
</style>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<h1>Tally Public API</h1>
|
|
20
|
+
<p>Small, JSON-driven Tally query API.</p>
|
|
21
|
+
|
|
22
|
+
<ul>
|
|
23
|
+
<li><a href="end-user.html">End User</a> — use the package</li>
|
|
24
|
+
<li><a href="developer.html">Application Developer</a> — integrate the package</li>
|
|
25
|
+
<li><a href="repo-developer.html">Repository Developer</a> — maintain the repository</li>
|
|
26
|
+
<li><a href="api.html">API Reference</a> — public API paths</li>
|
|
27
|
+
<li><a href="architecture.html">Architecture</a> — how it works</li>
|
|
28
|
+
</ul>
|
|
29
|
+
|
|
30
|
+
<p><small><a href="../README.html">README</a> · <a href="../README.md">README.md</a></small></p>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>Repo Developer</title>
|
|
7
|
+
<style>
|
|
8
|
+
body{font-family:system-ui,-apple-system,Segoe UI,Arial,sans-serif;max-width:900px;margin:40px auto;padding:0 24px;line-height:1.65;color:#202124}
|
|
9
|
+
nav{padding:12px 16px;margin-bottom:28px;background:#f5f5f5;border-radius:8px}
|
|
10
|
+
nav a{margin-right:16px;text-decoration:none}
|
|
11
|
+
h1{font-size:30px;margin-top:0}
|
|
12
|
+
h2{font-size:22px;margin-top:32px}
|
|
13
|
+
h3{font-size:18px;margin-top:24px}
|
|
14
|
+
pre{background:#f6f8fa;padding:16px;border-radius:8px;overflow:auto}
|
|
15
|
+
code{background:#f1f3f4;padding:2px 5px;border-radius:4px}
|
|
16
|
+
pre code{background:none;padding:0}
|
|
17
|
+
table{border-collapse:collapse;width:100%;margin:16px 0}
|
|
18
|
+
th,td{border:1px solid #ddd;padding:8px;text-align:left}
|
|
19
|
+
th{background:#f6f6f6}
|
|
20
|
+
a{color:#0969da}
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<nav>
|
|
25
|
+
<a href="../README.html">README</a>
|
|
26
|
+
<a href="end-user.html">End User</a>
|
|
27
|
+
<a href="developer.html">Application Developer</a>
|
|
28
|
+
<a href="repo-developer.html">Repository Developer</a>
|
|
29
|
+
<a href="api.html">API</a>
|
|
30
|
+
<a href="architecture.html">Architecture</a>
|
|
31
|
+
</nav>
|
|
32
|
+
<h1>Repository Developer Guide</h1>
|
|
33
|
+
<p>This guide is for developers who change the repository itself.</p>
|
|
34
|
+
<h2>Source of truth</h2>
|
|
35
|
+
<p>The active version contains the Tally definitions:</p>
|
|
36
|
+
<pre><code>src/
|
|
37
|
+
└── v1/
|
|
38
|
+
├── source.json
|
|
39
|
+
├── traverse.js
|
|
40
|
+
├── traverseObject/
|
|
41
|
+
├── external-api/
|
|
42
|
+
│ ├── api.json
|
|
43
|
+
│ ├── api.js
|
|
44
|
+
│ └── index.js
|
|
45
|
+
└── index.js</code></pre>
|
|
46
|
+
<p><code>source.json</code> contains the internal request definitions.</p>
|
|
47
|
+
<p><code>external-api/api.json</code> contains only the public paths.</p>
|
|
48
|
+
<p>That separation is intentional.</p>
|
|
49
|
+
<h2>Adding a public operation</h2>
|
|
50
|
+
<p>1. Add or change the internal TDL definition in <code>source.json</code>.</p>
|
|
51
|
+
<p>2. Add its public path to <code>external-api/api.json</code>.</p>
|
|
52
|
+
<p>3. Regenerate <code>src/index.d.ts</code>.</p>
|
|
53
|
+
<p>4. Test the public call.</p>
|
|
54
|
+
<p>5. Leave older source versions untouched.</p>
|
|
55
|
+
<h2>Company input</h2>
|
|
56
|
+
<p>Do not hardcode a real company name into the public definition.</p>
|
|
57
|
+
<p>The caller supplies the company:</p>
|
|
58
|
+
<pre><code>tally.masters.units.fetch("Mani9");</code></pre>
|
|
59
|
+
<p>The runtime passes that value into the request builder.</p>
|
|
60
|
+
<h2>Traversal</h2>
|
|
61
|
+
<p>Traversal should remain generic.</p>
|
|
62
|
+
<p>Its job is to locate the requested definition.</p>
|
|
63
|
+
<p>The action/request layer interprets the terminal definition and performs the Tally HTTP request.</p>
|
|
64
|
+
<p>Avoid putting endpoint-specific paths directly into the traversal engine.</p>
|
|
65
|
+
<h2>Versioning</h2>
|
|
66
|
+
<p>When a new implementation is required:</p>
|
|
67
|
+
<pre><code>src/v1
|
|
68
|
+
src/v2
|
|
69
|
+
src/v3</code></pre>
|
|
70
|
+
<p>Create the next version and make the root <code>src/index.js</code> point to the highest active version.</p>
|
|
71
|
+
<p>Do not rewrite old versions merely to keep them visually consistent.</p>
|
|
72
|
+
<h2>Declaration generation</h2>
|
|
73
|
+
<p>The generated declaration file represents the public API tree.</p>
|
|
74
|
+
<p>It should be generated from the public API definition rather than manually maintained.</p>
|
|
75
|
+
<pre><code>node generate-dts.js</code></pre>
|
|
76
|
+
<h2>Testing</h2>
|
|
77
|
+
<p>Test both:</p>
|
|
78
|
+
<p>1. the public JavaScript API;</p>
|
|
79
|
+
<p>2. the generated request sent to Tally.</p>
|
|
80
|
+
<p>Keep Tally-specific request definitions in JSON where possible so adding another operation is primarily a data change rather than a new JavaScript module.</p>
|
|
81
|
+
<h2>Design boundary</h2>
|
|
82
|
+
<p>The repository deliberately has three layers:</p>
|
|
83
|
+
<pre><code>public API
|
|
84
|
+
↓
|
|
85
|
+
traversal / execution
|
|
86
|
+
↓
|
|
87
|
+
source JSON</code></pre>
|
|
88
|
+
<p>The public consumer sees the first layer.</p>
|
|
89
|
+
<p>The repository developer maintains all three.</p>
|
|
90
|
+
</body>
|
|
91
|
+
</html>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
[**View this document as HTML**](./repo-developer.html)
|
|
2
|
+
|
|
3
|
+
# Repository Developer Guide
|
|
4
|
+
|
|
5
|
+
This guide is for developers who change the repository itself.
|
|
6
|
+
|
|
7
|
+
## Source of truth
|
|
8
|
+
|
|
9
|
+
The active version contains the Tally definitions:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
src/
|
|
13
|
+
└── v1/
|
|
14
|
+
├── source.json
|
|
15
|
+
├── traverse.js
|
|
16
|
+
├── traverseObject/
|
|
17
|
+
├── external-api/
|
|
18
|
+
│ ├── api.json
|
|
19
|
+
│ ├── api.js
|
|
20
|
+
│ └── index.js
|
|
21
|
+
└── index.js
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`source.json` contains the internal request definitions.
|
|
25
|
+
|
|
26
|
+
`external-api/api.json` contains only the public paths.
|
|
27
|
+
|
|
28
|
+
That separation is intentional.
|
|
29
|
+
|
|
30
|
+
## Adding a public operation
|
|
31
|
+
|
|
32
|
+
1. Add or change the internal TDL definition in `source.json`.
|
|
33
|
+
2. Add its public path to `external-api/api.json`.
|
|
34
|
+
3. Regenerate `src/index.d.ts`.
|
|
35
|
+
4. Test the public call.
|
|
36
|
+
5. Leave older source versions untouched.
|
|
37
|
+
|
|
38
|
+
## Company input
|
|
39
|
+
|
|
40
|
+
Do not hardcode a real company name into the public definition.
|
|
41
|
+
|
|
42
|
+
The caller supplies the company:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
tally.masters.units.fetch("Mani9");
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The runtime passes that value into the request builder.
|
|
49
|
+
|
|
50
|
+
## Traversal
|
|
51
|
+
|
|
52
|
+
Traversal should remain generic.
|
|
53
|
+
|
|
54
|
+
Its job is to locate the requested definition.
|
|
55
|
+
|
|
56
|
+
The action/request layer interprets the terminal definition and performs the Tally HTTP request.
|
|
57
|
+
|
|
58
|
+
Avoid putting endpoint-specific paths directly into the traversal engine.
|
|
59
|
+
|
|
60
|
+
## Versioning
|
|
61
|
+
|
|
62
|
+
When a new implementation is required:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
src/v1
|
|
66
|
+
src/v2
|
|
67
|
+
src/v3
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Create the next version and make the root `src/index.js` point to the highest active version.
|
|
71
|
+
|
|
72
|
+
Do not rewrite old versions merely to keep them visually consistent.
|
|
73
|
+
|
|
74
|
+
## Declaration generation
|
|
75
|
+
|
|
76
|
+
The generated declaration file represents the public API tree.
|
|
77
|
+
|
|
78
|
+
It should be generated from the public API definition rather than manually maintained.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
node generate-dts.js
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Testing
|
|
85
|
+
|
|
86
|
+
Test both:
|
|
87
|
+
|
|
88
|
+
1. the public JavaScript API;
|
|
89
|
+
2. the generated request sent to Tally.
|
|
90
|
+
|
|
91
|
+
Keep Tally-specific request definitions in JSON where possible so adding another operation is primarily a data change rather than a new JavaScript module.
|
|
92
|
+
|
|
93
|
+
## Design boundary
|
|
94
|
+
|
|
95
|
+
The repository deliberately has three layers:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
public API
|
|
99
|
+
↓
|
|
100
|
+
traversal / execution
|
|
101
|
+
↓
|
|
102
|
+
source JSON
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The public consumer sees the first layer.
|
|
106
|
+
|
|
107
|
+
The repository developer maintains all three.
|
package/package.json
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "tally-simple",
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"description": "A small, typed JavaScript client and CLI for querying Tally through TDL definitions.",
|
|
5
|
+
"homepage": "https://github.com/keshavsoft/tally-simple#readme",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/keshavsoft/tally-simple.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/keshavsoft/tally-simple/issues"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./src/index.d.ts",
|
|
17
|
+
"import": "./src/index.js",
|
|
18
|
+
"default": "./src/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
"bin": {
|
|
23
|
+
"tally-simple": "./bin/tally-simple.js"
|
|
24
|
+
},
|
|
25
|
+
"main": "./src/index.js",
|
|
26
|
+
"types": "./src/index.d.ts",
|
|
27
|
+
"files": [
|
|
28
|
+
"bin",
|
|
29
|
+
"src",
|
|
30
|
+
"docs",
|
|
31
|
+
"README.md",
|
|
32
|
+
"CHANGELOG.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"generate:dts": "node generate-dts.js",
|
|
37
|
+
"test": "node --test test/test.js test/units.js",
|
|
38
|
+
"verify": "npm run generate:dts && npm test",
|
|
39
|
+
"prepack": "npm run verify",
|
|
40
|
+
"prepublishOnly": "npm run verify"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"tally",
|
|
44
|
+
"tdl",
|
|
45
|
+
"api",
|
|
46
|
+
"accounting"
|
|
47
|
+
],
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=20.10"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated from src/v3/external-api/api.json and src/v3/source.json.
|
|
3
|
+
* Do not edit this file directly. Run npm run generate:dts instead.
|
|
4
|
+
*/
|
|
5
|
+
export interface TallyClientOptions {
|
|
6
|
+
/** Tally HTTP endpoint. Defaults to http://localhost:9000. */
|
|
7
|
+
url?: string;
|
|
8
|
+
/** HTTP method used for the Tally request. Defaults to POST. */
|
|
9
|
+
method?: string;
|
|
10
|
+
/** Additional request headers. */
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
/** Abort the request after this many milliseconds. */
|
|
13
|
+
timeout?: number;
|
|
14
|
+
/** Optional fetch-compatible implementation for tests or adapters. */
|
|
15
|
+
fetch?: (url: string, options: {
|
|
16
|
+
method: string;
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
body: string;
|
|
19
|
+
signal?: unknown;
|
|
20
|
+
}) => Promise<{
|
|
21
|
+
ok: boolean;
|
|
22
|
+
status: number;
|
|
23
|
+
text: () => Promise<string>;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type TallyApi = {
|
|
28
|
+
masters: {
|
|
29
|
+
units: {
|
|
30
|
+
/** Runs tally.masters.units.fetch and returns Tally's raw XML response. */
|
|
31
|
+
fetch: (company: string) => Promise<string>;
|
|
32
|
+
};
|
|
33
|
+
stockItems: {
|
|
34
|
+
/** Runs tally.masters.stockItems.withBatches and returns Tally's raw XML response. */
|
|
35
|
+
withBatches: (company: string) => Promise<string>;
|
|
36
|
+
};
|
|
37
|
+
ledgers: {
|
|
38
|
+
/** Runs tally.masters.ledgers.withGstDetails and returns Tally's raw XML response. */
|
|
39
|
+
withGstDetails: (company: string) => Promise<string>;
|
|
40
|
+
};
|
|
41
|
+
stockGroup: {
|
|
42
|
+
/** Runs tally.masters.stockGroup.withParent and returns Tally's raw XML response. */
|
|
43
|
+
withParent: (company: string) => Promise<string>;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Create an independently configured Tally client. */
|
|
49
|
+
export declare const createTallyClient: (options?: TallyClientOptions) => TallyApi;
|
|
50
|
+
|
|
51
|
+
/** Default Tally client using the package connection defaults. */
|
|
52
|
+
export declare const tally: TallyApi;
|
|
53
|
+
|
|
54
|
+
export default tally;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import source from "../source.json" with { type: "json" };
|
|
2
|
+
import traverse from "../traverse.js";
|
|
3
|
+
import apiPaths from "./api.json" with { type: "json" };
|
|
4
|
+
|
|
5
|
+
const createApi = (options = {}) => {
|
|
6
|
+
const connection = {
|
|
7
|
+
...source.tally.connection,
|
|
8
|
+
...options.connection,
|
|
9
|
+
...(options.url ? { url: options.url } : {}),
|
|
10
|
+
...(options.method ? { method: options.method } : {}),
|
|
11
|
+
headers: {
|
|
12
|
+
...source.tally.connection.headers,
|
|
13
|
+
...options.connection?.headers,
|
|
14
|
+
...options.headers
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const request = options.request ?? source.tally.request;
|
|
18
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
19
|
+
const clientSource = {
|
|
20
|
+
...source,
|
|
21
|
+
tally: {
|
|
22
|
+
...source.tally,
|
|
23
|
+
connection,
|
|
24
|
+
request
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const createFunction = (path) => async (company) => {
|
|
29
|
+
if (typeof company !== "string" || !company.trim()) {
|
|
30
|
+
throw new TypeError("Company name is required.");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return await traverse(clientSource, "", path, {
|
|
34
|
+
company: company.trim(),
|
|
35
|
+
connection,
|
|
36
|
+
request,
|
|
37
|
+
fetch: fetchImpl,
|
|
38
|
+
timeout: options.timeout
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const root = {};
|
|
43
|
+
|
|
44
|
+
for (const path of apiPaths) {
|
|
45
|
+
const parts = path.split(".");
|
|
46
|
+
let current = root;
|
|
47
|
+
|
|
48
|
+
parts.forEach((part, index) => {
|
|
49
|
+
const isLast = index === parts.length - 1;
|
|
50
|
+
|
|
51
|
+
if (isLast) {
|
|
52
|
+
current[part] = createFunction(path);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
current[part] ??= {};
|
|
57
|
+
current = current[part];
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const rootName = apiPaths[0]?.split(".")[0];
|
|
62
|
+
|
|
63
|
+
return rootName ? root[rootName] : root;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export { createApi };
|
|
67
|
+
export default createApi();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createApi, default } from "./api.js";
|
package/src/v1/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createApi, default } from "./external-api/api.js";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tally": {
|
|
3
|
+
"connection": {
|
|
4
|
+
"url": "http://localhost:9000",
|
|
5
|
+
"method": "POST",
|
|
6
|
+
"headers": {
|
|
7
|
+
"Content-Type": "text/xml"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"request": {
|
|
11
|
+
"body": "<ENVELOPE>\n<HEADER>\n<VERSION>1</VERSION>\n<TALLYREQUEST>Export</TALLYREQUEST>\n<TYPE>Collection</TYPE>\n<ID>TDLID</ID>\n</HEADER>\n<BODY>\n<DESC>\n<STATICVARIABLES>\n<SVEXPORTFORMAT>$$SysName:XML</SVEXPORTFORMAT>\n<SVCURRENTCOMPANY>{company}</SVCURRENTCOMPANY>\n</STATICVARIABLES>\n<TDL>\n<TDLMESSAGE>\n<COLLECTION NAME=\"TDLID\">\n{collectionBody}</COLLECTION>\n</TDLMESSAGE>\n</TDL>\n</DESC>\n</BODY>\n</ENVELOPE>"
|
|
12
|
+
},
|
|
13
|
+
"masters": {
|
|
14
|
+
"units": {
|
|
15
|
+
"fetch": {
|
|
16
|
+
"tdl": {
|
|
17
|
+
"collection": "<TYPE>Unit</TYPE>\n<FETCH>$$Alias:Name</FETCH>\n"
|
|
18
|
+
},
|
|
19
|
+
"action": "fetch"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"stockItems": {
|
|
23
|
+
"withBatches": {
|
|
24
|
+
"tdl": {
|
|
25
|
+
"collection": "<TYPE>StockItem</TYPE><FETCH>$$Alias:Name</FETCH><FETCH>BaseUnits</FETCH><FETCH>BatchAllocations.*</FETCH>"
|
|
26
|
+
},
|
|
27
|
+
"action": "fetch"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"ledgers": {
|
|
31
|
+
"withGstDetails": {
|
|
32
|
+
"tdl": {
|
|
33
|
+
"collection": "<TYPE>Ledger</TYPE><FETCH>$$Alias:Name</FETCH><FETCH>LEDGSTREGDETAILS.LIST</FETCH>"
|
|
34
|
+
},
|
|
35
|
+
"action": "fetch"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import traverseObject from "./traverseObject/index.js";
|
|
2
|
+
|
|
3
|
+
const escapeXml = (value) => value.replace(/[&<>"']/g, (character) => ({
|
|
4
|
+
"&": "&",
|
|
5
|
+
"<": "<",
|
|
6
|
+
">": ">",
|
|
7
|
+
'"': """,
|
|
8
|
+
"'": "'"
|
|
9
|
+
}[character]));
|
|
10
|
+
|
|
11
|
+
const buildRequestBody = (requestBody, { company, collection }) => {
|
|
12
|
+
return requestBody
|
|
13
|
+
.replace("{company}", escapeXml(company))
|
|
14
|
+
.replace("{collectionBody}", collection);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const execute = async (connection, xml, fetchImpl, timeout) => {
|
|
18
|
+
if (typeof fetchImpl !== "function") {
|
|
19
|
+
throw new Error(
|
|
20
|
+
"No fetch implementation is available. Use Node.js 20+ or provide fetch to createTallyClient()."
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const controller = typeof AbortController === "function"
|
|
25
|
+
? new AbortController()
|
|
26
|
+
: undefined;
|
|
27
|
+
const timeoutId = Number.isFinite(timeout) && timeout > 0
|
|
28
|
+
? setTimeout(() => controller?.abort(), timeout)
|
|
29
|
+
: undefined;
|
|
30
|
+
|
|
31
|
+
let response;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
response = await fetchImpl(connection.url, {
|
|
35
|
+
method: connection.method,
|
|
36
|
+
headers: connection.headers,
|
|
37
|
+
body: xml,
|
|
38
|
+
...(controller ? { signal: controller.signal } : {})
|
|
39
|
+
});
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (controller?.signal.aborted) {
|
|
42
|
+
throw new Error(`Tally request timed out after ${timeout} ms.`, {
|
|
43
|
+
cause: error
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
throw error;
|
|
48
|
+
} finally {
|
|
49
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const responseText = await response.text();
|
|
53
|
+
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Tally request failed with HTTP ${response.status}: ${responseText}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return responseText;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const traverse = async (
|
|
64
|
+
raka,
|
|
65
|
+
relativePath,
|
|
66
|
+
pathToFind,
|
|
67
|
+
context = {}
|
|
68
|
+
) => {
|
|
69
|
+
if (relativePath === pathToFind) {
|
|
70
|
+
if (raka?.action === "fetch") {
|
|
71
|
+
const body = buildRequestBody(
|
|
72
|
+
context.request.body,
|
|
73
|
+
{
|
|
74
|
+
company: context.company,
|
|
75
|
+
collection: raka.tdl.collection
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return await execute(
|
|
80
|
+
context.connection,
|
|
81
|
+
body,
|
|
82
|
+
context.fetch,
|
|
83
|
+
context.timeout
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return raka;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (typeof raka === "object" && raka !== null) {
|
|
91
|
+
return await traverseObject(
|
|
92
|
+
raka,
|
|
93
|
+
relativePath,
|
|
94
|
+
pathToFind,
|
|
95
|
+
context
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return undefined;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export { traverse };
|
|
103
|
+
export default traverse;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { traverse } from "../traverse.js";
|
|
2
|
+
|
|
3
|
+
const traverseObject = async (
|
|
4
|
+
specJson,
|
|
5
|
+
relativePath,
|
|
6
|
+
pathToFind,
|
|
7
|
+
context = {}
|
|
8
|
+
) => {
|
|
9
|
+
const nextContext = {
|
|
10
|
+
...context,
|
|
11
|
+
connection: specJson.connection ?? context.connection,
|
|
12
|
+
request: specJson.request ?? context.request
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
for (const [key, value] of Object.entries(specJson)) {
|
|
16
|
+
if (key === "connection" || key === "request") continue;
|
|
17
|
+
|
|
18
|
+
if (typeof value === "object" && value !== null) {
|
|
19
|
+
const nextPath = relativePath
|
|
20
|
+
? `${relativePath}.${key}`
|
|
21
|
+
: key;
|
|
22
|
+
|
|
23
|
+
const result = await traverse(
|
|
24
|
+
value,
|
|
25
|
+
nextPath,
|
|
26
|
+
pathToFind,
|
|
27
|
+
nextContext
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (result !== undefined) {
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return undefined;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default traverseObject;
|