tally-simple 1.1.1 → 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.
Files changed (42) hide show
  1. package/CHANGELOG.md +8 -8
  2. package/LICENSE +21 -21
  3. package/README.md +127 -70
  4. package/bin/tally-simple.js +175 -175
  5. package/docs/api.html +41 -0
  6. package/docs/api.md +20 -19
  7. package/docs/architecture.html +74 -0
  8. package/docs/architecture.md +65 -43
  9. package/docs/developer.html +57 -0
  10. package/docs/developer.md +41 -0
  11. package/docs/end-user.html +49 -0
  12. package/docs/end-user.md +34 -0
  13. package/docs/index.html +32 -0
  14. package/docs/repo-developer.html +91 -0
  15. package/docs/repo-developer.md +107 -0
  16. package/package.json +55 -47
  17. package/src/index.d.ts +20 -2
  18. package/src/index.js +8 -8
  19. package/src/v1/external-api/api.js +67 -67
  20. package/src/v1/external-api/api.json +4 -4
  21. package/src/v1/external-api/index.js +1 -1
  22. package/src/v1/index.js +1 -1
  23. package/src/v1/source.json +40 -40
  24. package/src/v1/traverse.js +103 -103
  25. package/src/v1/traverseObject/index.js +39 -39
  26. package/src/v2/external-api/api.js +67 -0
  27. package/src/v2/external-api/api.json +5 -0
  28. package/src/v2/external-api/index.js +1 -0
  29. package/src/v2/index.js +1 -0
  30. package/src/v2/source.json +40 -0
  31. package/src/v2/traverse.js +103 -0
  32. package/src/v2/traverseObject/index.js +39 -0
  33. package/src/v3/external-api/api.js +67 -0
  34. package/src/v3/external-api/api.json +6 -0
  35. package/src/v3/external-api/index.js +1 -0
  36. package/src/v3/index.js +1 -0
  37. package/src/v3/source.json +48 -0
  38. package/src/v3/traverse.js +103 -0
  39. package/src/v3/traverseObject/index.js +39 -0
  40. package/docs/cli.md +0 -32
  41. package/docs/development.md +0 -66
  42. package/docs/usage.md +0 -73
package/CHANGELOG.md CHANGED
@@ -1,8 +1,8 @@
1
- # Changelog
2
-
3
- ## 1.0.0
4
-
5
- - Expose the Tally query API as an installable ESM package.
6
- - Add `createTallyClient()` for endpoint, header, timeout, and fetch configuration.
7
- - Add the `tally-simple` command for querying a published API path from a shell.
8
- - Keep the default import as a ready-to-use client for backwards compatibility.
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ - Expose the Tally query API as an installable ESM package.
6
+ - Add `createTallyClient()` for endpoint, header, timeout, and fetch configuration.
7
+ - Add the `tally-simple` command for querying a published API path from a shell.
8
+ - Keep the default import as a ready-to-use client for backwards compatibility.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 KeshavSoft
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 KeshavSoft
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,70 +1,127 @@
1
- # Tally Simple
2
-
3
- Tally Simple gives JavaScript applications and shell scripts a straightforward way to ask Tally for business data.
4
-
5
- Choose a supported query, provide the company name, and receive the response directly from Tally. The same queries work from an application or from the command line.
6
-
7
- ## Requirements
8
-
9
- - Node.js 20.10 or newer
10
- - A Tally HTTP endpoint that accepts the request, usually http://localhost:9000
11
-
12
- Tally Simple returns Tally's response body as XML text.
13
-
14
- ## Install
15
-
16
- ~~~bash
17
- npm install tally-simple
18
- ~~~
19
-
20
- ## Use it in JavaScript
21
-
22
- ~~~js
23
- import tally from "tally-simple";
24
-
25
- const xml = await tally.masters.units.fetch("Mani9");
26
- console.log(xml);
27
- ~~~
28
-
29
- ## Use it from the command line
30
-
31
- Run a query with npm's command runner:
32
-
33
- ~~~bash
34
- npx tally-simple masters.units.fetch --company Mani9
35
- ~~~
36
-
37
- You can also include the root name:
38
-
39
- ~~~bash
40
- npx tally-simple tally.masters.stockItems.withBatches \
41
- --company Mani9 \
42
- --url http://localhost:9000
43
- ~~~
44
-
45
- The response is written to stdout, so it can be saved or piped:
46
-
47
- ~~~bash
48
- npx tally-simple masters.ledgers.withGstDetails --company Mani9 > ledgers.xml
49
- ~~~
50
-
51
- For environment-based use:
52
-
53
- ~~~bash
54
- TALLY_COMPANY=Mani9 TALLY_URL=http://localhost:9000 \
55
- npx tally-simple masters.units.fetch
56
- ~~~
57
-
58
- Run npx tally-simple --help to see all CLI options.
59
-
60
- ## Available queries
61
-
62
- | Query | Returns |
63
- | --- | --- |
64
- | masters.units.fetch | Units and their aliases |
65
- | masters.stockItems.withBatches | Stock items, base units, and batch allocations |
66
- | masters.ledgers.withGstDetails | Ledgers and GST registration details |
67
-
68
- Every query accepts one company name. Blank company names are rejected before a request is sent. HTTP errors include the status and response body.
69
-
70
- More user-facing details are available in [usage](docs/usage.md), the [CLI reference](docs/cli.md), and the [available query list](docs/api.md).
1
+ # Tally Simple
2
+
3
+ [![npm version](https://img.shields.io/npm/v/tally-simple.svg)](https://www.npmjs.com/package/tally-simple)
4
+ [![license](https://img.shields.io/npm/l/tally-simple.svg)](https://github.com/keshavsoft/tally-simple/blob/main/LICENSE)
5
+
6
+ Tally Simple is a small, typed JavaScript client and CLI for asking Tally for business data.
7
+
8
+ Choose a supported query, provide the company name, and receive Tally's response as XML. The same query paths work in an application and from a shell, so a quick experiment can grow into an automated job without changing the request model.
9
+
10
+ **[View the package on npm](https://www.npmjs.com/package/tally-simple)** · **[Open the visual guide](docs/index.html)**
11
+
12
+ ## The story in one minute
13
+
14
+ Tally Simple sits between your code or shell and a Tally HTTP endpoint:
15
+
16
+ ~~~text
17
+ your app / shell command
18
+ │ choose a public query + company
19
+ ▼
20
+ Tally Simple
21
+ │ builds the XML request
22
+ ▼
23
+ Tally HTTP endpoint
24
+ │
25
+ ▼
26
+ XML response text
27
+ ~~~
28
+
29
+ It deliberately keeps the response raw. You can inspect it, save it, pipe it to another tool, or parse it with the XML library your application already uses.
30
+
31
+ ## Requirements
32
+
33
+ - Node.js 20.10 or newer
34
+ - A Tally HTTP endpoint that accepts the request, usually http://localhost:9000
35
+
36
+ Tally Simple returns Tally's response body as XML text.
37
+
38
+ ## Install
39
+
40
+ ~~~bash
41
+ npm install tally-simple
42
+ ~~~
43
+
44
+ The package has no runtime dependencies and requires Node.js 20.10 or newer.
45
+
46
+ ## Use it in JavaScript
47
+
48
+ ~~~js
49
+ import tally from "tally-simple";
50
+
51
+ const xml = await tally.masters.units.fetch("Mani9");
52
+ console.log(xml);
53
+ ~~~
54
+
55
+ The default import is a ready-to-use client. For a different endpoint, headers, timeout, or fetch implementation, use `createTallyClient()`:
56
+
57
+ ~~~js
58
+ import { createTallyClient } from "tally-simple";
59
+
60
+ const tally = createTallyClient({
61
+ url: "http://localhost:9000",
62
+ timeout: 15_000
63
+ });
64
+
65
+ const xml = await tally.masters.ledgers.withGstDetails("Mani9");
66
+ ~~~
67
+
68
+ ## Use it from the command line
69
+
70
+ Run a query with npm's command runner:
71
+
72
+ ~~~bash
73
+ npx tally-simple masters.units.fetch --company Mani9
74
+ ~~~
75
+
76
+ You can also include the root name:
77
+
78
+ ~~~bash
79
+ npx tally-simple tally.masters.stockItems.withBatches \
80
+ --company Mani9 \
81
+ --url http://localhost:9000
82
+ ~~~
83
+
84
+ The response is written to stdout, so it can be saved or piped:
85
+
86
+ ~~~bash
87
+ npx tally-simple masters.ledgers.withGstDetails --company Mani9 > ledgers.xml
88
+ ~~~
89
+
90
+ For environment-based use:
91
+
92
+ ~~~bash
93
+ TALLY_COMPANY=Mani9 TALLY_URL=http://localhost:9000 \
94
+ npx tally-simple masters.units.fetch
95
+ ~~~
96
+
97
+ Run npx tally-simple --help to see all CLI options.
98
+
99
+ In PowerShell, environment variables use this form:
100
+
101
+ ~~~powershell
102
+ $env:TALLY_COMPANY = "Mani9"
103
+ $env:TALLY_URL = "http://localhost:9000"
104
+ npx tally-simple masters.units.fetch
105
+ ~~~
106
+
107
+ ## Available queries
108
+
109
+ | Query | Returns |
110
+ | --- | --- |
111
+ | masters.units.fetch | Units and their aliases |
112
+ | masters.stockItems.withBatches | Stock items, base units, and batch allocations |
113
+ | masters.ledgers.withGstDetails | Ledgers and GST registration details |
114
+ | masters.stockGroup.withParent | Stock groups and their parent groups |
115
+
116
+ Every query accepts one company name. Blank company names are rejected before a request is sent. HTTP errors include the status and response body.
117
+
118
+ ## Choose your next step
119
+
120
+ - [JavaScript usage](docs/usage.md): configure the client, test without Tally, and use TypeScript.
121
+ - [CLI reference](docs/cli.md): see options, environment variables, piping, and errors.
122
+ - [Available query paths](docs/api.md): see the public API and the TDL each path requests.
123
+ - [Architecture story](docs/architecture.md): see how one definition drives the client and CLI.
124
+ - [Developer guide](docs/development.md): see versioning, declaration generation, and verification.
125
+ - [Visual guide](docs/index.html): a dependency-free HTML walkthrough of the same flow.
126
+
127
+ The source code is on [GitHub](https://github.com/keshavsoft/tally-simple), and the published package is on [npm](https://www.npmjs.com/package/tally-simple).
@@ -1,175 +1,175 @@
1
- #!/usr/bin/env node
2
-
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import packageInfo from "../package.json" with { type: "json" };
6
- import tally, { createTallyClient } from "../src/index.js";
7
-
8
- const usage = [
9
- "Usage:",
10
- " tally-simple <api-path> --company <company> [options]",
11
- "",
12
- "Examples:",
13
- " tally-simple masters.units.fetch --company Mani9",
14
- " tally-simple tally.masters.ledgers.withGstDetails --company Mani9 --url http://localhost:9000",
15
- "",
16
- "Options:",
17
- " -c, --company <name> Tally company name (or TALLY_COMPANY)",
18
- " -u, --url <url> Tally HTTP endpoint (or TALLY_URL)",
19
- " --header <k:v> Add a request header; may be repeated",
20
- " --timeout <ms> Abort a request after the given number of milliseconds",
21
- " -h, --help Show this help",
22
- " -v, --version Show the package version",
23
- "",
24
- "The API path is one of the paths listed in docs/api.md. The response is written",
25
- "to stdout exactly as Tally returns it, so it can be piped to another command.",
26
- ""
27
- ].join("\n");
28
-
29
- const readValue = (args, index, option) => {
30
- const value = args[index + 1];
31
-
32
- if (!value || value.startsWith("-")) {
33
- throw new Error(option + " requires a value.");
34
- }
35
-
36
- return value;
37
- };
38
-
39
- const parseHeader = (value) => {
40
- const separator = value.indexOf(":");
41
-
42
- if (separator < 1) {
43
- throw new Error("Invalid header \"" + value + "\". Use the form name:value.");
44
- }
45
-
46
- return [
47
- value.slice(0, separator).trim(),
48
- value.slice(separator + 1).trim()
49
- ];
50
- };
51
-
52
- const parseArgs = (args) => {
53
- const options = { headers: {} };
54
- let apiPath;
55
-
56
- for (let index = 0; index < args.length; index += 1) {
57
- const argument = args[index];
58
-
59
- if (argument === "--help" || argument === "-h") {
60
- return { help: true };
61
- }
62
-
63
- if (argument === "--version" || argument === "-v") {
64
- return { version: true };
65
- }
66
-
67
- if (argument === "--company" || argument === "-c") {
68
- options.company = readValue(args, index, argument);
69
- index += 1;
70
- continue;
71
- }
72
-
73
- if (argument === "--url" || argument === "-u") {
74
- options.url = readValue(args, index, argument);
75
- index += 1;
76
- continue;
77
- }
78
-
79
- if (argument === "--timeout") {
80
- const value = readValue(args, index, argument);
81
- options.timeout = Number(value);
82
-
83
- if (!Number.isFinite(options.timeout) || options.timeout <= 0) {
84
- throw new Error("--timeout must be a positive number of milliseconds.");
85
- }
86
-
87
- index += 1;
88
- continue;
89
- }
90
-
91
- if (argument === "--header") {
92
- const [name, value] = parseHeader(readValue(args, index, argument));
93
- options.headers[name] = value;
94
- index += 1;
95
- continue;
96
- }
97
-
98
- if (argument.startsWith("-")) {
99
- throw new Error("Unknown option: " + argument);
100
- }
101
-
102
- if (apiPath) {
103
- throw new Error(
104
- "Only one API path may be supplied; received \"" + argument + "\" too."
105
- );
106
- }
107
-
108
- apiPath = argument;
109
- }
110
-
111
- return { ...options, apiPath };
112
- };
113
-
114
- const normalizePath = (apiPath) => apiPath.startsWith("tally.")
115
- ? apiPath.slice("tally.".length)
116
- : apiPath;
117
-
118
- const findEndpoint = (apiPath, client = tally) => {
119
- const endpoint = normalizePath(apiPath).split(".").reduce(
120
- (current, part) => current?.[part],
121
- client
122
- );
123
-
124
- if (typeof endpoint !== "function") {
125
- throw new Error("Unknown API path: " + apiPath);
126
- }
127
-
128
- return endpoint;
129
- };
130
-
131
- const run = async (args) => {
132
- const parsed = parseArgs(args);
133
-
134
- if (parsed.help) {
135
- process.stdout.write(usage);
136
- return;
137
- }
138
-
139
- if (parsed.version) {
140
- process.stdout.write(packageInfo.version + "\n");
141
- return;
142
- }
143
-
144
- if (!parsed.apiPath) {
145
- throw new Error("An API path is required. Use --help to see examples.");
146
- }
147
-
148
- const company = parsed.company ?? process.env.TALLY_COMPANY;
149
-
150
- if (!company) {
151
- throw new Error("A company is required. Pass --company or set TALLY_COMPANY.");
152
- }
153
-
154
- const client = createTallyClient({
155
- url: parsed.url ?? process.env.TALLY_URL,
156
- headers: parsed.headers,
157
- timeout: parsed.timeout
158
- });
159
- const endpoint = findEndpoint(parsed.apiPath, client);
160
- const response = await endpoint(company);
161
-
162
- process.stdout.write(response + "\n");
163
- };
164
-
165
- const isMain = process.argv[1]
166
- && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
167
-
168
- if (isMain) {
169
- run(process.argv.slice(2)).catch((error) => {
170
- process.stderr.write("Error: " + error.message + "\n\n" + usage);
171
- process.exitCode = 1;
172
- });
173
- }
174
-
175
- export { findEndpoint, parseArgs, run };
1
+ #!/usr/bin/env node
2
+
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import packageInfo from "../package.json" with { type: "json" };
6
+ import tally, { createTallyClient } from "../src/index.js";
7
+
8
+ const usage = [
9
+ "Usage:",
10
+ " tally-simple <api-path> --company <company> [options]",
11
+ "",
12
+ "Examples:",
13
+ " tally-simple masters.units.fetch --company Mani9",
14
+ " tally-simple tally.masters.ledgers.withGstDetails --company Mani9 --url http://localhost:9000",
15
+ "",
16
+ "Options:",
17
+ " -c, --company <name> Tally company name (or TALLY_COMPANY)",
18
+ " -u, --url <url> Tally HTTP endpoint (or TALLY_URL)",
19
+ " --header <k:v> Add a request header; may be repeated",
20
+ " --timeout <ms> Abort a request after the given number of milliseconds",
21
+ " -h, --help Show this help",
22
+ " -v, --version Show the package version",
23
+ "",
24
+ "The API path is one of the paths listed in docs/api.md. The response body is",
25
+ "written to stdout with a trailing newline, so it can be piped to another command.",
26
+ ""
27
+ ].join("\n");
28
+
29
+ const readValue = (args, index, option) => {
30
+ const value = args[index + 1];
31
+
32
+ if (!value || value.startsWith("-")) {
33
+ throw new Error(option + " requires a value.");
34
+ }
35
+
36
+ return value;
37
+ };
38
+
39
+ const parseHeader = (value) => {
40
+ const separator = value.indexOf(":");
41
+
42
+ if (separator < 1) {
43
+ throw new Error("Invalid header \"" + value + "\". Use the form name:value.");
44
+ }
45
+
46
+ return [
47
+ value.slice(0, separator).trim(),
48
+ value.slice(separator + 1).trim()
49
+ ];
50
+ };
51
+
52
+ const parseArgs = (args) => {
53
+ const options = { headers: {} };
54
+ let apiPath;
55
+
56
+ for (let index = 0; index < args.length; index += 1) {
57
+ const argument = args[index];
58
+
59
+ if (argument === "--help" || argument === "-h") {
60
+ return { help: true };
61
+ }
62
+
63
+ if (argument === "--version" || argument === "-v") {
64
+ return { version: true };
65
+ }
66
+
67
+ if (argument === "--company" || argument === "-c") {
68
+ options.company = readValue(args, index, argument);
69
+ index += 1;
70
+ continue;
71
+ }
72
+
73
+ if (argument === "--url" || argument === "-u") {
74
+ options.url = readValue(args, index, argument);
75
+ index += 1;
76
+ continue;
77
+ }
78
+
79
+ if (argument === "--timeout") {
80
+ const value = readValue(args, index, argument);
81
+ options.timeout = Number(value);
82
+
83
+ if (!Number.isFinite(options.timeout) || options.timeout <= 0) {
84
+ throw new Error("--timeout must be a positive number of milliseconds.");
85
+ }
86
+
87
+ index += 1;
88
+ continue;
89
+ }
90
+
91
+ if (argument === "--header") {
92
+ const [name, value] = parseHeader(readValue(args, index, argument));
93
+ options.headers[name] = value;
94
+ index += 1;
95
+ continue;
96
+ }
97
+
98
+ if (argument.startsWith("-")) {
99
+ throw new Error("Unknown option: " + argument);
100
+ }
101
+
102
+ if (apiPath) {
103
+ throw new Error(
104
+ "Only one API path may be supplied; received \"" + argument + "\" too."
105
+ );
106
+ }
107
+
108
+ apiPath = argument;
109
+ }
110
+
111
+ return { ...options, apiPath };
112
+ };
113
+
114
+ const normalizePath = (apiPath) => apiPath.startsWith("tally.")
115
+ ? apiPath.slice("tally.".length)
116
+ : apiPath;
117
+
118
+ const findEndpoint = (apiPath, client = tally) => {
119
+ const endpoint = normalizePath(apiPath).split(".").reduce(
120
+ (current, part) => current?.[part],
121
+ client
122
+ );
123
+
124
+ if (typeof endpoint !== "function") {
125
+ throw new Error("Unknown API path: " + apiPath);
126
+ }
127
+
128
+ return endpoint;
129
+ };
130
+
131
+ const run = async (args) => {
132
+ const parsed = parseArgs(args);
133
+
134
+ if (parsed.help) {
135
+ process.stdout.write(usage);
136
+ return;
137
+ }
138
+
139
+ if (parsed.version) {
140
+ process.stdout.write(packageInfo.version + "\n");
141
+ return;
142
+ }
143
+
144
+ if (!parsed.apiPath) {
145
+ throw new Error("An API path is required. Use --help to see examples.");
146
+ }
147
+
148
+ const company = parsed.company ?? process.env.TALLY_COMPANY;
149
+
150
+ if (!company) {
151
+ throw new Error("A company is required. Pass --company or set TALLY_COMPANY.");
152
+ }
153
+
154
+ const client = createTallyClient({
155
+ url: parsed.url ?? process.env.TALLY_URL,
156
+ headers: parsed.headers,
157
+ timeout: parsed.timeout
158
+ });
159
+ const endpoint = findEndpoint(parsed.apiPath, client);
160
+ const response = await endpoint(company);
161
+
162
+ process.stdout.write(response + "\n");
163
+ };
164
+
165
+ const isMain = process.argv[1]
166
+ && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
167
+
168
+ if (isMain) {
169
+ run(process.argv.slice(2)).catch((error) => {
170
+ process.stderr.write("Error: " + error.message + "\n\n" + usage);
171
+ process.exitCode = 1;
172
+ });
173
+ }
174
+
175
+ export { findEndpoint, parseArgs, run };
package/docs/api.html ADDED
@@ -0,0 +1,41 @@
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>Api</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>API Reference</h1>
33
+ <p>The public API is generated from <code>src/v1/external-api/api.json</code>.</p>
34
+ <p>Current public paths:</p>
35
+ <pre><code>tally.masters.units.fetch
36
+ tally.masters.stockItems.withBatches</code></pre>
37
+ <p>Example:</p>
38
+ <pre><code>await tally.masters.units.fetch(&quot;Mani9&quot;);</code></pre>
39
+ <p>The exact public surface should be changed through <code>external-api/api.json</code>, followed by declaration generation.</p>
40
+ </body>
41
+ </html>