@apicity/cost 0.2.0-alpha.1 → 0.2.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.
@@ -0,0 +1,91 @@
1
+ import { dispatchWithPaidGate, createReplayStore, } from "./paygate.js";
2
+ import { isPaidEndpoint } from "./paid-endpoints.js";
3
+ /**
4
+ * HTTP-method roots that the walker will descend into. Properties outside
5
+ * this allowlist (sub-providers like `kie.veo`, schema collections like
6
+ * `kie.modelInputSchemas`, attached helpers like `kie.examples`) are
7
+ * returned by reference and never wrapped.
8
+ */
9
+ const DEFAULT_ROOTS = ["post", "get", "delete", "patch", "put"];
10
+ /**
11
+ * Walk a provider tree once and route every paid-endpoint leaf through
12
+ * `dispatchWithPaidGate`. Free leaves are returned untouched. Callable
13
+ * namespaces (functions with child properties — e.g. `xai.v1.models` is a
14
+ * function with `.languageModels` children) preserve all children and their
15
+ * `.schema` attachments.
16
+ *
17
+ * @param providerName Provider identifier matching `PAID_ENDPOINTS`.
18
+ * @param tree The provider object returned by the factory.
19
+ * @param opts Optional roots allowlist and IO injection.
20
+ * @returns A new tree of the same shape; paid leaves accept `(req, approval?)`.
21
+ */
22
+ export function withPaidGate(providerName, tree, opts) {
23
+ const roots = opts?.roots ?? DEFAULT_ROOTS;
24
+ // Normalize the config once so every gated dispatch on this provider instance
25
+ // shares a single replay store (single-use OTPs must be remembered across
26
+ // calls). A custom store passed by the code client is preserved.
27
+ const config = opts?.config
28
+ ? {
29
+ ...opts.config,
30
+ replayStore: opts.config.replayStore ?? createReplayStore(),
31
+ }
32
+ : undefined;
33
+ const out = {};
34
+ for (const [key, value] of Object.entries(tree)) {
35
+ if (roots.includes(key) && value && typeof value === "object") {
36
+ out[key] = walk(providerName, [key], value, config);
37
+ }
38
+ else {
39
+ out[key] = value;
40
+ }
41
+ }
42
+ return out;
43
+ }
44
+ function walk(providerName, path, node, config) {
45
+ if (typeof node === "function") {
46
+ return wrapCallable(providerName, path, node, config);
47
+ }
48
+ if (node && typeof node === "object" && !Array.isArray(node)) {
49
+ const out = {};
50
+ for (const [k, v] of Object.entries(node)) {
51
+ out[k] = walk(providerName, [...path, k], v, config);
52
+ }
53
+ return out;
54
+ }
55
+ return node;
56
+ }
57
+ /**
58
+ * Wrap a callable leaf. If the path resolves to a paid endpoint, the returned
59
+ * function routes through `dispatchWithPaidGate`. Otherwise the original
60
+ * function is returned. Either way, any properties hanging off the function
61
+ * (`.schema`, callable-namespace children) are preserved and recursively
62
+ * walked so nested paid leaves are also gated.
63
+ */
64
+ function wrapCallable(providerName, path, fn, config) {
65
+ const method = path[0].toUpperCase();
66
+ const dotPath = path.slice(1).join(".");
67
+ const paid = dotPath.length > 0 && isPaidEndpoint(providerName, method, dotPath);
68
+ const base = paid
69
+ ? (...args) => {
70
+ const [req, approval] = args;
71
+ return dispatchWithPaidGate(providerName, method, dotPath, req, approval, () => fn(req), config);
72
+ }
73
+ : fn;
74
+ // For callable namespaces (functions with own properties), only recurse into
75
+ // function-typed children — those are sub-endpoints that may themselves be
76
+ // paid. Non-function props (`.schema`, other metadata) are preserved by
77
+ // reference so test assertions and runtime callers can compare by identity.
78
+ const children = {};
79
+ for (const [k, v] of Object.entries(fn)) {
80
+ if (typeof v === "function") {
81
+ children[k] = walk(providerName, [...path, k], v, config);
82
+ }
83
+ else {
84
+ children[k] = v;
85
+ }
86
+ }
87
+ // If `base === fn` we still merge children, but they're already attached;
88
+ // the assignment is a no-op for own enumerable keys.
89
+ return Object.assign(base, children);
90
+ }
91
+ //# sourceMappingURL=with-paid-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-paid-gate.js","sourceRoot":"","sources":["../../src/with-paid-gate.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apicity/cost",
3
- "version": "0.2.0-alpha.1",
3
+ "version": "0.2.1",
4
4
  "description": "Pure-table cost & token estimation across @apicity providers — zero network calls, all rates bundled.",
5
5
  "license": "MIT",
6
6
  "repository": {