@agentcash/router 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -90,33 +90,46 @@ module.exports = __toCommonJS(index_exports);
90
90
  // src/registry.ts
91
91
  var RouteRegistry = class {
92
92
  routes = /* @__PURE__ */ new Map();
93
- // Silently overwrites on duplicate key. Next.js module loading order is
94
- // non-deterministic during build discovery stubs and real handlers may
95
- // register the same route key in either order. Last writer wins.
93
+ // Internal map key includes the HTTP method so that POST and DELETE on the
94
+ // same path coexist. Within the same path+method, last-write-wins is still
95
+ // intentional Next.js module loading order is non-deterministic during
96
+ // build and discovery stubs may register the same route in either order.
96
97
  // Prior art: ElysiaJS uses the same pattern (silent overwrite in router.history).
98
+ mapKey(entry) {
99
+ return `${entry.key}:${entry.method}`;
100
+ }
97
101
  register(entry) {
98
- if (this.routes.has(entry.key) && typeof process !== "undefined" && process.env?.["NODE_ENV"] !== "production") {
102
+ const k = this.mapKey(entry);
103
+ if (this.routes.has(k) && typeof process !== "undefined" && process.env?.["NODE_ENV"] !== "production") {
99
104
  console.warn(
100
- `[agentcash/router] route '${entry.key}' registered twice \u2014 overwriting (this is expected for discovery stubs during next build)`
105
+ `[agentcash/router] route '${entry.key}' (${entry.method}) registered twice \u2014 overwriting (this is expected for discovery stubs during next build)`
101
106
  );
102
107
  }
103
- this.routes.set(entry.key, entry);
108
+ this.routes.set(k, entry);
104
109
  }
110
+ // Accepts either a compound key ("site/domain:DELETE") or a path-only key
111
+ // ("site/domain") — path-only returns the first registered method for that path.
105
112
  get(key) {
106
- return this.routes.get(key);
113
+ const direct = this.routes.get(key);
114
+ if (direct) return direct;
115
+ for (const entry of this.routes.values()) {
116
+ if (entry.key === key) return entry;
117
+ }
118
+ return void 0;
107
119
  }
108
120
  entries() {
109
121
  return this.routes.entries();
110
122
  }
111
123
  has(key) {
112
- return this.routes.has(key);
124
+ return this.get(key) !== void 0;
113
125
  }
114
126
  get size() {
115
127
  return this.routes.size;
116
128
  }
117
129
  validate(expectedKeys) {
118
130
  if (!expectedKeys) return;
119
- const missing = expectedKeys.filter((k) => !this.routes.has(k));
131
+ const registeredPathKeys = new Set([...this.routes.values()].map((e) => e.key));
132
+ const missing = expectedKeys.filter((k) => !registeredPathKeys.has(k));
120
133
  if (missing.length > 0) {
121
134
  throw new Error(
122
135
  `route${missing.length > 1 ? "s" : ""} ${missing.map((k) => `'${k}'`).join(", ")} in prices map but not registered \u2014 add to barrel imports`
@@ -1475,8 +1488,8 @@ function createWellKnownHandler(registry, baseUrl, pricesKeys, discovery) {
1475
1488
  const x402Set = /* @__PURE__ */ new Set();
1476
1489
  const mppSet = /* @__PURE__ */ new Set();
1477
1490
  const methodHints = discovery.methodHints ?? "non-default";
1478
- for (const [key, entry] of registry.entries()) {
1479
- const url = `${normalizedBase}/api/${entry.path ?? key}`;
1491
+ for (const [, entry] of registry.entries()) {
1492
+ const url = `${normalizedBase}/api/${entry.path ?? entry.key}`;
1480
1493
  const resource = toDiscoveryResource(entry.method, url, methodHints);
1481
1494
  if (entry.authMode !== "unprotected") x402Set.add(resource);
1482
1495
  if (entry.protocols.includes("mpp")) mppSet.add(resource);
@@ -1532,12 +1545,12 @@ function createOpenAPIHandler(registry, baseUrl, pricesKeys, discovery) {
1532
1545
  const tagSet = /* @__PURE__ */ new Set();
1533
1546
  let requiresSiwxScheme = false;
1534
1547
  let requiresApiKeyScheme = false;
1535
- for (const [key, entry] of registry.entries()) {
1536
- const apiPath = `/api/${entry.path ?? key}`;
1548
+ for (const [, entry] of registry.entries()) {
1549
+ const apiPath = `/api/${entry.path ?? entry.key}`;
1537
1550
  const method = entry.method.toLowerCase();
1538
- const tag = deriveTag(key);
1551
+ const tag = deriveTag(entry.key);
1539
1552
  tagSet.add(tag);
1540
- const built = buildOperation(key, entry, tag);
1553
+ const built = buildOperation(entry.key, entry, tag);
1541
1554
  if (built.requiresSiwxScheme) requiresSiwxScheme = true;
1542
1555
  if (built.requiresApiKeyScheme) requiresApiKeyScheme = true;
1543
1556
  paths[apiPath] = { ...paths[apiPath], [method]: built.operation };
package/dist/index.d.cts CHANGED
@@ -349,6 +349,7 @@ interface RouterConfig {
349
349
 
350
350
  declare class RouteRegistry {
351
351
  private routes;
352
+ private mapKey;
352
353
  register(entry: RouteEntry): void;
353
354
  get(key: string): RouteEntry | undefined;
354
355
  entries(): IterableIterator<[string, RouteEntry]>;
package/dist/index.d.ts CHANGED
@@ -349,6 +349,7 @@ interface RouterConfig {
349
349
 
350
350
  declare class RouteRegistry {
351
351
  private routes;
352
+ private mapKey;
352
353
  register(entry: RouteEntry): void;
353
354
  get(key: string): RouteEntry | undefined;
354
355
  entries(): IterableIterator<[string, RouteEntry]>;
package/dist/index.js CHANGED
@@ -51,33 +51,46 @@ var init_server = __esm({
51
51
  // src/registry.ts
52
52
  var RouteRegistry = class {
53
53
  routes = /* @__PURE__ */ new Map();
54
- // Silently overwrites on duplicate key. Next.js module loading order is
55
- // non-deterministic during build discovery stubs and real handlers may
56
- // register the same route key in either order. Last writer wins.
54
+ // Internal map key includes the HTTP method so that POST and DELETE on the
55
+ // same path coexist. Within the same path+method, last-write-wins is still
56
+ // intentional Next.js module loading order is non-deterministic during
57
+ // build and discovery stubs may register the same route in either order.
57
58
  // Prior art: ElysiaJS uses the same pattern (silent overwrite in router.history).
59
+ mapKey(entry) {
60
+ return `${entry.key}:${entry.method}`;
61
+ }
58
62
  register(entry) {
59
- if (this.routes.has(entry.key) && typeof process !== "undefined" && process.env?.["NODE_ENV"] !== "production") {
63
+ const k = this.mapKey(entry);
64
+ if (this.routes.has(k) && typeof process !== "undefined" && process.env?.["NODE_ENV"] !== "production") {
60
65
  console.warn(
61
- `[agentcash/router] route '${entry.key}' registered twice \u2014 overwriting (this is expected for discovery stubs during next build)`
66
+ `[agentcash/router] route '${entry.key}' (${entry.method}) registered twice \u2014 overwriting (this is expected for discovery stubs during next build)`
62
67
  );
63
68
  }
64
- this.routes.set(entry.key, entry);
69
+ this.routes.set(k, entry);
65
70
  }
71
+ // Accepts either a compound key ("site/domain:DELETE") or a path-only key
72
+ // ("site/domain") — path-only returns the first registered method for that path.
66
73
  get(key) {
67
- return this.routes.get(key);
74
+ const direct = this.routes.get(key);
75
+ if (direct) return direct;
76
+ for (const entry of this.routes.values()) {
77
+ if (entry.key === key) return entry;
78
+ }
79
+ return void 0;
68
80
  }
69
81
  entries() {
70
82
  return this.routes.entries();
71
83
  }
72
84
  has(key) {
73
- return this.routes.has(key);
85
+ return this.get(key) !== void 0;
74
86
  }
75
87
  get size() {
76
88
  return this.routes.size;
77
89
  }
78
90
  validate(expectedKeys) {
79
91
  if (!expectedKeys) return;
80
- const missing = expectedKeys.filter((k) => !this.routes.has(k));
92
+ const registeredPathKeys = new Set([...this.routes.values()].map((e) => e.key));
93
+ const missing = expectedKeys.filter((k) => !registeredPathKeys.has(k));
81
94
  if (missing.length > 0) {
82
95
  throw new Error(
83
96
  `route${missing.length > 1 ? "s" : ""} ${missing.map((k) => `'${k}'`).join(", ")} in prices map but not registered \u2014 add to barrel imports`
@@ -1436,8 +1449,8 @@ function createWellKnownHandler(registry, baseUrl, pricesKeys, discovery) {
1436
1449
  const x402Set = /* @__PURE__ */ new Set();
1437
1450
  const mppSet = /* @__PURE__ */ new Set();
1438
1451
  const methodHints = discovery.methodHints ?? "non-default";
1439
- for (const [key, entry] of registry.entries()) {
1440
- const url = `${normalizedBase}/api/${entry.path ?? key}`;
1452
+ for (const [, entry] of registry.entries()) {
1453
+ const url = `${normalizedBase}/api/${entry.path ?? entry.key}`;
1441
1454
  const resource = toDiscoveryResource(entry.method, url, methodHints);
1442
1455
  if (entry.authMode !== "unprotected") x402Set.add(resource);
1443
1456
  if (entry.protocols.includes("mpp")) mppSet.add(resource);
@@ -1493,12 +1506,12 @@ function createOpenAPIHandler(registry, baseUrl, pricesKeys, discovery) {
1493
1506
  const tagSet = /* @__PURE__ */ new Set();
1494
1507
  let requiresSiwxScheme = false;
1495
1508
  let requiresApiKeyScheme = false;
1496
- for (const [key, entry] of registry.entries()) {
1497
- const apiPath = `/api/${entry.path ?? key}`;
1509
+ for (const [, entry] of registry.entries()) {
1510
+ const apiPath = `/api/${entry.path ?? entry.key}`;
1498
1511
  const method = entry.method.toLowerCase();
1499
- const tag = deriveTag(key);
1512
+ const tag = deriveTag(entry.key);
1500
1513
  tagSet.add(tag);
1501
- const built = buildOperation(key, entry, tag);
1514
+ const built = buildOperation(entry.key, entry, tag);
1502
1515
  if (built.requiresSiwxScheme) requiresSiwxScheme = true;
1503
1516
  if (built.requiresApiKeyScheme) requiresApiKeyScheme = true;
1504
1517
  paths[apiPath] = { ...paths[apiPath], [method]: built.operation };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/router",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Unified route builder for Next.js App Router APIs with x402, MPP, SIWX, and API key auth",
5
5
  "type": "module",
6
6
  "exports": {