@hasna/invoices 0.1.4 → 0.1.5

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/dist/cli/index.js CHANGED
@@ -2093,6 +2093,16 @@ function assertInvoiceStatus(status) {
2093
2093
  throw new RangeError(`Invalid invoice status: ${String(status)}`);
2094
2094
  }
2095
2095
  }
2096
+ function assertPartyKind(kind) {
2097
+ if (!PARTY_KINDS.has(kind)) {
2098
+ throw new RangeError(`Invalid party kind: ${String(kind)}`);
2099
+ }
2100
+ }
2101
+ function assertNonBlankString(value, field) {
2102
+ if (typeof value !== "string" || value.trim().length === 0) {
2103
+ throw new TypeError(`${field} must not be blank`);
2104
+ }
2105
+ }
2096
2106
  function assertSafeCents(value, field) {
2097
2107
  if (!Number.isSafeInteger(value) || value < 0) {
2098
2108
  throw new RangeError(`${field} must be a safe nonnegative integer`);
@@ -2210,14 +2220,27 @@ function mapInvoiceLineRow(row) {
2210
2220
  };
2211
2221
  }
2212
2222
  function createParty(db, input) {
2213
- const id = input.id ?? randomUUID();
2223
+ const id = input.id;
2224
+ const kind = input.kind;
2225
+ const legalName = input.legalName;
2226
+ const email = input.email;
2227
+ const taxId = input.taxId;
2228
+ const country = input.country;
2229
+ const address = input.address;
2230
+ if (id !== undefined) {
2231
+ assertNonBlankString(id, "id");
2232
+ }
2233
+ assertPartyKind(kind);
2234
+ assertNonBlankString(legalName, "legalName");
2235
+ const partyId = id ?? randomUUID();
2214
2236
  db.query(`
2215
2237
  INSERT INTO parties (id, kind, legal_name, email, tax_id, country, address)
2216
2238
  VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
2217
- `).run(id, input.kind, input.legalName, input.email ?? null, input.taxId ?? null, input.country ?? null, input.address ?? null);
2218
- return getPartyById(db, id);
2239
+ `).run(partyId, kind, legalName, email ?? null, taxId ?? null, country ?? null, address ?? null);
2240
+ return getPartyById(db, partyId);
2219
2241
  }
2220
2242
  function getPartyById(db, id) {
2243
+ assertNonBlankString(id, "id");
2221
2244
  const row = db.query("SELECT * FROM parties WHERE id = ?1").get(id);
2222
2245
  return row ? mapPartyRow(row) : null;
2223
2246
  }
@@ -2228,8 +2251,28 @@ function listParties(db, kind) {
2228
2251
  return db.query("SELECT * FROM parties ORDER BY legal_name ASC").all().map(mapPartyRow);
2229
2252
  }
2230
2253
  function createInvoice(db, input) {
2231
- const preparedLines = prepareInvoiceLines(input.lines);
2232
- const invoiceId = input.id ?? randomUUID();
2254
+ const id = input.id;
2255
+ const number = input.number;
2256
+ const issuerId = input.issuerId;
2257
+ const customerId = input.customerId;
2258
+ const currency = input.currency;
2259
+ const notes = input.notes;
2260
+ const issuedAt = input.issuedAt;
2261
+ const dueAt = input.dueAt;
2262
+ const lines = input.lines;
2263
+ if (id !== undefined) {
2264
+ assertNonBlankString(id, "id");
2265
+ }
2266
+ assertNonBlankString(number, "number");
2267
+ assertNonBlankString(issuerId, "issuerId");
2268
+ assertNonBlankString(customerId, "customerId");
2269
+ assertNonBlankString(currency, "currency");
2270
+ assertNonBlankString(issuedAt, "issuedAt");
2271
+ if (dueAt !== undefined) {
2272
+ assertNonBlankString(dueAt, "dueAt");
2273
+ }
2274
+ const preparedLines = prepareInvoiceLines(lines);
2275
+ const invoiceId = id ?? randomUUID();
2233
2276
  let subtotalCents = 0;
2234
2277
  let taxCents = 0;
2235
2278
  for (const line of preparedLines) {
@@ -2246,7 +2289,7 @@ INSERT INTO invoices (
2246
2289
  id, number, issuer_id, customer_id, status, currency, notes, issued_at, due_at, subtotal_cents, tax_cents, total_cents
2247
2290
  )
2248
2291
  VALUES (?1, ?2, ?3, ?4, 'draft', ?5, ?6, ?7, ?8, ?9, ?10, ?11)
2249
- `).run(invoiceId, input.number, input.issuerId, input.customerId, input.currency, input.notes ?? null, input.issuedAt, input.dueAt ?? null, subtotalCents, taxCents, totalCents);
2292
+ `).run(invoiceId, number, issuerId, customerId, currency, notes ?? null, issuedAt, dueAt ?? null, subtotalCents, taxCents, totalCents);
2250
2293
  const insertLine = db.query(`
2251
2294
  INSERT INTO invoice_lines (
2252
2295
  id, invoice_id, position, description, quantity, unit_price_cents, tax_rate_basis_points, line_total_cents
@@ -2310,9 +2353,10 @@ LIMIT ?2
2310
2353
  OFFSET ?3
2311
2354
  `).all(escapedPhrase, limit, offset).map(mapInvoiceRow);
2312
2355
  }
2313
- var INVOICE_STATUSES;
2356
+ var INVOICE_STATUSES, PARTY_KINDS;
2314
2357
  var init_invoices = __esm(() => {
2315
2358
  INVOICE_STATUSES = new Set(["draft", "sent", "partially_paid", "paid", "void", "overdue"]);
2359
+ PARTY_KINDS = new Set(["issuer", "customer"]);
2316
2360
  });
2317
2361
 
2318
2362
  // src/db/database.ts
@@ -2482,7 +2526,7 @@ function listAgents(db) {
2482
2526
  }
2483
2527
 
2484
2528
  // src/lib/version.ts
2485
- var VERSION = "0.1.4";
2529
+ var VERSION = "0.1.5";
2486
2530
 
2487
2531
  // src/cli/index.ts
2488
2532
  init_database();
@@ -1 +1 @@
1
- {"version":3,"file":"invoices.d.ts","sourceRoot":"","sources":["../../src/db/invoices.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"invoices.d.ts","sourceRoot":"","sources":["../../src/db/invoices.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // @bun
2
2
  // src/lib/version.ts
3
- var VERSION = "0.1.4";
3
+ var VERSION = "0.1.5";
4
4
  // src/db/database.ts
5
5
  import { mkdirSync } from "fs";
6
6
  import { dirname, join } from "path";
@@ -206,6 +206,7 @@ function migrateDatabase(options = {}) {
206
206
  // src/db/invoices.ts
207
207
  import { randomUUID } from "crypto";
208
208
  var INVOICE_STATUSES = new Set(["draft", "sent", "partially_paid", "paid", "void", "overdue"]);
209
+ var PARTY_KINDS = new Set(["issuer", "customer"]);
209
210
  function normalizePagination(options, defaults) {
210
211
  const limit = Number.isFinite(options.limit) ? Math.max(Math.trunc(options.limit), 0) : defaults.limit;
211
212
  const offset = Number.isFinite(options.offset) ? Math.max(Math.trunc(options.offset), 0) : 0;
@@ -216,6 +217,16 @@ function assertInvoiceStatus(status) {
216
217
  throw new RangeError(`Invalid invoice status: ${String(status)}`);
217
218
  }
218
219
  }
220
+ function assertPartyKind(kind) {
221
+ if (!PARTY_KINDS.has(kind)) {
222
+ throw new RangeError(`Invalid party kind: ${String(kind)}`);
223
+ }
224
+ }
225
+ function assertNonBlankString(value, field) {
226
+ if (typeof value !== "string" || value.trim().length === 0) {
227
+ throw new TypeError(`${field} must not be blank`);
228
+ }
229
+ }
219
230
  function assertSafeCents(value, field) {
220
231
  if (!Number.isSafeInteger(value) || value < 0) {
221
232
  throw new RangeError(`${field} must be a safe nonnegative integer`);
@@ -333,14 +344,35 @@ function mapInvoiceLineRow(row) {
333
344
  };
334
345
  }
335
346
  function createParty(db, input) {
336
- const id = input.id ?? randomUUID();
347
+ const id = input.id;
348
+ const kind = input.kind;
349
+ const legalName = input.legalName;
350
+ const email = input.email;
351
+ const taxId = input.taxId;
352
+ const country = input.country;
353
+ const address = input.address;
354
+ if (id !== undefined) {
355
+ assertNonBlankString(id, "id");
356
+ }
357
+ assertPartyKind(kind);
358
+ assertNonBlankString(legalName, "legalName");
359
+ const partyId = id ?? randomUUID();
337
360
  db.query(`
338
361
  INSERT INTO parties (id, kind, legal_name, email, tax_id, country, address)
339
362
  VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
340
- `).run(id, input.kind, input.legalName, input.email ?? null, input.taxId ?? null, input.country ?? null, input.address ?? null);
341
- return getPartyById(db, id);
363
+ `).run(partyId, kind, legalName, email ?? null, taxId ?? null, country ?? null, address ?? null);
364
+ return getPartyById(db, partyId);
342
365
  }
343
366
  function updateParty(db, id, updates) {
367
+ const legalName = updates.legalName;
368
+ const email = updates.email;
369
+ const taxId = updates.taxId;
370
+ const country = updates.country;
371
+ const address = updates.address;
372
+ assertNonBlankString(id, "id");
373
+ if (legalName !== undefined) {
374
+ assertNonBlankString(legalName, "legalName");
375
+ }
344
376
  db.query(`
345
377
  UPDATE parties
346
378
  SET legal_name = COALESCE(?2, legal_name),
@@ -350,10 +382,11 @@ SET legal_name = COALESCE(?2, legal_name),
350
382
  address = COALESCE(?6, address),
351
383
  updated_at = datetime('now')
352
384
  WHERE id = ?1
353
- `).run(id, updates.legalName ?? null, updates.email ?? null, updates.taxId ?? null, updates.country ?? null, updates.address ?? null);
385
+ `).run(id, legalName ?? null, email ?? null, taxId ?? null, country ?? null, address ?? null);
354
386
  return getPartyById(db, id);
355
387
  }
356
388
  function getPartyById(db, id) {
389
+ assertNonBlankString(id, "id");
357
390
  const row = db.query("SELECT * FROM parties WHERE id = ?1").get(id);
358
391
  return row ? mapPartyRow(row) : null;
359
392
  }
@@ -364,8 +397,28 @@ function listParties(db, kind) {
364
397
  return db.query("SELECT * FROM parties ORDER BY legal_name ASC").all().map(mapPartyRow);
365
398
  }
366
399
  function createInvoice(db, input) {
367
- const preparedLines = prepareInvoiceLines(input.lines);
368
- const invoiceId = input.id ?? randomUUID();
400
+ const id = input.id;
401
+ const number = input.number;
402
+ const issuerId = input.issuerId;
403
+ const customerId = input.customerId;
404
+ const currency = input.currency;
405
+ const notes = input.notes;
406
+ const issuedAt = input.issuedAt;
407
+ const dueAt = input.dueAt;
408
+ const lines = input.lines;
409
+ if (id !== undefined) {
410
+ assertNonBlankString(id, "id");
411
+ }
412
+ assertNonBlankString(number, "number");
413
+ assertNonBlankString(issuerId, "issuerId");
414
+ assertNonBlankString(customerId, "customerId");
415
+ assertNonBlankString(currency, "currency");
416
+ assertNonBlankString(issuedAt, "issuedAt");
417
+ if (dueAt !== undefined) {
418
+ assertNonBlankString(dueAt, "dueAt");
419
+ }
420
+ const preparedLines = prepareInvoiceLines(lines);
421
+ const invoiceId = id ?? randomUUID();
369
422
  let subtotalCents = 0;
370
423
  let taxCents = 0;
371
424
  for (const line of preparedLines) {
@@ -382,7 +435,7 @@ INSERT INTO invoices (
382
435
  id, number, issuer_id, customer_id, status, currency, notes, issued_at, due_at, subtotal_cents, tax_cents, total_cents
383
436
  )
384
437
  VALUES (?1, ?2, ?3, ?4, 'draft', ?5, ?6, ?7, ?8, ?9, ?10, ?11)
385
- `).run(invoiceId, input.number, input.issuerId, input.customerId, input.currency, input.notes ?? null, input.issuedAt, input.dueAt ?? null, subtotalCents, taxCents, totalCents);
438
+ `).run(invoiceId, number, issuerId, customerId, currency, notes ?? null, issuedAt, dueAt ?? null, subtotalCents, taxCents, totalCents);
386
439
  const insertLine = db.query(`
387
440
  INSERT INTO invoice_lines (
388
441
  id, invoice_id, position, description, quantity, unit_price_cents, tax_rate_basis_points, line_total_cents
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.4";
1
+ export declare const VERSION = "0.1.5";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/mcp/index.js CHANGED
@@ -4202,6 +4202,7 @@ function migrateDatabase(options = {}) {
4202
4202
  // src/db/invoices.ts
4203
4203
  import { randomUUID } from "crypto";
4204
4204
  var INVOICE_STATUSES = new Set(["draft", "sent", "partially_paid", "paid", "void", "overdue"]);
4205
+ var PARTY_KINDS = new Set(["issuer", "customer"]);
4205
4206
  function normalizePagination(options, defaults) {
4206
4207
  const limit = Number.isFinite(options.limit) ? Math.max(Math.trunc(options.limit), 0) : defaults.limit;
4207
4208
  const offset = Number.isFinite(options.offset) ? Math.max(Math.trunc(options.offset), 0) : 0;
@@ -4212,6 +4213,16 @@ function assertInvoiceStatus(status) {
4212
4213
  throw new RangeError(`Invalid invoice status: ${String(status)}`);
4213
4214
  }
4214
4215
  }
4216
+ function assertPartyKind(kind) {
4217
+ if (!PARTY_KINDS.has(kind)) {
4218
+ throw new RangeError(`Invalid party kind: ${String(kind)}`);
4219
+ }
4220
+ }
4221
+ function assertNonBlankString(value, field) {
4222
+ if (typeof value !== "string" || value.trim().length === 0) {
4223
+ throw new TypeError(`${field} must not be blank`);
4224
+ }
4225
+ }
4215
4226
  function assertSafeCents(value, field) {
4216
4227
  if (!Number.isSafeInteger(value) || value < 0) {
4217
4228
  throw new RangeError(`${field} must be a safe nonnegative integer`);
@@ -4329,14 +4340,27 @@ function mapInvoiceLineRow(row) {
4329
4340
  };
4330
4341
  }
4331
4342
  function createParty(db, input) {
4332
- const id = input.id ?? randomUUID();
4343
+ const id = input.id;
4344
+ const kind = input.kind;
4345
+ const legalName = input.legalName;
4346
+ const email = input.email;
4347
+ const taxId = input.taxId;
4348
+ const country = input.country;
4349
+ const address = input.address;
4350
+ if (id !== undefined) {
4351
+ assertNonBlankString(id, "id");
4352
+ }
4353
+ assertPartyKind(kind);
4354
+ assertNonBlankString(legalName, "legalName");
4355
+ const partyId = id ?? randomUUID();
4333
4356
  db.query(`
4334
4357
  INSERT INTO parties (id, kind, legal_name, email, tax_id, country, address)
4335
4358
  VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
4336
- `).run(id, input.kind, input.legalName, input.email ?? null, input.taxId ?? null, input.country ?? null, input.address ?? null);
4337
- return getPartyById(db, id);
4359
+ `).run(partyId, kind, legalName, email ?? null, taxId ?? null, country ?? null, address ?? null);
4360
+ return getPartyById(db, partyId);
4338
4361
  }
4339
4362
  function getPartyById(db, id) {
4363
+ assertNonBlankString(id, "id");
4340
4364
  const row = db.query("SELECT * FROM parties WHERE id = ?1").get(id);
4341
4365
  return row ? mapPartyRow(row) : null;
4342
4366
  }
@@ -4347,8 +4371,28 @@ function listParties(db, kind) {
4347
4371
  return db.query("SELECT * FROM parties ORDER BY legal_name ASC").all().map(mapPartyRow);
4348
4372
  }
4349
4373
  function createInvoice(db, input) {
4350
- const preparedLines = prepareInvoiceLines(input.lines);
4351
- const invoiceId = input.id ?? randomUUID();
4374
+ const id = input.id;
4375
+ const number = input.number;
4376
+ const issuerId = input.issuerId;
4377
+ const customerId = input.customerId;
4378
+ const currency = input.currency;
4379
+ const notes = input.notes;
4380
+ const issuedAt = input.issuedAt;
4381
+ const dueAt = input.dueAt;
4382
+ const lines = input.lines;
4383
+ if (id !== undefined) {
4384
+ assertNonBlankString(id, "id");
4385
+ }
4386
+ assertNonBlankString(number, "number");
4387
+ assertNonBlankString(issuerId, "issuerId");
4388
+ assertNonBlankString(customerId, "customerId");
4389
+ assertNonBlankString(currency, "currency");
4390
+ assertNonBlankString(issuedAt, "issuedAt");
4391
+ if (dueAt !== undefined) {
4392
+ assertNonBlankString(dueAt, "dueAt");
4393
+ }
4394
+ const preparedLines = prepareInvoiceLines(lines);
4395
+ const invoiceId = id ?? randomUUID();
4352
4396
  let subtotalCents = 0;
4353
4397
  let taxCents = 0;
4354
4398
  for (const line of preparedLines) {
@@ -4365,7 +4409,7 @@ INSERT INTO invoices (
4365
4409
  id, number, issuer_id, customer_id, status, currency, notes, issued_at, due_at, subtotal_cents, tax_cents, total_cents
4366
4410
  )
4367
4411
  VALUES (?1, ?2, ?3, ?4, 'draft', ?5, ?6, ?7, ?8, ?9, ?10, ?11)
4368
- `).run(invoiceId, input.number, input.issuerId, input.customerId, input.currency, input.notes ?? null, input.issuedAt, input.dueAt ?? null, subtotalCents, taxCents, totalCents);
4412
+ `).run(invoiceId, number, issuerId, customerId, currency, notes ?? null, issuedAt, dueAt ?? null, subtotalCents, taxCents, totalCents);
4369
4413
  const insertLine = db.query(`
4370
4414
  INSERT INTO invoice_lines (
4371
4415
  id, invoice_id, position, description, quantity, unit_price_cents, tax_rate_basis_points, line_total_cents
@@ -4010,6 +4010,7 @@ function openInvoiceDatabase(options = {}) {
4010
4010
  // src/db/invoices.ts
4011
4011
  import { randomUUID } from "crypto";
4012
4012
  var INVOICE_STATUSES = new Set(["draft", "sent", "partially_paid", "paid", "void", "overdue"]);
4013
+ var PARTY_KINDS = new Set(["issuer", "customer"]);
4013
4014
  function normalizePagination(options, defaults) {
4014
4015
  const limit = Number.isFinite(options.limit) ? Math.max(Math.trunc(options.limit), 0) : defaults.limit;
4015
4016
  const offset = Number.isFinite(options.offset) ? Math.max(Math.trunc(options.offset), 0) : 0;
@@ -4020,6 +4021,16 @@ function assertInvoiceStatus(status) {
4020
4021
  throw new RangeError(`Invalid invoice status: ${String(status)}`);
4021
4022
  }
4022
4023
  }
4024
+ function assertPartyKind(kind) {
4025
+ if (!PARTY_KINDS.has(kind)) {
4026
+ throw new RangeError(`Invalid party kind: ${String(kind)}`);
4027
+ }
4028
+ }
4029
+ function assertNonBlankString(value, field) {
4030
+ if (typeof value !== "string" || value.trim().length === 0) {
4031
+ throw new TypeError(`${field} must not be blank`);
4032
+ }
4033
+ }
4023
4034
  function assertSafeCents(value, field) {
4024
4035
  if (!Number.isSafeInteger(value) || value < 0) {
4025
4036
  throw new RangeError(`${field} must be a safe nonnegative integer`);
@@ -4137,14 +4148,27 @@ function mapInvoiceLineRow(row) {
4137
4148
  };
4138
4149
  }
4139
4150
  function createParty(db, input) {
4140
- const id = input.id ?? randomUUID();
4151
+ const id = input.id;
4152
+ const kind = input.kind;
4153
+ const legalName = input.legalName;
4154
+ const email = input.email;
4155
+ const taxId = input.taxId;
4156
+ const country = input.country;
4157
+ const address = input.address;
4158
+ if (id !== undefined) {
4159
+ assertNonBlankString(id, "id");
4160
+ }
4161
+ assertPartyKind(kind);
4162
+ assertNonBlankString(legalName, "legalName");
4163
+ const partyId = id ?? randomUUID();
4141
4164
  db.query(`
4142
4165
  INSERT INTO parties (id, kind, legal_name, email, tax_id, country, address)
4143
4166
  VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
4144
- `).run(id, input.kind, input.legalName, input.email ?? null, input.taxId ?? null, input.country ?? null, input.address ?? null);
4145
- return getPartyById(db, id);
4167
+ `).run(partyId, kind, legalName, email ?? null, taxId ?? null, country ?? null, address ?? null);
4168
+ return getPartyById(db, partyId);
4146
4169
  }
4147
4170
  function getPartyById(db, id) {
4171
+ assertNonBlankString(id, "id");
4148
4172
  const row = db.query("SELECT * FROM parties WHERE id = ?1").get(id);
4149
4173
  return row ? mapPartyRow(row) : null;
4150
4174
  }
@@ -4155,8 +4179,28 @@ function listParties(db, kind) {
4155
4179
  return db.query("SELECT * FROM parties ORDER BY legal_name ASC").all().map(mapPartyRow);
4156
4180
  }
4157
4181
  function createInvoice(db, input) {
4158
- const preparedLines = prepareInvoiceLines(input.lines);
4159
- const invoiceId = input.id ?? randomUUID();
4182
+ const id = input.id;
4183
+ const number = input.number;
4184
+ const issuerId = input.issuerId;
4185
+ const customerId = input.customerId;
4186
+ const currency = input.currency;
4187
+ const notes = input.notes;
4188
+ const issuedAt = input.issuedAt;
4189
+ const dueAt = input.dueAt;
4190
+ const lines = input.lines;
4191
+ if (id !== undefined) {
4192
+ assertNonBlankString(id, "id");
4193
+ }
4194
+ assertNonBlankString(number, "number");
4195
+ assertNonBlankString(issuerId, "issuerId");
4196
+ assertNonBlankString(customerId, "customerId");
4197
+ assertNonBlankString(currency, "currency");
4198
+ assertNonBlankString(issuedAt, "issuedAt");
4199
+ if (dueAt !== undefined) {
4200
+ assertNonBlankString(dueAt, "dueAt");
4201
+ }
4202
+ const preparedLines = prepareInvoiceLines(lines);
4203
+ const invoiceId = id ?? randomUUID();
4160
4204
  let subtotalCents = 0;
4161
4205
  let taxCents = 0;
4162
4206
  for (const line of preparedLines) {
@@ -4173,7 +4217,7 @@ INSERT INTO invoices (
4173
4217
  id, number, issuer_id, customer_id, status, currency, notes, issued_at, due_at, subtotal_cents, tax_cents, total_cents
4174
4218
  )
4175
4219
  VALUES (?1, ?2, ?3, ?4, 'draft', ?5, ?6, ?7, ?8, ?9, ?10, ?11)
4176
- `).run(invoiceId, input.number, input.issuerId, input.customerId, input.currency, input.notes ?? null, input.issuedAt, input.dueAt ?? null, subtotalCents, taxCents, totalCents);
4220
+ `).run(invoiceId, number, issuerId, customerId, currency, notes ?? null, issuedAt, dueAt ?? null, subtotalCents, taxCents, totalCents);
4177
4221
  const insertLine = db.query(`
4178
4222
  INSERT INTO invoice_lines (
4179
4223
  id, invoice_id, position, description, quantity, unit_price_cents, tax_rate_basis_points, line_total_cents
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/invoices",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Invoice generation and management for AI agents - CLI + MCP server + REST API + dashboard + SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",