@superheld/summae-cli 0.2.0

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/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @superheld/summae-cli
2
+
3
+ Terminal-Werkzeug (Node) für [summae](../core) — JSON rein, JSON raus, mit
4
+ persistentem SQLite-Arbeitsbereich (über [`@superheld/summae-knex`](../knex)).
5
+ Pendant zur PHP-CLI; gleiche Operationen, gleiches Datenformat.
6
+
7
+ ```bash
8
+ npm install -g @superheld/summae-cli
9
+
10
+ summae init --name "Muster GmbH" --rules regeln.json # summae.json + summae.sqlite
11
+ summae op postVoucher --input @beleg.json # Schreiboperation (oder --input '{…}')
12
+ summae report trialBalance --params '{"fiscalYear":2026}'
13
+ ```
14
+
15
+ `summae.json` trägt Mandanten-Meta + Regelmodul-Daten (App-Schicht); `summae.sqlite`
16
+ die Buchungsdaten. Jeder Aufruf lädt den Mandanten, führt aus, die DB persistiert.
17
+ Exit-Codes bilden den Fehlerkatalog ab (0 = Erfolg).
18
+
19
+ Vollständige API + Datenformat: **[zentrales Handbuch](https://github.com/Superheld/summae/blob/main/docs/handbuch/README.md)**.
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/workspace.ts
4
+ import { existsSync, readFileSync, writeFileSync } from "fs";
5
+ import { join } from "path";
6
+ import {
7
+ Currency,
8
+ DimensionRegistry,
9
+ MappingRegistry,
10
+ SystemClock,
11
+ TaxCodeRegistry,
12
+ TaxProfile,
13
+ Uuid,
14
+ UuidV7IdGenerator
15
+ } from "@superheld/summae-core";
16
+ import { DatabaseTenantFactory, SyncDb, installSchema } from "@superheld/summae-knex";
17
+ var CONFIG_FILE = "summae.json";
18
+ var DB_FILE = "summae.sqlite";
19
+ function isRecord(v) {
20
+ return v !== null && typeof v === "object" && !Array.isArray(v);
21
+ }
22
+ function recordList(v) {
23
+ return Array.isArray(v) ? v.filter(isRecord) : [];
24
+ }
25
+ var Workspace = class _Workspace {
26
+ constructor(directory) {
27
+ this.directory = directory;
28
+ }
29
+ directory;
30
+ static in(directory) {
31
+ return new _Workspace(directory.replace(/\/+$/, ""));
32
+ }
33
+ exists() {
34
+ return existsSync(this.configPath());
35
+ }
36
+ /** @param ruleData accounts, taxCodes, taxProfile, dimensionTypes/-Values, ruleModules */
37
+ initialize(name, currency, ruleData) {
38
+ if (this.exists()) {
39
+ throw new Error(`Arbeitsbereich existiert bereits: ${this.configPath()}`);
40
+ }
41
+ const config = { name, baseCurrency: currency, tenantId: Uuid.v7().value, rules: ruleData };
42
+ writeFileSync(this.configPath(), `${JSON.stringify(config, null, 2)}
43
+ `);
44
+ const db = new SyncDb(this.dbPath());
45
+ installSchema(db);
46
+ db.close();
47
+ }
48
+ tenant() {
49
+ if (!this.exists()) {
50
+ throw new Error(`Kein Arbeitsbereich in ${this.directory} \u2014 zuerst \`summae init\` ausf\xFChren`);
51
+ }
52
+ const config = JSON.parse(readFileSync(this.configPath(), "utf8"));
53
+ const rules = isRecord(config.rules) ? config.rules : {};
54
+ const ruleModules = isRecord(rules.ruleModules) ? rules.ruleModules : {};
55
+ const dimensionTypes = recordList(rules.dimensionTypes).map((t) => ({ code: String(t.code) }));
56
+ const dimensionValues = recordList(rules.dimensionValues).map((v) => ({
57
+ typeCode: String(v.typeCode),
58
+ code: String(v.code)
59
+ }));
60
+ const dimensionRules = recordList(ruleModules.dimensionRules).map((r) => {
61
+ const range = isRecord(r.accountRange) ? r.accountRange : {};
62
+ return { accountRange: { from: String(range.from), to: String(range.to) }, requiredDimension: String(r.requiredDimension) };
63
+ });
64
+ const clock = new SystemClock();
65
+ const tenantId = typeof config.tenantId === "string" ? Uuid.fromString(config.tenantId) : void 0;
66
+ const tenant = DatabaseTenantFactory.build(
67
+ new SyncDb(this.dbPath()),
68
+ typeof config.name === "string" ? config.name : "CLI",
69
+ Currency.of(typeof config.baseCurrency === "string" ? config.baseCurrency : "EUR"),
70
+ clock,
71
+ new UuidV7IdGenerator(clock),
72
+ {
73
+ dimensions: DimensionRegistry.fromData(dimensionTypes, dimensionValues, dimensionRules),
74
+ taxCodes: TaxCodeRegistry.fromData(recordList(rules.taxCodes)),
75
+ taxProfile: TaxProfile.fromData(isRecord(rules.taxProfile) ? rules.taxProfile : {}),
76
+ mappings: MappingRegistry.fromRuleModules(Array.isArray(ruleModules.mappings) ? ruleModules.mappings : []),
77
+ ...tenantId ? { tenantId } : {}
78
+ }
79
+ );
80
+ tenant.assetService.setRuleModule(ruleModules);
81
+ return tenant;
82
+ }
83
+ configPath() {
84
+ return join(this.directory, CONFIG_FILE);
85
+ }
86
+ dbPath() {
87
+ return join(this.directory, DB_FILE);
88
+ }
89
+ };
90
+
91
+ // src/exit-codes.ts
92
+ var CODES = [
93
+ "E_ENTRY_UNBALANCED",
94
+ "E_ENTRY_NO_VOUCHER",
95
+ "E_VOUCHER_UNKNOWN",
96
+ "E_ENTRY_TOO_FEW_LINES",
97
+ "E_ENTRY_INVALID_AMOUNT",
98
+ "E_ENTRY_FINALIZED",
99
+ "E_ENTRY_ALREADY_REVERSED",
100
+ "E_ENTRY_UNKNOWN",
101
+ "E_PERIOD_CLOSED",
102
+ "E_PERIOD_OUT_OF_ORDER",
103
+ "E_PERIOD_UNKNOWN",
104
+ "E_FISCALYEAR_CLOSED",
105
+ "E_FISCALYEAR_UNFINALIZED_ENTRIES",
106
+ "E_FISCALYEAR_OVERLAP",
107
+ "E_ACCOUNT_UNKNOWN",
108
+ "E_ACCOUNT_LOCKED",
109
+ "E_ACCOUNT_NUMBER_TAKEN",
110
+ "E_COA_FORMAT_INVALID",
111
+ "E_SETTLEMENT_EXCEEDS_ITEM",
112
+ "E_SETTLEMENT_DIFFERENCE_INVALID",
113
+ "E_OPENITEM_UNKNOWN",
114
+ "E_CASHBASIS_DEVIATING_FISCAL_YEAR",
115
+ "E_TAXCODE_UNKNOWN",
116
+ "E_TAXCODE_NO_VALID_VERSION",
117
+ "E_PROFILE_RETROACTIVE_CONFLICT",
118
+ "E_PROFILE_UNKNOWN",
119
+ "E_PARTNER_UNKNOWN",
120
+ "E_DIMENSION_INVALID",
121
+ "E_ASSET_UNKNOWN",
122
+ "E_ASSET_DISPOSED",
123
+ "E_COSTING_RUN_RELEASED",
124
+ "E_COSTING_RUN_UNKNOWN",
125
+ "E_COSTING_CYCLE",
126
+ "E_MAPPING_OVERLAP",
127
+ "E_NOT_IMPLEMENTED"
128
+ ];
129
+ function exitCodeFor(errorCode) {
130
+ const index = CODES.indexOf(errorCode);
131
+ return index === -1 ? 1 : index + 10;
132
+ }
133
+
134
+ // src/cli.ts
135
+ import { readFileSync as readFileSync2 } from "fs";
136
+ import { DomainError, TenantOperations } from "@superheld/summae-core";
137
+ import { Command } from "commander";
138
+ function parseJson(raw) {
139
+ const text = raw.startsWith("@") ? readFileSync2(raw.slice(1), "utf8") : raw;
140
+ const parsed = JSON.parse(text);
141
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
142
+ }
143
+ function emit(value) {
144
+ console.log(JSON.stringify(value));
145
+ }
146
+ function reportDomainError(error) {
147
+ if (!(error instanceof DomainError)) throw error;
148
+ emit({
149
+ error: error.errorCode,
150
+ message: error.message,
151
+ details: Object.keys(error.details).length === 0 ? {} : error.details
152
+ });
153
+ process.exitCode = exitCodeFor(error.errorCode);
154
+ }
155
+ function buildProgram() {
156
+ const program = new Command();
157
+ program.name("summae").description("summae \u2014 Buchf\xFChrung \xFCber JSON-Ein/Ausgabe").version("0.1.0");
158
+ program.command("init").description("Arbeitsbereich anlegen (summae.json + SQLite-Datenbank)").requiredOption("--name <name>", "Mandantenname").option("--currency <iso>", "Basisw\xE4hrung (ISO 4217)", "EUR").option("--rules <file>", "JSON-Datei mit Regelmodul-Daten").option("--dir <dir>", "Arbeitsverzeichnis", ".").action((opts) => {
159
+ const rules = typeof opts.rules === "string" ? parseJson(`@${opts.rules}`) : {};
160
+ const workspace = Workspace.in(opts.dir);
161
+ workspace.initialize(opts.name, opts.currency, rules);
162
+ const ops = new TenantOperations(workspace.tenant());
163
+ const created = { accounts: 0, fiscalYears: 0 };
164
+ for (const account of Array.isArray(rules.accounts) ? rules.accounts : []) {
165
+ if (account !== null && typeof account === "object") {
166
+ ops.execute("createAccount", account);
167
+ created.accounts++;
168
+ }
169
+ }
170
+ for (const fiscalYear of Array.isArray(rules.fiscalYears) ? rules.fiscalYears : []) {
171
+ if (fiscalYear !== null && typeof fiscalYear === "object") {
172
+ ops.execute("createFiscalYear", fiscalYear);
173
+ created.fiscalYears++;
174
+ }
175
+ }
176
+ emit({ initialized: true, tenant: opts.name, baseCurrency: opts.currency, created });
177
+ });
178
+ program.command("op").description("Schreiboperation ausf\xFChren (post, postVoucher, settle, \u2026)").argument("<operation>", "Operationsname laut api.md").option("--input <json>", "Eingabe als JSON oder @datei", "{}").option("--dir <dir>", "Arbeitsverzeichnis", ".").action((operation, opts) => {
179
+ try {
180
+ const payload = parseJson(opts.input);
181
+ emit(new TenantOperations(Workspace.in(opts.dir).tenant()).execute(operation, payload));
182
+ } catch (error) {
183
+ reportDomainError(error);
184
+ }
185
+ });
186
+ program.command("report").description("Projektion berechnen (trialBalance, cashBasisReport, vatReturn, \u2026)").argument("<projection>", "Projektionsname laut api.md").option("--params <json>", "Parameter als JSON oder @datei", "{}").option("--dir <dir>", "Arbeitsverzeichnis", ".").action((projection, opts) => {
187
+ try {
188
+ const params = parseJson(opts.params);
189
+ emit(new TenantOperations(Workspace.in(opts.dir).tenant()).project(projection, params));
190
+ } catch (error) {
191
+ reportDomainError(error);
192
+ }
193
+ });
194
+ return program;
195
+ }
196
+ function run(argv) {
197
+ buildProgram().parse(argv);
198
+ }
199
+
200
+ export {
201
+ Workspace,
202
+ exitCodeFor,
203
+ buildProgram,
204
+ run
205
+ };
206
+ //# sourceMappingURL=chunk-XAXHYNID.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/workspace.ts","../src/exit-codes.ts","../src/cli.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n Currency,\n DimensionRegistry,\n type DimensionRuleData,\n type DimensionTypeData,\n type DimensionValueData,\n MappingRegistry,\n SystemClock,\n TaxCodeRegistry,\n TaxProfile,\n type Tenant,\n Uuid,\n UuidV7IdGenerator,\n} from '@superheld/summae-core';\nimport { DatabaseTenantFactory, SyncDb, installSchema } from '@superheld/summae-knex';\n\nconst CONFIG_FILE = 'summae.json';\nconst DB_FILE = 'summae.sqlite';\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === 'object' && !Array.isArray(v);\n}\nfunction recordList(v: unknown): Record<string, unknown>[] {\n return Array.isArray(v) ? v.filter(isRecord) : [];\n}\n\n/**\n * CLI-Arbeitsbereich: `summae.json` (Mandanten-Meta + Regelmodul-Daten,\n * App-Schicht) + `summae.sqlite` (Persistenz via @superheld/summae-knex).\n * Jeder Aufruf lädt den Mandanten, führt aus, die Datenbank persistiert.\n * Pendant zu PHPs `Summae\\Cli\\Workspace`.\n */\nexport class Workspace {\n private constructor(private readonly directory: string) {}\n\n static in(directory: string): Workspace {\n return new Workspace(directory.replace(/\\/+$/, ''));\n }\n\n exists(): boolean {\n return existsSync(this.configPath());\n }\n\n /** @param ruleData accounts, taxCodes, taxProfile, dimensionTypes/-Values, ruleModules */\n initialize(name: string, currency: string, ruleData: Record<string, unknown>): void {\n if (this.exists()) {\n throw new Error(`Arbeitsbereich existiert bereits: ${this.configPath()}`);\n }\n const config = { name, baseCurrency: currency, tenantId: Uuid.v7().value, rules: ruleData };\n writeFileSync(this.configPath(), `${JSON.stringify(config, null, 2)}\\n`);\n\n const db = new SyncDb(this.dbPath());\n installSchema(db);\n db.close();\n }\n\n tenant(): Tenant {\n if (!this.exists()) {\n throw new Error(`Kein Arbeitsbereich in ${this.directory} — zuerst \\`summae init\\` ausführen`);\n }\n const config = JSON.parse(readFileSync(this.configPath(), 'utf8')) as Record<string, unknown>;\n const rules = isRecord(config.rules) ? config.rules : {};\n const ruleModules = isRecord(rules.ruleModules) ? rules.ruleModules : {};\n\n const dimensionTypes: DimensionTypeData[] = recordList(rules.dimensionTypes).map((t) => ({ code: String(t.code) }));\n const dimensionValues: DimensionValueData[] = recordList(rules.dimensionValues).map((v) => ({\n typeCode: String(v.typeCode),\n code: String(v.code),\n }));\n const dimensionRules: DimensionRuleData[] = recordList(ruleModules.dimensionRules).map((r) => {\n const range = isRecord(r.accountRange) ? r.accountRange : {};\n return { accountRange: { from: String(range.from), to: String(range.to) }, requiredDimension: String(r.requiredDimension) };\n });\n\n const clock = new SystemClock();\n const tenantId = typeof config.tenantId === 'string' ? Uuid.fromString(config.tenantId) : undefined;\n\n const tenant = DatabaseTenantFactory.build(\n new SyncDb(this.dbPath()),\n typeof config.name === 'string' ? config.name : 'CLI',\n Currency.of(typeof config.baseCurrency === 'string' ? config.baseCurrency : 'EUR'),\n clock,\n new UuidV7IdGenerator(clock),\n {\n dimensions: DimensionRegistry.fromData(dimensionTypes, dimensionValues, dimensionRules),\n taxCodes: TaxCodeRegistry.fromData(recordList(rules.taxCodes)),\n taxProfile: TaxProfile.fromData(isRecord(rules.taxProfile) ? rules.taxProfile : {}),\n mappings: MappingRegistry.fromRuleModules(Array.isArray(ruleModules.mappings) ? ruleModules.mappings : []),\n ...(tenantId ? { tenantId } : {}),\n },\n );\n tenant.assetService.setRuleModule(ruleModules);\n return tenant;\n }\n\n private configPath(): string {\n return join(this.directory, CONFIG_FILE);\n }\n private dbPath(): string {\n return join(this.directory, DB_FILE);\n }\n}\n","/**\n * Exit-Codes = Fehlercodes (api.md F-IO-003): stabile numerische Abbildung des\n * Fehlerkatalogs. 0 = Erfolg, 1 = unbekannter Fehler, sonst Index + 10.\n * **Reihenfolge ist append-only** und identisch zur PHP-Referenz (`ExitCodes.php`) —\n * Umsortieren wäre ein Breaking Change.\n */\nconst CODES: readonly string[] = [\n 'E_ENTRY_UNBALANCED',\n 'E_ENTRY_NO_VOUCHER',\n 'E_VOUCHER_UNKNOWN',\n 'E_ENTRY_TOO_FEW_LINES',\n 'E_ENTRY_INVALID_AMOUNT',\n 'E_ENTRY_FINALIZED',\n 'E_ENTRY_ALREADY_REVERSED',\n 'E_ENTRY_UNKNOWN',\n 'E_PERIOD_CLOSED',\n 'E_PERIOD_OUT_OF_ORDER',\n 'E_PERIOD_UNKNOWN',\n 'E_FISCALYEAR_CLOSED',\n 'E_FISCALYEAR_UNFINALIZED_ENTRIES',\n 'E_FISCALYEAR_OVERLAP',\n 'E_ACCOUNT_UNKNOWN',\n 'E_ACCOUNT_LOCKED',\n 'E_ACCOUNT_NUMBER_TAKEN',\n 'E_COA_FORMAT_INVALID',\n 'E_SETTLEMENT_EXCEEDS_ITEM',\n 'E_SETTLEMENT_DIFFERENCE_INVALID',\n 'E_OPENITEM_UNKNOWN',\n 'E_CASHBASIS_DEVIATING_FISCAL_YEAR',\n 'E_TAXCODE_UNKNOWN',\n 'E_TAXCODE_NO_VALID_VERSION',\n 'E_PROFILE_RETROACTIVE_CONFLICT',\n 'E_PROFILE_UNKNOWN',\n 'E_PARTNER_UNKNOWN',\n 'E_DIMENSION_INVALID',\n 'E_ASSET_UNKNOWN',\n 'E_ASSET_DISPOSED',\n 'E_COSTING_RUN_RELEASED',\n 'E_COSTING_RUN_UNKNOWN',\n 'E_COSTING_CYCLE',\n 'E_MAPPING_OVERLAP',\n 'E_NOT_IMPLEMENTED',\n];\n\nexport function exitCodeFor(errorCode: string): number {\n const index = CODES.indexOf(errorCode);\n return index === -1 ? 1 : index + 10;\n}\n","import { readFileSync } from 'node:fs';\nimport { DomainError, TenantOperations } from '@superheld/summae-core';\nimport { Command } from 'commander';\nimport { exitCodeFor } from './exit-codes.js';\nimport { Workspace } from './workspace.js';\n\nfunction parseJson(raw: string): Record<string, unknown> {\n const text = raw.startsWith('@') ? readFileSync(raw.slice(1), 'utf8') : raw;\n const parsed: unknown = JSON.parse(text);\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : {};\n}\n\nfunction emit(value: unknown): void {\n console.log(JSON.stringify(value));\n}\n\n/** DomainError → JSON-Fehler + Exit-Code; sonst neu werfen. */\nfunction reportDomainError(error: unknown): void {\n if (!(error instanceof DomainError)) throw error;\n emit({\n error: error.errorCode,\n message: error.message,\n details: Object.keys(error.details).length === 0 ? {} : error.details,\n });\n process.exitCode = exitCodeFor(error.errorCode);\n}\n\ninterface CommonOptions {\n dir: string;\n}\ninterface InitOptions extends CommonOptions {\n name: string;\n currency: string;\n rules?: string;\n}\n\n/** Baut die CLI (init/op/report) — Pendant zu PHPs Symfony-Console-App. */\nexport function buildProgram(): Command {\n const program = new Command();\n program.name('summae').description('summae — Buchführung über JSON-Ein/Ausgabe').version('0.1.0');\n\n program\n .command('init')\n .description('Arbeitsbereich anlegen (summae.json + SQLite-Datenbank)')\n .requiredOption('--name <name>', 'Mandantenname')\n .option('--currency <iso>', 'Basiswährung (ISO 4217)', 'EUR')\n .option('--rules <file>', 'JSON-Datei mit Regelmodul-Daten')\n .option('--dir <dir>', 'Arbeitsverzeichnis', '.')\n .action((opts: InitOptions) => {\n const rules = typeof opts.rules === 'string' ? parseJson(`@${opts.rules}`) : {};\n const workspace = Workspace.in(opts.dir);\n workspace.initialize(opts.name, opts.currency, rules);\n\n // SF-01: Stammdaten aus der Regeldatei direkt anlegen — sofort buchbar.\n const ops = new TenantOperations(workspace.tenant());\n const created = { accounts: 0, fiscalYears: 0 };\n for (const account of Array.isArray(rules.accounts) ? rules.accounts : []) {\n if (account !== null && typeof account === 'object') {\n ops.execute('createAccount', account as Record<string, unknown>);\n created.accounts++;\n }\n }\n for (const fiscalYear of Array.isArray(rules.fiscalYears) ? rules.fiscalYears : []) {\n if (fiscalYear !== null && typeof fiscalYear === 'object') {\n ops.execute('createFiscalYear', fiscalYear as Record<string, unknown>);\n created.fiscalYears++;\n }\n }\n emit({ initialized: true, tenant: opts.name, baseCurrency: opts.currency, created });\n });\n\n program\n .command('op')\n .description('Schreiboperation ausführen (post, postVoucher, settle, …)')\n .argument('<operation>', 'Operationsname laut api.md')\n .option('--input <json>', 'Eingabe als JSON oder @datei', '{}')\n .option('--dir <dir>', 'Arbeitsverzeichnis', '.')\n .action((operation: string, opts: CommonOptions & { input: string }) => {\n try {\n const payload = parseJson(opts.input);\n emit(new TenantOperations(Workspace.in(opts.dir).tenant()).execute(operation, payload));\n } catch (error) {\n reportDomainError(error);\n }\n });\n\n program\n .command('report')\n .description('Projektion berechnen (trialBalance, cashBasisReport, vatReturn, …)')\n .argument('<projection>', 'Projektionsname laut api.md')\n .option('--params <json>', 'Parameter als JSON oder @datei', '{}')\n .option('--dir <dir>', 'Arbeitsverzeichnis', '.')\n .action((projection: string, opts: CommonOptions & { params: string }) => {\n try {\n const params = parseJson(opts.params);\n emit(new TenantOperations(Workspace.in(opts.dir).tenant()).project(projection, params));\n } catch (error) {\n reportDomainError(error);\n }\n });\n\n return program;\n}\n\nexport function run(argv: string[]): void {\n buildProgram().parse(argv);\n}\n"],"mappings":"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","names":["readFileSync","readFileSync"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ Workspace: () => Workspace,
25
+ buildProgram: () => buildProgram,
26
+ exitCodeFor: () => exitCodeFor,
27
+ run: () => run
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/workspace.ts
32
+ var import_node_fs = require("fs");
33
+ var import_node_path = require("path");
34
+ var import_summae_core = require("@superheld/summae-core");
35
+ var import_summae_knex = require("@superheld/summae-knex");
36
+ var CONFIG_FILE = "summae.json";
37
+ var DB_FILE = "summae.sqlite";
38
+ function isRecord(v) {
39
+ return v !== null && typeof v === "object" && !Array.isArray(v);
40
+ }
41
+ function recordList(v) {
42
+ return Array.isArray(v) ? v.filter(isRecord) : [];
43
+ }
44
+ var Workspace = class _Workspace {
45
+ constructor(directory) {
46
+ this.directory = directory;
47
+ }
48
+ directory;
49
+ static in(directory) {
50
+ return new _Workspace(directory.replace(/\/+$/, ""));
51
+ }
52
+ exists() {
53
+ return (0, import_node_fs.existsSync)(this.configPath());
54
+ }
55
+ /** @param ruleData accounts, taxCodes, taxProfile, dimensionTypes/-Values, ruleModules */
56
+ initialize(name, currency, ruleData) {
57
+ if (this.exists()) {
58
+ throw new Error(`Arbeitsbereich existiert bereits: ${this.configPath()}`);
59
+ }
60
+ const config = { name, baseCurrency: currency, tenantId: import_summae_core.Uuid.v7().value, rules: ruleData };
61
+ (0, import_node_fs.writeFileSync)(this.configPath(), `${JSON.stringify(config, null, 2)}
62
+ `);
63
+ const db = new import_summae_knex.SyncDb(this.dbPath());
64
+ (0, import_summae_knex.installSchema)(db);
65
+ db.close();
66
+ }
67
+ tenant() {
68
+ if (!this.exists()) {
69
+ throw new Error(`Kein Arbeitsbereich in ${this.directory} \u2014 zuerst \`summae init\` ausf\xFChren`);
70
+ }
71
+ const config = JSON.parse((0, import_node_fs.readFileSync)(this.configPath(), "utf8"));
72
+ const rules = isRecord(config.rules) ? config.rules : {};
73
+ const ruleModules = isRecord(rules.ruleModules) ? rules.ruleModules : {};
74
+ const dimensionTypes = recordList(rules.dimensionTypes).map((t) => ({ code: String(t.code) }));
75
+ const dimensionValues = recordList(rules.dimensionValues).map((v) => ({
76
+ typeCode: String(v.typeCode),
77
+ code: String(v.code)
78
+ }));
79
+ const dimensionRules = recordList(ruleModules.dimensionRules).map((r) => {
80
+ const range = isRecord(r.accountRange) ? r.accountRange : {};
81
+ return { accountRange: { from: String(range.from), to: String(range.to) }, requiredDimension: String(r.requiredDimension) };
82
+ });
83
+ const clock = new import_summae_core.SystemClock();
84
+ const tenantId = typeof config.tenantId === "string" ? import_summae_core.Uuid.fromString(config.tenantId) : void 0;
85
+ const tenant = import_summae_knex.DatabaseTenantFactory.build(
86
+ new import_summae_knex.SyncDb(this.dbPath()),
87
+ typeof config.name === "string" ? config.name : "CLI",
88
+ import_summae_core.Currency.of(typeof config.baseCurrency === "string" ? config.baseCurrency : "EUR"),
89
+ clock,
90
+ new import_summae_core.UuidV7IdGenerator(clock),
91
+ {
92
+ dimensions: import_summae_core.DimensionRegistry.fromData(dimensionTypes, dimensionValues, dimensionRules),
93
+ taxCodes: import_summae_core.TaxCodeRegistry.fromData(recordList(rules.taxCodes)),
94
+ taxProfile: import_summae_core.TaxProfile.fromData(isRecord(rules.taxProfile) ? rules.taxProfile : {}),
95
+ mappings: import_summae_core.MappingRegistry.fromRuleModules(Array.isArray(ruleModules.mappings) ? ruleModules.mappings : []),
96
+ ...tenantId ? { tenantId } : {}
97
+ }
98
+ );
99
+ tenant.assetService.setRuleModule(ruleModules);
100
+ return tenant;
101
+ }
102
+ configPath() {
103
+ return (0, import_node_path.join)(this.directory, CONFIG_FILE);
104
+ }
105
+ dbPath() {
106
+ return (0, import_node_path.join)(this.directory, DB_FILE);
107
+ }
108
+ };
109
+
110
+ // src/cli.ts
111
+ var import_node_fs2 = require("fs");
112
+ var import_summae_core2 = require("@superheld/summae-core");
113
+ var import_commander = require("commander");
114
+
115
+ // src/exit-codes.ts
116
+ var CODES = [
117
+ "E_ENTRY_UNBALANCED",
118
+ "E_ENTRY_NO_VOUCHER",
119
+ "E_VOUCHER_UNKNOWN",
120
+ "E_ENTRY_TOO_FEW_LINES",
121
+ "E_ENTRY_INVALID_AMOUNT",
122
+ "E_ENTRY_FINALIZED",
123
+ "E_ENTRY_ALREADY_REVERSED",
124
+ "E_ENTRY_UNKNOWN",
125
+ "E_PERIOD_CLOSED",
126
+ "E_PERIOD_OUT_OF_ORDER",
127
+ "E_PERIOD_UNKNOWN",
128
+ "E_FISCALYEAR_CLOSED",
129
+ "E_FISCALYEAR_UNFINALIZED_ENTRIES",
130
+ "E_FISCALYEAR_OVERLAP",
131
+ "E_ACCOUNT_UNKNOWN",
132
+ "E_ACCOUNT_LOCKED",
133
+ "E_ACCOUNT_NUMBER_TAKEN",
134
+ "E_COA_FORMAT_INVALID",
135
+ "E_SETTLEMENT_EXCEEDS_ITEM",
136
+ "E_SETTLEMENT_DIFFERENCE_INVALID",
137
+ "E_OPENITEM_UNKNOWN",
138
+ "E_CASHBASIS_DEVIATING_FISCAL_YEAR",
139
+ "E_TAXCODE_UNKNOWN",
140
+ "E_TAXCODE_NO_VALID_VERSION",
141
+ "E_PROFILE_RETROACTIVE_CONFLICT",
142
+ "E_PROFILE_UNKNOWN",
143
+ "E_PARTNER_UNKNOWN",
144
+ "E_DIMENSION_INVALID",
145
+ "E_ASSET_UNKNOWN",
146
+ "E_ASSET_DISPOSED",
147
+ "E_COSTING_RUN_RELEASED",
148
+ "E_COSTING_RUN_UNKNOWN",
149
+ "E_COSTING_CYCLE",
150
+ "E_MAPPING_OVERLAP",
151
+ "E_NOT_IMPLEMENTED"
152
+ ];
153
+ function exitCodeFor(errorCode) {
154
+ const index = CODES.indexOf(errorCode);
155
+ return index === -1 ? 1 : index + 10;
156
+ }
157
+
158
+ // src/cli.ts
159
+ function parseJson(raw) {
160
+ const text = raw.startsWith("@") ? (0, import_node_fs2.readFileSync)(raw.slice(1), "utf8") : raw;
161
+ const parsed = JSON.parse(text);
162
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
163
+ }
164
+ function emit(value) {
165
+ console.log(JSON.stringify(value));
166
+ }
167
+ function reportDomainError(error) {
168
+ if (!(error instanceof import_summae_core2.DomainError)) throw error;
169
+ emit({
170
+ error: error.errorCode,
171
+ message: error.message,
172
+ details: Object.keys(error.details).length === 0 ? {} : error.details
173
+ });
174
+ process.exitCode = exitCodeFor(error.errorCode);
175
+ }
176
+ function buildProgram() {
177
+ const program = new import_commander.Command();
178
+ program.name("summae").description("summae \u2014 Buchf\xFChrung \xFCber JSON-Ein/Ausgabe").version("0.1.0");
179
+ program.command("init").description("Arbeitsbereich anlegen (summae.json + SQLite-Datenbank)").requiredOption("--name <name>", "Mandantenname").option("--currency <iso>", "Basisw\xE4hrung (ISO 4217)", "EUR").option("--rules <file>", "JSON-Datei mit Regelmodul-Daten").option("--dir <dir>", "Arbeitsverzeichnis", ".").action((opts) => {
180
+ const rules = typeof opts.rules === "string" ? parseJson(`@${opts.rules}`) : {};
181
+ const workspace = Workspace.in(opts.dir);
182
+ workspace.initialize(opts.name, opts.currency, rules);
183
+ const ops = new import_summae_core2.TenantOperations(workspace.tenant());
184
+ const created = { accounts: 0, fiscalYears: 0 };
185
+ for (const account of Array.isArray(rules.accounts) ? rules.accounts : []) {
186
+ if (account !== null && typeof account === "object") {
187
+ ops.execute("createAccount", account);
188
+ created.accounts++;
189
+ }
190
+ }
191
+ for (const fiscalYear of Array.isArray(rules.fiscalYears) ? rules.fiscalYears : []) {
192
+ if (fiscalYear !== null && typeof fiscalYear === "object") {
193
+ ops.execute("createFiscalYear", fiscalYear);
194
+ created.fiscalYears++;
195
+ }
196
+ }
197
+ emit({ initialized: true, tenant: opts.name, baseCurrency: opts.currency, created });
198
+ });
199
+ program.command("op").description("Schreiboperation ausf\xFChren (post, postVoucher, settle, \u2026)").argument("<operation>", "Operationsname laut api.md").option("--input <json>", "Eingabe als JSON oder @datei", "{}").option("--dir <dir>", "Arbeitsverzeichnis", ".").action((operation, opts) => {
200
+ try {
201
+ const payload = parseJson(opts.input);
202
+ emit(new import_summae_core2.TenantOperations(Workspace.in(opts.dir).tenant()).execute(operation, payload));
203
+ } catch (error) {
204
+ reportDomainError(error);
205
+ }
206
+ });
207
+ program.command("report").description("Projektion berechnen (trialBalance, cashBasisReport, vatReturn, \u2026)").argument("<projection>", "Projektionsname laut api.md").option("--params <json>", "Parameter als JSON oder @datei", "{}").option("--dir <dir>", "Arbeitsverzeichnis", ".").action((projection, opts) => {
208
+ try {
209
+ const params = parseJson(opts.params);
210
+ emit(new import_summae_core2.TenantOperations(Workspace.in(opts.dir).tenant()).project(projection, params));
211
+ } catch (error) {
212
+ reportDomainError(error);
213
+ }
214
+ });
215
+ return program;
216
+ }
217
+ function run(argv) {
218
+ buildProgram().parse(argv);
219
+ }
220
+ // Annotate the CommonJS export names for ESM import in node:
221
+ 0 && (module.exports = {
222
+ Workspace,
223
+ buildProgram,
224
+ exitCodeFor,
225
+ run
226
+ });
227
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/workspace.ts","../src/cli.ts","../src/exit-codes.ts"],"sourcesContent":["export { Workspace } from './workspace.js';\nexport { buildProgram, run } from './cli.js';\nexport { exitCodeFor } from './exit-codes.js';\n","import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n Currency,\n DimensionRegistry,\n type DimensionRuleData,\n type DimensionTypeData,\n type DimensionValueData,\n MappingRegistry,\n SystemClock,\n TaxCodeRegistry,\n TaxProfile,\n type Tenant,\n Uuid,\n UuidV7IdGenerator,\n} from '@superheld/summae-core';\nimport { DatabaseTenantFactory, SyncDb, installSchema } from '@superheld/summae-knex';\n\nconst CONFIG_FILE = 'summae.json';\nconst DB_FILE = 'summae.sqlite';\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === 'object' && !Array.isArray(v);\n}\nfunction recordList(v: unknown): Record<string, unknown>[] {\n return Array.isArray(v) ? v.filter(isRecord) : [];\n}\n\n/**\n * CLI-Arbeitsbereich: `summae.json` (Mandanten-Meta + Regelmodul-Daten,\n * App-Schicht) + `summae.sqlite` (Persistenz via @superheld/summae-knex).\n * Jeder Aufruf lädt den Mandanten, führt aus, die Datenbank persistiert.\n * Pendant zu PHPs `Summae\\Cli\\Workspace`.\n */\nexport class Workspace {\n private constructor(private readonly directory: string) {}\n\n static in(directory: string): Workspace {\n return new Workspace(directory.replace(/\\/+$/, ''));\n }\n\n exists(): boolean {\n return existsSync(this.configPath());\n }\n\n /** @param ruleData accounts, taxCodes, taxProfile, dimensionTypes/-Values, ruleModules */\n initialize(name: string, currency: string, ruleData: Record<string, unknown>): void {\n if (this.exists()) {\n throw new Error(`Arbeitsbereich existiert bereits: ${this.configPath()}`);\n }\n const config = { name, baseCurrency: currency, tenantId: Uuid.v7().value, rules: ruleData };\n writeFileSync(this.configPath(), `${JSON.stringify(config, null, 2)}\\n`);\n\n const db = new SyncDb(this.dbPath());\n installSchema(db);\n db.close();\n }\n\n tenant(): Tenant {\n if (!this.exists()) {\n throw new Error(`Kein Arbeitsbereich in ${this.directory} — zuerst \\`summae init\\` ausführen`);\n }\n const config = JSON.parse(readFileSync(this.configPath(), 'utf8')) as Record<string, unknown>;\n const rules = isRecord(config.rules) ? config.rules : {};\n const ruleModules = isRecord(rules.ruleModules) ? rules.ruleModules : {};\n\n const dimensionTypes: DimensionTypeData[] = recordList(rules.dimensionTypes).map((t) => ({ code: String(t.code) }));\n const dimensionValues: DimensionValueData[] = recordList(rules.dimensionValues).map((v) => ({\n typeCode: String(v.typeCode),\n code: String(v.code),\n }));\n const dimensionRules: DimensionRuleData[] = recordList(ruleModules.dimensionRules).map((r) => {\n const range = isRecord(r.accountRange) ? r.accountRange : {};\n return { accountRange: { from: String(range.from), to: String(range.to) }, requiredDimension: String(r.requiredDimension) };\n });\n\n const clock = new SystemClock();\n const tenantId = typeof config.tenantId === 'string' ? Uuid.fromString(config.tenantId) : undefined;\n\n const tenant = DatabaseTenantFactory.build(\n new SyncDb(this.dbPath()),\n typeof config.name === 'string' ? config.name : 'CLI',\n Currency.of(typeof config.baseCurrency === 'string' ? config.baseCurrency : 'EUR'),\n clock,\n new UuidV7IdGenerator(clock),\n {\n dimensions: DimensionRegistry.fromData(dimensionTypes, dimensionValues, dimensionRules),\n taxCodes: TaxCodeRegistry.fromData(recordList(rules.taxCodes)),\n taxProfile: TaxProfile.fromData(isRecord(rules.taxProfile) ? rules.taxProfile : {}),\n mappings: MappingRegistry.fromRuleModules(Array.isArray(ruleModules.mappings) ? ruleModules.mappings : []),\n ...(tenantId ? { tenantId } : {}),\n },\n );\n tenant.assetService.setRuleModule(ruleModules);\n return tenant;\n }\n\n private configPath(): string {\n return join(this.directory, CONFIG_FILE);\n }\n private dbPath(): string {\n return join(this.directory, DB_FILE);\n }\n}\n","import { readFileSync } from 'node:fs';\nimport { DomainError, TenantOperations } from '@superheld/summae-core';\nimport { Command } from 'commander';\nimport { exitCodeFor } from './exit-codes.js';\nimport { Workspace } from './workspace.js';\n\nfunction parseJson(raw: string): Record<string, unknown> {\n const text = raw.startsWith('@') ? readFileSync(raw.slice(1), 'utf8') : raw;\n const parsed: unknown = JSON.parse(text);\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : {};\n}\n\nfunction emit(value: unknown): void {\n console.log(JSON.stringify(value));\n}\n\n/** DomainError → JSON-Fehler + Exit-Code; sonst neu werfen. */\nfunction reportDomainError(error: unknown): void {\n if (!(error instanceof DomainError)) throw error;\n emit({\n error: error.errorCode,\n message: error.message,\n details: Object.keys(error.details).length === 0 ? {} : error.details,\n });\n process.exitCode = exitCodeFor(error.errorCode);\n}\n\ninterface CommonOptions {\n dir: string;\n}\ninterface InitOptions extends CommonOptions {\n name: string;\n currency: string;\n rules?: string;\n}\n\n/** Baut die CLI (init/op/report) — Pendant zu PHPs Symfony-Console-App. */\nexport function buildProgram(): Command {\n const program = new Command();\n program.name('summae').description('summae — Buchführung über JSON-Ein/Ausgabe').version('0.1.0');\n\n program\n .command('init')\n .description('Arbeitsbereich anlegen (summae.json + SQLite-Datenbank)')\n .requiredOption('--name <name>', 'Mandantenname')\n .option('--currency <iso>', 'Basiswährung (ISO 4217)', 'EUR')\n .option('--rules <file>', 'JSON-Datei mit Regelmodul-Daten')\n .option('--dir <dir>', 'Arbeitsverzeichnis', '.')\n .action((opts: InitOptions) => {\n const rules = typeof opts.rules === 'string' ? parseJson(`@${opts.rules}`) : {};\n const workspace = Workspace.in(opts.dir);\n workspace.initialize(opts.name, opts.currency, rules);\n\n // SF-01: Stammdaten aus der Regeldatei direkt anlegen — sofort buchbar.\n const ops = new TenantOperations(workspace.tenant());\n const created = { accounts: 0, fiscalYears: 0 };\n for (const account of Array.isArray(rules.accounts) ? rules.accounts : []) {\n if (account !== null && typeof account === 'object') {\n ops.execute('createAccount', account as Record<string, unknown>);\n created.accounts++;\n }\n }\n for (const fiscalYear of Array.isArray(rules.fiscalYears) ? rules.fiscalYears : []) {\n if (fiscalYear !== null && typeof fiscalYear === 'object') {\n ops.execute('createFiscalYear', fiscalYear as Record<string, unknown>);\n created.fiscalYears++;\n }\n }\n emit({ initialized: true, tenant: opts.name, baseCurrency: opts.currency, created });\n });\n\n program\n .command('op')\n .description('Schreiboperation ausführen (post, postVoucher, settle, …)')\n .argument('<operation>', 'Operationsname laut api.md')\n .option('--input <json>', 'Eingabe als JSON oder @datei', '{}')\n .option('--dir <dir>', 'Arbeitsverzeichnis', '.')\n .action((operation: string, opts: CommonOptions & { input: string }) => {\n try {\n const payload = parseJson(opts.input);\n emit(new TenantOperations(Workspace.in(opts.dir).tenant()).execute(operation, payload));\n } catch (error) {\n reportDomainError(error);\n }\n });\n\n program\n .command('report')\n .description('Projektion berechnen (trialBalance, cashBasisReport, vatReturn, …)')\n .argument('<projection>', 'Projektionsname laut api.md')\n .option('--params <json>', 'Parameter als JSON oder @datei', '{}')\n .option('--dir <dir>', 'Arbeitsverzeichnis', '.')\n .action((projection: string, opts: CommonOptions & { params: string }) => {\n try {\n const params = parseJson(opts.params);\n emit(new TenantOperations(Workspace.in(opts.dir).tenant()).project(projection, params));\n } catch (error) {\n reportDomainError(error);\n }\n });\n\n return program;\n}\n\nexport function run(argv: string[]): void {\n buildProgram().parse(argv);\n}\n","/**\n * Exit-Codes = Fehlercodes (api.md F-IO-003): stabile numerische Abbildung des\n * Fehlerkatalogs. 0 = Erfolg, 1 = unbekannter Fehler, sonst Index + 10.\n * **Reihenfolge ist append-only** und identisch zur PHP-Referenz (`ExitCodes.php`) —\n * Umsortieren wäre ein Breaking Change.\n */\nconst CODES: readonly string[] = [\n 'E_ENTRY_UNBALANCED',\n 'E_ENTRY_NO_VOUCHER',\n 'E_VOUCHER_UNKNOWN',\n 'E_ENTRY_TOO_FEW_LINES',\n 'E_ENTRY_INVALID_AMOUNT',\n 'E_ENTRY_FINALIZED',\n 'E_ENTRY_ALREADY_REVERSED',\n 'E_ENTRY_UNKNOWN',\n 'E_PERIOD_CLOSED',\n 'E_PERIOD_OUT_OF_ORDER',\n 'E_PERIOD_UNKNOWN',\n 'E_FISCALYEAR_CLOSED',\n 'E_FISCALYEAR_UNFINALIZED_ENTRIES',\n 'E_FISCALYEAR_OVERLAP',\n 'E_ACCOUNT_UNKNOWN',\n 'E_ACCOUNT_LOCKED',\n 'E_ACCOUNT_NUMBER_TAKEN',\n 'E_COA_FORMAT_INVALID',\n 'E_SETTLEMENT_EXCEEDS_ITEM',\n 'E_SETTLEMENT_DIFFERENCE_INVALID',\n 'E_OPENITEM_UNKNOWN',\n 'E_CASHBASIS_DEVIATING_FISCAL_YEAR',\n 'E_TAXCODE_UNKNOWN',\n 'E_TAXCODE_NO_VALID_VERSION',\n 'E_PROFILE_RETROACTIVE_CONFLICT',\n 'E_PROFILE_UNKNOWN',\n 'E_PARTNER_UNKNOWN',\n 'E_DIMENSION_INVALID',\n 'E_ASSET_UNKNOWN',\n 'E_ASSET_DISPOSED',\n 'E_COSTING_RUN_RELEASED',\n 'E_COSTING_RUN_UNKNOWN',\n 'E_COSTING_CYCLE',\n 'E_MAPPING_OVERLAP',\n 'E_NOT_IMPLEMENTED',\n];\n\nexport function exitCodeFor(errorCode: string): number {\n const index = CODES.indexOf(errorCode);\n return index === -1 ? 1 : index + 10;\n}\n"],"mappings":"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","names":["import_node_fs","import_summae_core"]}
@@ -0,0 +1,28 @@
1
+ import { Tenant } from '@superheld/summae-core';
2
+ import { Command } from 'commander';
3
+
4
+ /**
5
+ * CLI-Arbeitsbereich: `summae.json` (Mandanten-Meta + Regelmodul-Daten,
6
+ * App-Schicht) + `summae.sqlite` (Persistenz via @superheld/summae-knex).
7
+ * Jeder Aufruf lädt den Mandanten, führt aus, die Datenbank persistiert.
8
+ * Pendant zu PHPs `Summae\Cli\Workspace`.
9
+ */
10
+ declare class Workspace {
11
+ private readonly directory;
12
+ private constructor();
13
+ static in(directory: string): Workspace;
14
+ exists(): boolean;
15
+ /** @param ruleData accounts, taxCodes, taxProfile, dimensionTypes/-Values, ruleModules */
16
+ initialize(name: string, currency: string, ruleData: Record<string, unknown>): void;
17
+ tenant(): Tenant;
18
+ private configPath;
19
+ private dbPath;
20
+ }
21
+
22
+ /** Baut die CLI (init/op/report) — Pendant zu PHPs Symfony-Console-App. */
23
+ declare function buildProgram(): Command;
24
+ declare function run(argv: string[]): void;
25
+
26
+ declare function exitCodeFor(errorCode: string): number;
27
+
28
+ export { Workspace, buildProgram, exitCodeFor, run };
@@ -0,0 +1,28 @@
1
+ import { Tenant } from '@superheld/summae-core';
2
+ import { Command } from 'commander';
3
+
4
+ /**
5
+ * CLI-Arbeitsbereich: `summae.json` (Mandanten-Meta + Regelmodul-Daten,
6
+ * App-Schicht) + `summae.sqlite` (Persistenz via @superheld/summae-knex).
7
+ * Jeder Aufruf lädt den Mandanten, führt aus, die Datenbank persistiert.
8
+ * Pendant zu PHPs `Summae\Cli\Workspace`.
9
+ */
10
+ declare class Workspace {
11
+ private readonly directory;
12
+ private constructor();
13
+ static in(directory: string): Workspace;
14
+ exists(): boolean;
15
+ /** @param ruleData accounts, taxCodes, taxProfile, dimensionTypes/-Values, ruleModules */
16
+ initialize(name: string, currency: string, ruleData: Record<string, unknown>): void;
17
+ tenant(): Tenant;
18
+ private configPath;
19
+ private dbPath;
20
+ }
21
+
22
+ /** Baut die CLI (init/op/report) — Pendant zu PHPs Symfony-Console-App. */
23
+ declare function buildProgram(): Command;
24
+ declare function run(argv: string[]): void;
25
+
26
+ declare function exitCodeFor(errorCode: string): number;
27
+
28
+ export { Workspace, buildProgram, exitCodeFor, run };
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ Workspace,
4
+ buildProgram,
5
+ exitCodeFor,
6
+ run
7
+ } from "./chunk-XAXHYNID.js";
8
+ export {
9
+ Workspace,
10
+ buildProgram,
11
+ exitCodeFor,
12
+ run
13
+ };
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/cli.ts
5
+ var import_node_fs2 = require("fs");
6
+ var import_summae_core2 = require("@superheld/summae-core");
7
+ var import_commander = require("commander");
8
+
9
+ // src/exit-codes.ts
10
+ var CODES = [
11
+ "E_ENTRY_UNBALANCED",
12
+ "E_ENTRY_NO_VOUCHER",
13
+ "E_VOUCHER_UNKNOWN",
14
+ "E_ENTRY_TOO_FEW_LINES",
15
+ "E_ENTRY_INVALID_AMOUNT",
16
+ "E_ENTRY_FINALIZED",
17
+ "E_ENTRY_ALREADY_REVERSED",
18
+ "E_ENTRY_UNKNOWN",
19
+ "E_PERIOD_CLOSED",
20
+ "E_PERIOD_OUT_OF_ORDER",
21
+ "E_PERIOD_UNKNOWN",
22
+ "E_FISCALYEAR_CLOSED",
23
+ "E_FISCALYEAR_UNFINALIZED_ENTRIES",
24
+ "E_FISCALYEAR_OVERLAP",
25
+ "E_ACCOUNT_UNKNOWN",
26
+ "E_ACCOUNT_LOCKED",
27
+ "E_ACCOUNT_NUMBER_TAKEN",
28
+ "E_COA_FORMAT_INVALID",
29
+ "E_SETTLEMENT_EXCEEDS_ITEM",
30
+ "E_SETTLEMENT_DIFFERENCE_INVALID",
31
+ "E_OPENITEM_UNKNOWN",
32
+ "E_CASHBASIS_DEVIATING_FISCAL_YEAR",
33
+ "E_TAXCODE_UNKNOWN",
34
+ "E_TAXCODE_NO_VALID_VERSION",
35
+ "E_PROFILE_RETROACTIVE_CONFLICT",
36
+ "E_PROFILE_UNKNOWN",
37
+ "E_PARTNER_UNKNOWN",
38
+ "E_DIMENSION_INVALID",
39
+ "E_ASSET_UNKNOWN",
40
+ "E_ASSET_DISPOSED",
41
+ "E_COSTING_RUN_RELEASED",
42
+ "E_COSTING_RUN_UNKNOWN",
43
+ "E_COSTING_CYCLE",
44
+ "E_MAPPING_OVERLAP",
45
+ "E_NOT_IMPLEMENTED"
46
+ ];
47
+ function exitCodeFor(errorCode) {
48
+ const index = CODES.indexOf(errorCode);
49
+ return index === -1 ? 1 : index + 10;
50
+ }
51
+
52
+ // src/workspace.ts
53
+ var import_node_fs = require("fs");
54
+ var import_node_path = require("path");
55
+ var import_summae_core = require("@superheld/summae-core");
56
+ var import_summae_knex = require("@superheld/summae-knex");
57
+ var CONFIG_FILE = "summae.json";
58
+ var DB_FILE = "summae.sqlite";
59
+ function isRecord(v) {
60
+ return v !== null && typeof v === "object" && !Array.isArray(v);
61
+ }
62
+ function recordList(v) {
63
+ return Array.isArray(v) ? v.filter(isRecord) : [];
64
+ }
65
+ var Workspace = class _Workspace {
66
+ constructor(directory) {
67
+ this.directory = directory;
68
+ }
69
+ directory;
70
+ static in(directory) {
71
+ return new _Workspace(directory.replace(/\/+$/, ""));
72
+ }
73
+ exists() {
74
+ return (0, import_node_fs.existsSync)(this.configPath());
75
+ }
76
+ /** @param ruleData accounts, taxCodes, taxProfile, dimensionTypes/-Values, ruleModules */
77
+ initialize(name, currency, ruleData) {
78
+ if (this.exists()) {
79
+ throw new Error(`Arbeitsbereich existiert bereits: ${this.configPath()}`);
80
+ }
81
+ const config = { name, baseCurrency: currency, tenantId: import_summae_core.Uuid.v7().value, rules: ruleData };
82
+ (0, import_node_fs.writeFileSync)(this.configPath(), `${JSON.stringify(config, null, 2)}
83
+ `);
84
+ const db = new import_summae_knex.SyncDb(this.dbPath());
85
+ (0, import_summae_knex.installSchema)(db);
86
+ db.close();
87
+ }
88
+ tenant() {
89
+ if (!this.exists()) {
90
+ throw new Error(`Kein Arbeitsbereich in ${this.directory} \u2014 zuerst \`summae init\` ausf\xFChren`);
91
+ }
92
+ const config = JSON.parse((0, import_node_fs.readFileSync)(this.configPath(), "utf8"));
93
+ const rules = isRecord(config.rules) ? config.rules : {};
94
+ const ruleModules = isRecord(rules.ruleModules) ? rules.ruleModules : {};
95
+ const dimensionTypes = recordList(rules.dimensionTypes).map((t) => ({ code: String(t.code) }));
96
+ const dimensionValues = recordList(rules.dimensionValues).map((v) => ({
97
+ typeCode: String(v.typeCode),
98
+ code: String(v.code)
99
+ }));
100
+ const dimensionRules = recordList(ruleModules.dimensionRules).map((r) => {
101
+ const range = isRecord(r.accountRange) ? r.accountRange : {};
102
+ return { accountRange: { from: String(range.from), to: String(range.to) }, requiredDimension: String(r.requiredDimension) };
103
+ });
104
+ const clock = new import_summae_core.SystemClock();
105
+ const tenantId = typeof config.tenantId === "string" ? import_summae_core.Uuid.fromString(config.tenantId) : void 0;
106
+ const tenant = import_summae_knex.DatabaseTenantFactory.build(
107
+ new import_summae_knex.SyncDb(this.dbPath()),
108
+ typeof config.name === "string" ? config.name : "CLI",
109
+ import_summae_core.Currency.of(typeof config.baseCurrency === "string" ? config.baseCurrency : "EUR"),
110
+ clock,
111
+ new import_summae_core.UuidV7IdGenerator(clock),
112
+ {
113
+ dimensions: import_summae_core.DimensionRegistry.fromData(dimensionTypes, dimensionValues, dimensionRules),
114
+ taxCodes: import_summae_core.TaxCodeRegistry.fromData(recordList(rules.taxCodes)),
115
+ taxProfile: import_summae_core.TaxProfile.fromData(isRecord(rules.taxProfile) ? rules.taxProfile : {}),
116
+ mappings: import_summae_core.MappingRegistry.fromRuleModules(Array.isArray(ruleModules.mappings) ? ruleModules.mappings : []),
117
+ ...tenantId ? { tenantId } : {}
118
+ }
119
+ );
120
+ tenant.assetService.setRuleModule(ruleModules);
121
+ return tenant;
122
+ }
123
+ configPath() {
124
+ return (0, import_node_path.join)(this.directory, CONFIG_FILE);
125
+ }
126
+ dbPath() {
127
+ return (0, import_node_path.join)(this.directory, DB_FILE);
128
+ }
129
+ };
130
+
131
+ // src/cli.ts
132
+ function parseJson(raw) {
133
+ const text = raw.startsWith("@") ? (0, import_node_fs2.readFileSync)(raw.slice(1), "utf8") : raw;
134
+ const parsed = JSON.parse(text);
135
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
136
+ }
137
+ function emit(value) {
138
+ console.log(JSON.stringify(value));
139
+ }
140
+ function reportDomainError(error) {
141
+ if (!(error instanceof import_summae_core2.DomainError)) throw error;
142
+ emit({
143
+ error: error.errorCode,
144
+ message: error.message,
145
+ details: Object.keys(error.details).length === 0 ? {} : error.details
146
+ });
147
+ process.exitCode = exitCodeFor(error.errorCode);
148
+ }
149
+ function buildProgram() {
150
+ const program = new import_commander.Command();
151
+ program.name("summae").description("summae \u2014 Buchf\xFChrung \xFCber JSON-Ein/Ausgabe").version("0.1.0");
152
+ program.command("init").description("Arbeitsbereich anlegen (summae.json + SQLite-Datenbank)").requiredOption("--name <name>", "Mandantenname").option("--currency <iso>", "Basisw\xE4hrung (ISO 4217)", "EUR").option("--rules <file>", "JSON-Datei mit Regelmodul-Daten").option("--dir <dir>", "Arbeitsverzeichnis", ".").action((opts) => {
153
+ const rules = typeof opts.rules === "string" ? parseJson(`@${opts.rules}`) : {};
154
+ const workspace = Workspace.in(opts.dir);
155
+ workspace.initialize(opts.name, opts.currency, rules);
156
+ const ops = new import_summae_core2.TenantOperations(workspace.tenant());
157
+ const created = { accounts: 0, fiscalYears: 0 };
158
+ for (const account of Array.isArray(rules.accounts) ? rules.accounts : []) {
159
+ if (account !== null && typeof account === "object") {
160
+ ops.execute("createAccount", account);
161
+ created.accounts++;
162
+ }
163
+ }
164
+ for (const fiscalYear of Array.isArray(rules.fiscalYears) ? rules.fiscalYears : []) {
165
+ if (fiscalYear !== null && typeof fiscalYear === "object") {
166
+ ops.execute("createFiscalYear", fiscalYear);
167
+ created.fiscalYears++;
168
+ }
169
+ }
170
+ emit({ initialized: true, tenant: opts.name, baseCurrency: opts.currency, created });
171
+ });
172
+ program.command("op").description("Schreiboperation ausf\xFChren (post, postVoucher, settle, \u2026)").argument("<operation>", "Operationsname laut api.md").option("--input <json>", "Eingabe als JSON oder @datei", "{}").option("--dir <dir>", "Arbeitsverzeichnis", ".").action((operation, opts) => {
173
+ try {
174
+ const payload = parseJson(opts.input);
175
+ emit(new import_summae_core2.TenantOperations(Workspace.in(opts.dir).tenant()).execute(operation, payload));
176
+ } catch (error) {
177
+ reportDomainError(error);
178
+ }
179
+ });
180
+ program.command("report").description("Projektion berechnen (trialBalance, cashBasisReport, vatReturn, \u2026)").argument("<projection>", "Projektionsname laut api.md").option("--params <json>", "Parameter als JSON oder @datei", "{}").option("--dir <dir>", "Arbeitsverzeichnis", ".").action((projection, opts) => {
181
+ try {
182
+ const params = parseJson(opts.params);
183
+ emit(new import_summae_core2.TenantOperations(Workspace.in(opts.dir).tenant()).project(projection, params));
184
+ } catch (error) {
185
+ reportDomainError(error);
186
+ }
187
+ });
188
+ return program;
189
+ }
190
+ function run(argv) {
191
+ buildProgram().parse(argv);
192
+ }
193
+
194
+ // src/summae.ts
195
+ run(process.argv);
196
+ //# sourceMappingURL=summae.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/exit-codes.ts","../src/workspace.ts","../src/summae.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { DomainError, TenantOperations } from '@superheld/summae-core';\nimport { Command } from 'commander';\nimport { exitCodeFor } from './exit-codes.js';\nimport { Workspace } from './workspace.js';\n\nfunction parseJson(raw: string): Record<string, unknown> {\n const text = raw.startsWith('@') ? readFileSync(raw.slice(1), 'utf8') : raw;\n const parsed: unknown = JSON.parse(text);\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : {};\n}\n\nfunction emit(value: unknown): void {\n console.log(JSON.stringify(value));\n}\n\n/** DomainError → JSON-Fehler + Exit-Code; sonst neu werfen. */\nfunction reportDomainError(error: unknown): void {\n if (!(error instanceof DomainError)) throw error;\n emit({\n error: error.errorCode,\n message: error.message,\n details: Object.keys(error.details).length === 0 ? {} : error.details,\n });\n process.exitCode = exitCodeFor(error.errorCode);\n}\n\ninterface CommonOptions {\n dir: string;\n}\ninterface InitOptions extends CommonOptions {\n name: string;\n currency: string;\n rules?: string;\n}\n\n/** Baut die CLI (init/op/report) — Pendant zu PHPs Symfony-Console-App. */\nexport function buildProgram(): Command {\n const program = new Command();\n program.name('summae').description('summae — Buchführung über JSON-Ein/Ausgabe').version('0.1.0');\n\n program\n .command('init')\n .description('Arbeitsbereich anlegen (summae.json + SQLite-Datenbank)')\n .requiredOption('--name <name>', 'Mandantenname')\n .option('--currency <iso>', 'Basiswährung (ISO 4217)', 'EUR')\n .option('--rules <file>', 'JSON-Datei mit Regelmodul-Daten')\n .option('--dir <dir>', 'Arbeitsverzeichnis', '.')\n .action((opts: InitOptions) => {\n const rules = typeof opts.rules === 'string' ? parseJson(`@${opts.rules}`) : {};\n const workspace = Workspace.in(opts.dir);\n workspace.initialize(opts.name, opts.currency, rules);\n\n // SF-01: Stammdaten aus der Regeldatei direkt anlegen — sofort buchbar.\n const ops = new TenantOperations(workspace.tenant());\n const created = { accounts: 0, fiscalYears: 0 };\n for (const account of Array.isArray(rules.accounts) ? rules.accounts : []) {\n if (account !== null && typeof account === 'object') {\n ops.execute('createAccount', account as Record<string, unknown>);\n created.accounts++;\n }\n }\n for (const fiscalYear of Array.isArray(rules.fiscalYears) ? rules.fiscalYears : []) {\n if (fiscalYear !== null && typeof fiscalYear === 'object') {\n ops.execute('createFiscalYear', fiscalYear as Record<string, unknown>);\n created.fiscalYears++;\n }\n }\n emit({ initialized: true, tenant: opts.name, baseCurrency: opts.currency, created });\n });\n\n program\n .command('op')\n .description('Schreiboperation ausführen (post, postVoucher, settle, …)')\n .argument('<operation>', 'Operationsname laut api.md')\n .option('--input <json>', 'Eingabe als JSON oder @datei', '{}')\n .option('--dir <dir>', 'Arbeitsverzeichnis', '.')\n .action((operation: string, opts: CommonOptions & { input: string }) => {\n try {\n const payload = parseJson(opts.input);\n emit(new TenantOperations(Workspace.in(opts.dir).tenant()).execute(operation, payload));\n } catch (error) {\n reportDomainError(error);\n }\n });\n\n program\n .command('report')\n .description('Projektion berechnen (trialBalance, cashBasisReport, vatReturn, …)')\n .argument('<projection>', 'Projektionsname laut api.md')\n .option('--params <json>', 'Parameter als JSON oder @datei', '{}')\n .option('--dir <dir>', 'Arbeitsverzeichnis', '.')\n .action((projection: string, opts: CommonOptions & { params: string }) => {\n try {\n const params = parseJson(opts.params);\n emit(new TenantOperations(Workspace.in(opts.dir).tenant()).project(projection, params));\n } catch (error) {\n reportDomainError(error);\n }\n });\n\n return program;\n}\n\nexport function run(argv: string[]): void {\n buildProgram().parse(argv);\n}\n","/**\n * Exit-Codes = Fehlercodes (api.md F-IO-003): stabile numerische Abbildung des\n * Fehlerkatalogs. 0 = Erfolg, 1 = unbekannter Fehler, sonst Index + 10.\n * **Reihenfolge ist append-only** und identisch zur PHP-Referenz (`ExitCodes.php`) —\n * Umsortieren wäre ein Breaking Change.\n */\nconst CODES: readonly string[] = [\n 'E_ENTRY_UNBALANCED',\n 'E_ENTRY_NO_VOUCHER',\n 'E_VOUCHER_UNKNOWN',\n 'E_ENTRY_TOO_FEW_LINES',\n 'E_ENTRY_INVALID_AMOUNT',\n 'E_ENTRY_FINALIZED',\n 'E_ENTRY_ALREADY_REVERSED',\n 'E_ENTRY_UNKNOWN',\n 'E_PERIOD_CLOSED',\n 'E_PERIOD_OUT_OF_ORDER',\n 'E_PERIOD_UNKNOWN',\n 'E_FISCALYEAR_CLOSED',\n 'E_FISCALYEAR_UNFINALIZED_ENTRIES',\n 'E_FISCALYEAR_OVERLAP',\n 'E_ACCOUNT_UNKNOWN',\n 'E_ACCOUNT_LOCKED',\n 'E_ACCOUNT_NUMBER_TAKEN',\n 'E_COA_FORMAT_INVALID',\n 'E_SETTLEMENT_EXCEEDS_ITEM',\n 'E_SETTLEMENT_DIFFERENCE_INVALID',\n 'E_OPENITEM_UNKNOWN',\n 'E_CASHBASIS_DEVIATING_FISCAL_YEAR',\n 'E_TAXCODE_UNKNOWN',\n 'E_TAXCODE_NO_VALID_VERSION',\n 'E_PROFILE_RETROACTIVE_CONFLICT',\n 'E_PROFILE_UNKNOWN',\n 'E_PARTNER_UNKNOWN',\n 'E_DIMENSION_INVALID',\n 'E_ASSET_UNKNOWN',\n 'E_ASSET_DISPOSED',\n 'E_COSTING_RUN_RELEASED',\n 'E_COSTING_RUN_UNKNOWN',\n 'E_COSTING_CYCLE',\n 'E_MAPPING_OVERLAP',\n 'E_NOT_IMPLEMENTED',\n];\n\nexport function exitCodeFor(errorCode: string): number {\n const index = CODES.indexOf(errorCode);\n return index === -1 ? 1 : index + 10;\n}\n","import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n Currency,\n DimensionRegistry,\n type DimensionRuleData,\n type DimensionTypeData,\n type DimensionValueData,\n MappingRegistry,\n SystemClock,\n TaxCodeRegistry,\n TaxProfile,\n type Tenant,\n Uuid,\n UuidV7IdGenerator,\n} from '@superheld/summae-core';\nimport { DatabaseTenantFactory, SyncDb, installSchema } from '@superheld/summae-knex';\n\nconst CONFIG_FILE = 'summae.json';\nconst DB_FILE = 'summae.sqlite';\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === 'object' && !Array.isArray(v);\n}\nfunction recordList(v: unknown): Record<string, unknown>[] {\n return Array.isArray(v) ? v.filter(isRecord) : [];\n}\n\n/**\n * CLI-Arbeitsbereich: `summae.json` (Mandanten-Meta + Regelmodul-Daten,\n * App-Schicht) + `summae.sqlite` (Persistenz via @superheld/summae-knex).\n * Jeder Aufruf lädt den Mandanten, führt aus, die Datenbank persistiert.\n * Pendant zu PHPs `Summae\\Cli\\Workspace`.\n */\nexport class Workspace {\n private constructor(private readonly directory: string) {}\n\n static in(directory: string): Workspace {\n return new Workspace(directory.replace(/\\/+$/, ''));\n }\n\n exists(): boolean {\n return existsSync(this.configPath());\n }\n\n /** @param ruleData accounts, taxCodes, taxProfile, dimensionTypes/-Values, ruleModules */\n initialize(name: string, currency: string, ruleData: Record<string, unknown>): void {\n if (this.exists()) {\n throw new Error(`Arbeitsbereich existiert bereits: ${this.configPath()}`);\n }\n const config = { name, baseCurrency: currency, tenantId: Uuid.v7().value, rules: ruleData };\n writeFileSync(this.configPath(), `${JSON.stringify(config, null, 2)}\\n`);\n\n const db = new SyncDb(this.dbPath());\n installSchema(db);\n db.close();\n }\n\n tenant(): Tenant {\n if (!this.exists()) {\n throw new Error(`Kein Arbeitsbereich in ${this.directory} — zuerst \\`summae init\\` ausführen`);\n }\n const config = JSON.parse(readFileSync(this.configPath(), 'utf8')) as Record<string, unknown>;\n const rules = isRecord(config.rules) ? config.rules : {};\n const ruleModules = isRecord(rules.ruleModules) ? rules.ruleModules : {};\n\n const dimensionTypes: DimensionTypeData[] = recordList(rules.dimensionTypes).map((t) => ({ code: String(t.code) }));\n const dimensionValues: DimensionValueData[] = recordList(rules.dimensionValues).map((v) => ({\n typeCode: String(v.typeCode),\n code: String(v.code),\n }));\n const dimensionRules: DimensionRuleData[] = recordList(ruleModules.dimensionRules).map((r) => {\n const range = isRecord(r.accountRange) ? r.accountRange : {};\n return { accountRange: { from: String(range.from), to: String(range.to) }, requiredDimension: String(r.requiredDimension) };\n });\n\n const clock = new SystemClock();\n const tenantId = typeof config.tenantId === 'string' ? Uuid.fromString(config.tenantId) : undefined;\n\n const tenant = DatabaseTenantFactory.build(\n new SyncDb(this.dbPath()),\n typeof config.name === 'string' ? config.name : 'CLI',\n Currency.of(typeof config.baseCurrency === 'string' ? config.baseCurrency : 'EUR'),\n clock,\n new UuidV7IdGenerator(clock),\n {\n dimensions: DimensionRegistry.fromData(dimensionTypes, dimensionValues, dimensionRules),\n taxCodes: TaxCodeRegistry.fromData(recordList(rules.taxCodes)),\n taxProfile: TaxProfile.fromData(isRecord(rules.taxProfile) ? rules.taxProfile : {}),\n mappings: MappingRegistry.fromRuleModules(Array.isArray(ruleModules.mappings) ? ruleModules.mappings : []),\n ...(tenantId ? { tenantId } : {}),\n },\n );\n tenant.assetService.setRuleModule(ruleModules);\n return tenant;\n }\n\n private configPath(): string {\n return join(this.directory, CONFIG_FILE);\n }\n private dbPath(): string {\n return join(this.directory, DB_FILE);\n }\n}\n","import { run } from './cli.js';\n\nrun(process.argv);\n"],"mappings":"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","names":["import_node_fs","import_summae_core"]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/summae.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ run
4
+ } from "./chunk-XAXHYNID.js";
5
+
6
+ // src/summae.ts
7
+ run(process.argv);
8
+ //# sourceMappingURL=summae.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/summae.ts"],"sourcesContent":["import { run } from './cli.js';\n\nrun(process.argv);\n"],"mappings":";;;;;;AAEA,IAAI,QAAQ,IAAI;","names":[]}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@superheld/summae-cli",
3
+ "version": "0.2.0",
4
+ "description": "CLI (Node) für summae — JSON-Ein/Ausgabe, persistenter SQLite-Arbeitsbereich über @superheld/summae-knex",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "accounting",
9
+ "rechnungswesen",
10
+ "summae",
11
+ "cli"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/superheld/summae.git",
16
+ "directory": "implementations/node/packages/cli"
17
+ },
18
+ "homepage": "https://github.com/superheld/summae/tree/main/implementations/node/packages/cli#readme",
19
+ "bugs": "https://github.com/superheld/summae/issues",
20
+ "engines": {
21
+ "node": ">=22"
22
+ },
23
+ "bin": {
24
+ "summae": "./dist/summae.js"
25
+ },
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js",
30
+ "require": "./dist/index.cjs"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "dependencies": {
37
+ "commander": "^12.1.0",
38
+ "@superheld/summae-core": "0.2.0",
39
+ "@superheld/summae-knex": "0.2.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^25.9.3",
43
+ "tsup": "^8.5.0"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup"
50
+ }
51
+ }