tally-simple 1.1.1 → 1.8.2
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 -8
- package/LICENSE +21 -21
- package/README.md +176 -70
- package/bin/tally-simple.js +170 -175
- package/docs/.nojekyll +1 -0
- package/docs/architecture.html +73 -0
- package/docs/architecture.md +82 -43
- package/docs/execution-pipeline.html +71 -0
- package/docs/execution-pipeline.md +158 -0
- package/docs/index.html +294 -0
- package/docs/queries.html +107 -0
- package/docs/queries.md +122 -0
- package/docs/route-engine.html +57 -0
- package/docs/route-engine.md +141 -0
- package/docs/style.css +137 -0
- package/docs/usage.html +82 -0
- package/docs/usage.md +117 -73
- package/package.json +61 -47
- package/src/index.js +2 -8
- package/src/v5/external-api/api.js +21 -0
- package/src/v5/external-api/api.json +6 -0
- package/src/v5/index.js +1 -0
- package/src/v5/internal-working/execution/buildXmlBody.js +41 -0
- package/src/v5/internal-working/execution/getTdl.js +12 -0
- package/src/v5/internal-working/execution/index.js +30 -0
- package/src/v5/internal-working/execution/postHttp.js +26 -0
- package/src/v5/internal-working/execution/validateCompany.js +11 -0
- package/src/v5/internal-working/route/attachPath.js +25 -0
- package/src/v5/internal-working/route/createLeafHandler.js +17 -0
- package/src/v5/internal-working/route/index.js +24 -0
- package/src/{v1 → v5}/source.json +38 -40
- package/src/v6/external-api/api.js +21 -0
- package/src/v6/external-api/api.json +6 -0
- package/src/v6/index.js +1 -0
- package/src/v6/internal-working/execution/buildXmlBody.js +41 -0
- package/src/v6/internal-working/execution/getTdl.js +12 -0
- package/src/v6/internal-working/execution/index.js +30 -0
- package/src/v6/internal-working/execution/postHttp.js +26 -0
- package/src/v6/internal-working/execution/validateCompany.js +11 -0
- package/src/v6/internal-working/route/attachPath.js +25 -0
- package/src/v6/internal-working/route/createLeafHandler.js +17 -0
- package/src/v6/internal-working/route/index.js +24 -0
- package/src/v6/source.json +38 -0
- package/src/v7/api.json +6 -0
- package/src/v7/index.js +14 -0
- package/src/v7/internal-working/execution/buildXmlBody.js +41 -0
- package/src/v7/internal-working/execution/getTdl.js +12 -0
- package/src/v7/internal-working/execution/index.js +30 -0
- package/src/v7/internal-working/execution/postHttp.js +26 -0
- package/src/v7/internal-working/execution/validateCompany.js +11 -0
- package/src/v7/internal-working/route/attachPath.js +25 -0
- package/src/v7/internal-working/route/createLeafHandler.js +17 -0
- package/src/v7/internal-working/route/index.js +24 -0
- package/src/v7/source.json +38 -0
- package/src/v8/engine/index.js +18 -0
- package/src/v8/engine/parseXml.js +11 -0
- package/src/v8/index.js +21 -0
- package/docs/api.md +0 -19
- package/docs/cli.md +0 -32
- package/docs/development.md +0 -66
- package/src/index.d.ts +0 -36
- package/src/v1/external-api/api.js +0 -67
- package/src/v1/external-api/api.json +0 -5
- package/src/v1/external-api/index.js +0 -1
- package/src/v1/index.js +0 -1
- package/src/v1/traverse.js +0 -103
- package/src/v1/traverseObject/index.js +0 -39
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import validateCompany from "./validateCompany.js";
|
|
2
|
+
import getTdl from "./getTdl.js";
|
|
3
|
+
import buildXmlBody from "./buildXmlBody.js";
|
|
4
|
+
import postHttp from "./postHttp.js";
|
|
5
|
+
|
|
6
|
+
const startFunc = async ({ inRoutePath, inCompany, inSource }) => {
|
|
7
|
+
const localRoutePath = inRoutePath;
|
|
8
|
+
const localCompany = inCompany;
|
|
9
|
+
const localSource = inSource;
|
|
10
|
+
|
|
11
|
+
const companyName = validateCompany({
|
|
12
|
+
inCompany: localCompany
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const tdl = getTdl({
|
|
16
|
+
inSource: localSource,
|
|
17
|
+
inRoutePath: localRoutePath
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const xmlBody = buildXmlBody({
|
|
21
|
+
inCompany: companyName,
|
|
22
|
+
inTdl: tdl
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return await postHttp({
|
|
26
|
+
inXmlBody: xmlBody
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default startFunc;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const url = "http://localhost:9000";
|
|
2
|
+
const headers = {
|
|
3
|
+
"Content-Type": "text/xml"
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
const startFunc = async ({ inXmlBody }) => {
|
|
7
|
+
const localXmlBody = inXmlBody;
|
|
8
|
+
|
|
9
|
+
const response = await fetch(url, {
|
|
10
|
+
method: "POST",
|
|
11
|
+
headers: headers,
|
|
12
|
+
body: localXmlBody
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const responseText = await response.text();
|
|
16
|
+
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`Tally request failed with HTTP ${response.status}: ${responseText}`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return responseText;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default startFunc;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const startFunc = ({ inCompany }) => {
|
|
2
|
+
const localCompany = inCompany;
|
|
3
|
+
|
|
4
|
+
if (typeof localCompany !== "string" || !localCompany.trim()) {
|
|
5
|
+
throw new TypeError("Company name is required.");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return localCompany.trim();
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default startFunc;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import createLeafHandler from "./createLeafHandler.js";
|
|
2
|
+
|
|
3
|
+
const startFunc = ({ inTree, inPath, inSource, inExecutor }) => {
|
|
4
|
+
const localTree = inTree;
|
|
5
|
+
const localPath = inPath;
|
|
6
|
+
const localSource = inSource;
|
|
7
|
+
const localExecutor = inExecutor;
|
|
8
|
+
|
|
9
|
+
const parts = localPath.split(".");
|
|
10
|
+
const leafName = parts.pop();
|
|
11
|
+
|
|
12
|
+
let branch = localTree;
|
|
13
|
+
for (const segment of parts) {
|
|
14
|
+
branch[segment] ??= {};
|
|
15
|
+
branch = branch[segment];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
branch[leafName] = createLeafHandler({
|
|
19
|
+
inPath: localPath,
|
|
20
|
+
inSource: localSource,
|
|
21
|
+
inExecutor: localExecutor
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default startFunc;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const startFunc = ({ inPath, inSource, inExecutor }) => {
|
|
2
|
+
const localPath = inPath;
|
|
3
|
+
const localSource = inSource;
|
|
4
|
+
const localExecutor = inExecutor;
|
|
5
|
+
|
|
6
|
+
return async (inCompany) => {
|
|
7
|
+
const localCompany = inCompany;
|
|
8
|
+
|
|
9
|
+
return await localExecutor({
|
|
10
|
+
inRoutePath: localPath,
|
|
11
|
+
inCompany: localCompany,
|
|
12
|
+
inSource: localSource
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default startFunc;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import attachPath from "./attachPath.js";
|
|
2
|
+
|
|
3
|
+
const startFunc = ({ inApiPaths, inSource, inExecutor }) => {
|
|
4
|
+
const localApiPaths = inApiPaths;
|
|
5
|
+
const localSource = inSource;
|
|
6
|
+
const localExecutor = inExecutor;
|
|
7
|
+
|
|
8
|
+
const tree = {};
|
|
9
|
+
|
|
10
|
+
for (const path of localApiPaths) {
|
|
11
|
+
attachPath({
|
|
12
|
+
inTree: tree,
|
|
13
|
+
inPath: path,
|
|
14
|
+
inSource: localSource,
|
|
15
|
+
inExecutor: localExecutor
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const rootNamespace = localApiPaths[0]?.split(".")[0];
|
|
20
|
+
|
|
21
|
+
return rootNamespace ? tree[rootNamespace] : tree;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default startFunc;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tally": {
|
|
3
|
+
"masters": {
|
|
4
|
+
"units": {
|
|
5
|
+
"fetch": {
|
|
6
|
+
"tdl": {
|
|
7
|
+
"collection": "<TYPE>Unit</TYPE>\n<FETCH>$$Alias:Name</FETCH>\n"
|
|
8
|
+
},
|
|
9
|
+
"action": "fetch"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"stockItems": {
|
|
13
|
+
"withBatches": {
|
|
14
|
+
"tdl": {
|
|
15
|
+
"collection": "<TYPE>StockItem</TYPE><FETCH>$$Alias:Name</FETCH><FETCH>BaseUnits</FETCH><FETCH>BatchAllocations.*</FETCH>"
|
|
16
|
+
},
|
|
17
|
+
"action": "fetch"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"ledgers": {
|
|
21
|
+
"withGstDetails": {
|
|
22
|
+
"tdl": {
|
|
23
|
+
"collection": "<TYPE>Ledger</TYPE><FETCH>$$Alias:Name</FETCH><FETCH>LEDGSTREGDETAILS.LIST</FETCH>"
|
|
24
|
+
},
|
|
25
|
+
"action": "fetch"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"stockGroup": {
|
|
29
|
+
"withParent": {
|
|
30
|
+
"tdl": {
|
|
31
|
+
"collection": "<TYPE>StockGroup</TYPE><FETCH>$$Alias:Name</FETCH><FETCH>Parent</FETCH>"
|
|
32
|
+
},
|
|
33
|
+
"action": "fetch"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { call } from "tally-simple-json";
|
|
2
|
+
import transform from "@keshavsoft/json-transformer";
|
|
3
|
+
|
|
4
|
+
const startFunc = async ({ inRoutePath, inParam, inSource, inLeafSpec }) => {
|
|
5
|
+
const localRoutePath = inRoutePath;
|
|
6
|
+
const localParam = inParam;
|
|
7
|
+
const localSource = inSource;
|
|
8
|
+
const localLeafSpec = inLeafSpec;
|
|
9
|
+
|
|
10
|
+
const rawResponse = await call(localRoutePath,
|
|
11
|
+
localParam);
|
|
12
|
+
const jsonNeeded = rawResponse?.ENVELOPE?.BODY?.DATA?.COLLECTION;
|
|
13
|
+
const transformedResponse = transform(jsonNeeded, localLeafSpec?.transform?.instructions);
|
|
14
|
+
// console.log("localSource: ", jsonNeeded, localLeafSpec?.transform?.instructions, transformedResponse);
|
|
15
|
+
return transformedResponse;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default startFunc;
|
package/src/v8/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import apiTree, { createCaller } from "@keshavsoft/api-tree";
|
|
2
|
+
import { source, apiPaths } from "tally-spec";
|
|
3
|
+
|
|
4
|
+
import execute from "./engine/index.js";
|
|
5
|
+
|
|
6
|
+
const tree = apiTree(source, apiPaths, execute);
|
|
7
|
+
const app = tree.tally ?? tree;
|
|
8
|
+
|
|
9
|
+
// Keep the package's historical root API while retaining the explicit
|
|
10
|
+
// `app.tally` form exposed by @keshavsoft/api-tree.
|
|
11
|
+
if (!app.tally) {
|
|
12
|
+
Object.defineProperty(app, "tally", {
|
|
13
|
+
value: app,
|
|
14
|
+
enumerable: false
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const call = createCaller({ inSource: source, inExecutor: execute });
|
|
19
|
+
|
|
20
|
+
export default app;
|
|
21
|
+
export { app, call };
|
package/docs/api.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# Published API paths
|
|
2
|
-
|
|
3
|
-
These are the paths intentionally exposed by the package. The root tally is present on the default JavaScript import, while the CLI accepts paths with or without that root.
|
|
4
|
-
|
|
5
|
-
| JavaScript call | CLI path | TDL request |
|
|
6
|
-
| --- | --- | --- |
|
|
7
|
-
| tally.masters.units.fetch(company) | masters.units.fetch | Unit with $$Alias:Name |
|
|
8
|
-
| tally.masters.stockItems.withBatches(company) | masters.stockItems.withBatches | StockItem with base units and batch allocations |
|
|
9
|
-
| tally.masters.ledgers.withGstDetails(company) | masters.ledgers.withGstDetails | Ledger with GST registration details |
|
|
10
|
-
|
|
11
|
-
All current calls:
|
|
12
|
-
|
|
13
|
-
- accept one company name;
|
|
14
|
-
- make a POST request with XML;
|
|
15
|
-
- return Tally's response body as a string;
|
|
16
|
-
- use the configured endpoint and headers;
|
|
17
|
-
- reject blank company names.
|
|
18
|
-
|
|
19
|
-
The source of truth for the request definitions is src/v1/source.json. The allowlist for this published surface is src/v1/external-api/api.json.
|
package/docs/cli.md
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# CLI reference
|
|
2
|
-
|
|
3
|
-
The package publishes the tally-simple executable. With npm, use it through npx:
|
|
4
|
-
|
|
5
|
-
~~~bash
|
|
6
|
-
npx tally-simple <api-path> --company <company>
|
|
7
|
-
~~~
|
|
8
|
-
|
|
9
|
-
## Options
|
|
10
|
-
|
|
11
|
-
| Option | Meaning |
|
|
12
|
-
| --- | --- |
|
|
13
|
-
| -c, --company <name> | Company to query. Also reads TALLY_COMPANY. |
|
|
14
|
-
| -u, --url <url> | Tally endpoint. Also reads TALLY_URL. |
|
|
15
|
-
| --header <name:value> | Adds a request header. Repeat it for multiple headers. |
|
|
16
|
-
| --timeout <ms> | Aborts a request after the given number of milliseconds. |
|
|
17
|
-
| -h, --help | Prints command help. |
|
|
18
|
-
| -v, --version | Prints the installed package version. |
|
|
19
|
-
|
|
20
|
-
The command accepts masters.units.fetch and tally.masters.units.fetch equivalently. The response is written unchanged to stdout; errors and usage help are written to stderr.
|
|
21
|
-
|
|
22
|
-
Examples:
|
|
23
|
-
|
|
24
|
-
~~~bash
|
|
25
|
-
npx tally-simple masters.units.fetch --company Mani9
|
|
26
|
-
|
|
27
|
-
npx tally-simple masters.stockItems.withBatches \
|
|
28
|
-
--company Mani9 \
|
|
29
|
-
--header X-Request-Source:nightly
|
|
30
|
-
|
|
31
|
-
TALLY_COMPANY=Mani9 npx tally-simple masters.units.fetch
|
|
32
|
-
~~~
|
package/docs/development.md
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# Developer guide
|
|
2
|
-
|
|
3
|
-
This document explains how Tally Simple is built and how to change it. End-user installation and query examples are kept in the root README.
|
|
4
|
-
|
|
5
|
-
## The developer story
|
|
6
|
-
|
|
7
|
-
Tally Simple keeps the TDL request definitions in one internal source and exposes only the queries deliberately selected for users.
|
|
8
|
-
|
|
9
|
-
~~~text
|
|
10
|
-
source.json
|
|
11
|
-
│
|
|
12
|
-
├── public API list ──> JavaScript client
|
|
13
|
-
│
|
|
14
|
-
└── request traversal ──> XML over HTTP ──> Tally
|
|
15
|
-
│
|
|
16
|
-
└──> CLI stdout
|
|
17
|
-
~~~
|
|
18
|
-
|
|
19
|
-
The source definitions describe how Tally should answer a query. The public API list is the product boundary: only listed paths become JavaScript methods and CLI commands.
|
|
20
|
-
|
|
21
|
-
## Repository map
|
|
22
|
-
|
|
23
|
-
- src/v1/source.json: Tally connection defaults, request envelope, and TDL collections.
|
|
24
|
-
- src/v1/external-api/api.json: public query allowlist.
|
|
25
|
-
- src/v1/external-api/api.js: builds the nested runtime API.
|
|
26
|
-
- src/v1/traverse.js: validates, builds, and sends a request.
|
|
27
|
-
- src/index.d.ts: generated TypeScript declarations.
|
|
28
|
-
- bin/tally-simple.js: command-line entrypoint.
|
|
29
|
-
- test/test.js and test/units.js: offline behavior and CLI tests.
|
|
30
|
-
- test/v1/units.js: manual integration smoke check against a reachable Tally instance; it is not part of npm test.
|
|
31
|
-
|
|
32
|
-
## Request lifecycle
|
|
33
|
-
|
|
34
|
-
1. The application or CLI selects a public query path.
|
|
35
|
-
2. The client validates and trims the company name.
|
|
36
|
-
3. The path is resolved in source.json.
|
|
37
|
-
4. The company name is XML-escaped and inserted into the request envelope.
|
|
38
|
-
5. The selected collection body is inserted into the envelope.
|
|
39
|
-
6. The configured fetch implementation sends the request to Tally.
|
|
40
|
-
7. The response body is returned as text; non-2xx responses become errors.
|
|
41
|
-
|
|
42
|
-
## Adding a query
|
|
43
|
-
|
|
44
|
-
1. Add or update the TDL definition in src/v1/source.json.
|
|
45
|
-
2. Add its complete path to src/v1/external-api/api.json.
|
|
46
|
-
3. Run npm run generate:dts.
|
|
47
|
-
4. Add or update an offline test.
|
|
48
|
-
5. Run npm run verify.
|
|
49
|
-
|
|
50
|
-
Do not edit src/index.d.ts by hand. It is generated from the source definition and public path list.
|
|
51
|
-
|
|
52
|
-
## Client configuration
|
|
53
|
-
|
|
54
|
-
createTallyClient() is the application boundary for a different URL, method, headers, timeout, or fetch implementation. The default import uses the default connection from source.json.
|
|
55
|
-
|
|
56
|
-
The fetch option is intentionally injectable so tests can run without a Tally installation or network connection.
|
|
57
|
-
|
|
58
|
-
## Verification and publishing
|
|
59
|
-
|
|
60
|
-
~~~bash
|
|
61
|
-
npm install
|
|
62
|
-
npm run verify
|
|
63
|
-
npm pack --dry-run
|
|
64
|
-
~~~
|
|
65
|
-
|
|
66
|
-
The prepack and prepublishOnly hooks run declaration generation and the offline test suite. The package files allowlist publishes the runtime, CLI, user documentation, and package metadata while excluding tests and development-only scripts.
|
package/src/index.d.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export interface TallyClientOptions {
|
|
2
|
-
url?: string;
|
|
3
|
-
method?: string;
|
|
4
|
-
headers?: Record<string, string>;
|
|
5
|
-
timeout?: number;
|
|
6
|
-
fetch?: (url: string, options: {
|
|
7
|
-
method: string;
|
|
8
|
-
headers: Record<string, string>;
|
|
9
|
-
body: string;
|
|
10
|
-
signal?: unknown;
|
|
11
|
-
}) => Promise<{
|
|
12
|
-
ok: boolean;
|
|
13
|
-
status: number;
|
|
14
|
-
text: () => Promise<string>;
|
|
15
|
-
}>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type TallyApi = {
|
|
19
|
-
masters: {
|
|
20
|
-
units: {
|
|
21
|
-
fetch: (company: string) => Promise<string>;
|
|
22
|
-
};
|
|
23
|
-
stockItems: {
|
|
24
|
-
withBatches: (company: string) => Promise<string>;
|
|
25
|
-
};
|
|
26
|
-
ledgers: {
|
|
27
|
-
withGstDetails: (company: string) => Promise<string>;
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
declare const tally: TallyApi;
|
|
33
|
-
|
|
34
|
-
export declare const createTallyClient: (options?: TallyClientOptions) => TallyApi;
|
|
35
|
-
export declare const tally: TallyApi;
|
|
36
|
-
export default tally;
|
|
@@ -1,67 +0,0 @@
|
|
|
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();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { createApi, default } from "./api.js";
|
package/src/v1/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { createApi, default } from "./external-api/api.js";
|
package/src/v1/traverse.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
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;
|
|
@@ -1,39 +0,0 @@
|
|
|
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;
|