@outray/runtime-shared 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +4 -2
- package/dist/buffered-logger.cjs +102 -0
- package/dist/buffered-logger.cjs.map +1 -0
- package/dist/buffered-logger.d.cts +26 -0
- package/dist/buffered-logger.d.ts +26 -0
- package/dist/buffered-logger.js +77 -0
- package/dist/buffered-logger.js.map +1 -0
- package/dist/chunk-7Z55KL36.js +1111 -0
- package/dist/chunk-7Z55KL36.js.map +1 -0
- package/dist/chunk-EBXUVSOO.js +37 -0
- package/dist/chunk-EBXUVSOO.js.map +1 -0
- package/dist/chunk-XAMVXW6B.js +19 -0
- package/dist/chunk-XAMVXW6B.js.map +1 -0
- package/dist/durable-queue.cjs +316 -0
- package/dist/durable-queue.cjs.map +1 -0
- package/dist/durable-queue.d.cts +41 -0
- package/dist/durable-queue.d.ts +41 -0
- package/dist/durable-queue.js +290 -0
- package/dist/durable-queue.js.map +1 -0
- package/dist/helpers.cjs +1167 -0
- package/dist/helpers.cjs.map +1 -0
- package/dist/helpers.d.cts +6 -0
- package/dist/helpers.d.ts +6 -0
- package/dist/helpers.js +14 -0
- package/dist/helpers.js.map +1 -0
- package/dist/postgres-ssl.cjs +37 -0
- package/dist/postgres-ssl.cjs.map +1 -0
- package/dist/postgres-ssl.d.cts +9 -0
- package/dist/postgres-ssl.d.ts +9 -0
- package/dist/postgres-ssl.js +12 -0
- package/dist/postgres-ssl.js.map +1 -0
- package/dist/protocol.cjs +38 -0
- package/dist/protocol.cjs.map +1 -0
- package/dist/protocol.d.cts +8 -0
- package/dist/protocol.d.ts +8 -0
- package/dist/protocol.js +13 -0
- package/dist/protocol.js.map +1 -0
- package/dist/public-hosts.cjs +116 -0
- package/dist/public-hosts.cjs.map +1 -0
- package/dist/public-hosts.d.cts +29 -0
- package/dist/public-hosts.d.ts +29 -0
- package/dist/public-hosts.js +82 -0
- package/dist/public-hosts.js.map +1 -0
- package/dist/tinybird-records.cjs +160 -0
- package/dist/tinybird-records.cjs.map +1 -0
- package/dist/tinybird-records.d.cts +62 -0
- package/dist/tinybird-records.d.ts +62 -0
- package/dist/tinybird-records.js +129 -0
- package/dist/tinybird-records.js.map +1 -0
- package/dist/tunnel-accounting.cjs +71 -0
- package/dist/tunnel-accounting.cjs.map +1 -0
- package/dist/tunnel-accounting.d.cts +12 -0
- package/dist/tunnel-accounting.d.ts +12 -0
- package/dist/tunnel-accounting.js +32 -0
- package/dist/tunnel-accounting.js.map +1 -0
- package/dist/tunnel-product-usage.cjs +107 -0
- package/dist/tunnel-product-usage.cjs.map +1 -0
- package/dist/tunnel-product-usage.d.cts +26 -0
- package/dist/tunnel-product-usage.d.ts +26 -0
- package/dist/tunnel-product-usage.js +81 -0
- package/dist/tunnel-product-usage.js.map +1 -0
- package/dist/types.cjs +19 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +105 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/usage-writer.cjs +43 -0
- package/dist/usage-writer.cjs.map +1 -0
- package/dist/usage-writer.d.cts +14 -0
- package/dist/usage-writer.d.ts +14 -0
- package/dist/usage-writer.js +7 -0
- package/dist/usage-writer.js.map +1 -0
- package/dist/utils.cjs +1171 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.cts +2 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +19 -0
- package/dist/utils.js.map +1 -0
- package/dist/words.cjs +1136 -0
- package/dist/words.cjs.map +1 -0
- package/dist/words.d.cts +4 -0
- package/dist/words.d.ts +4 -0
- package/dist/words.js +9 -0
- package/dist/words.js.map +1 -0
- package/package.json +99 -4
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/tunnel-product-usage.ts
|
|
21
|
+
var tunnel_product_usage_exports = {};
|
|
22
|
+
__export(tunnel_product_usage_exports, {
|
|
23
|
+
TunnelUsageBuffer: () => TunnelUsageBuffer,
|
|
24
|
+
default: () => tunnel_product_usage_default,
|
|
25
|
+
tunnelTrafficDeltas: () => tunnelTrafficDeltas
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(tunnel_product_usage_exports);
|
|
28
|
+
var import_node_crypto = require("crypto");
|
|
29
|
+
function tunnelTrafficDeltas(source, record) {
|
|
30
|
+
const deltas = {};
|
|
31
|
+
const add = (metric, value) => {
|
|
32
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value > 0) deltas[metric] = String(value);
|
|
33
|
+
};
|
|
34
|
+
if (source === "tunnel_events") {
|
|
35
|
+
deltas["tunnels.http_requests"] = "1";
|
|
36
|
+
add("tunnels.bytes.http.in", record.bytes_in);
|
|
37
|
+
add("tunnels.bytes.http.out", record.bytes_out);
|
|
38
|
+
} else if (source === "tunnel_protocol_events" && ["tcp", "udp"].includes(String(record.protocol))) {
|
|
39
|
+
if (record.protocol === "tcp") {
|
|
40
|
+
if (record.event_type === "connection") deltas["tunnels.tcp_connections"] = "1";
|
|
41
|
+
if (record.event_type === "data") {
|
|
42
|
+
add("tunnels.bytes.tcp.in", record.bytes_in);
|
|
43
|
+
add("tunnels.bytes.tcp.out", record.bytes_out);
|
|
44
|
+
}
|
|
45
|
+
} else if (record.event_type === "packet") {
|
|
46
|
+
const direction = record.direction === "in" || record.direction === "out" ? record.direction : Number(record.bytes_in) > 0 ? "in" : "out";
|
|
47
|
+
deltas[`tunnels.udp_datagrams.${direction}`] = "1";
|
|
48
|
+
add("tunnels.bytes.udp.in", record.bytes_in);
|
|
49
|
+
add("tunnels.bytes.udp.out", record.bytes_out);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return deltas;
|
|
53
|
+
}
|
|
54
|
+
var TunnelUsageBuffer = class {
|
|
55
|
+
constructor(queue, maxBuckets = 2e3) {
|
|
56
|
+
this.queue = queue;
|
|
57
|
+
this.maxBuckets = maxBuckets;
|
|
58
|
+
}
|
|
59
|
+
queue;
|
|
60
|
+
maxBuckets;
|
|
61
|
+
pending = /* @__PURE__ */ new Map();
|
|
62
|
+
sealed = [];
|
|
63
|
+
flushing = null;
|
|
64
|
+
log(organizationId, timestamp, deltas) {
|
|
65
|
+
if (!organizationId || !Object.keys(deltas).length) return;
|
|
66
|
+
const date = new Date(timestamp);
|
|
67
|
+
if (!Number.isFinite(date.getTime())) throw new Error("Invalid tunnel usage timestamp");
|
|
68
|
+
const day = date.toISOString().slice(0, 10);
|
|
69
|
+
const key = `${organizationId}:${day}`;
|
|
70
|
+
let bucket = this.pending.get(key);
|
|
71
|
+
if (!bucket) {
|
|
72
|
+
if (this.pending.size + this.sealed.length >= this.maxBuckets) throw new Error("Tunnel usage buffer is full");
|
|
73
|
+
bucket = { organizationId, source: "tunnel-traffic", id: (0, import_node_crypto.randomUUID)(), timestamp: `${day}T00:00:00.000Z`, deltas: {} };
|
|
74
|
+
}
|
|
75
|
+
const nextDeltas = { ...bucket.deltas };
|
|
76
|
+
for (const [metric, amount] of Object.entries(deltas)) {
|
|
77
|
+
if (!/^\d+$/.test(String(amount))) throw new Error("Invalid tunnel usage counter");
|
|
78
|
+
const next = BigInt(amount) + BigInt(bucket.deltas[metric] || "0");
|
|
79
|
+
if (next < 0n || next > 9223372036854775807n) throw new Error("Tunnel usage counter overflow");
|
|
80
|
+
nextDeltas[metric] = String(next);
|
|
81
|
+
}
|
|
82
|
+
bucket.deltas = nextDeltas;
|
|
83
|
+
this.pending.set(key, bucket);
|
|
84
|
+
}
|
|
85
|
+
flush() {
|
|
86
|
+
if (this.flushing) return this.flushing;
|
|
87
|
+
this.flushing = (async () => {
|
|
88
|
+
if (!this.sealed.length) {
|
|
89
|
+
this.sealed = [...this.pending.values()];
|
|
90
|
+
this.pending.clear();
|
|
91
|
+
}
|
|
92
|
+
if (!this.sealed.length) return;
|
|
93
|
+
await this.queue.enqueue(this.sealed);
|
|
94
|
+
this.sealed = [];
|
|
95
|
+
})().finally(() => {
|
|
96
|
+
this.flushing = null;
|
|
97
|
+
});
|
|
98
|
+
return this.flushing;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
var tunnel_product_usage_default = { tunnelTrafficDeltas, TunnelUsageBuffer };
|
|
102
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
103
|
+
0 && (module.exports = {
|
|
104
|
+
TunnelUsageBuffer,
|
|
105
|
+
tunnelTrafficDeltas
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=tunnel-product-usage.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tunnel-product-usage.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { UsageEvent, UsageMetric } from \"./tunnel-accounting\";\n\n/** Project measured counters only. Never queue tunnel addresses or payloads. */\nexport function tunnelTrafficDeltas(source: string, record: Record<string, unknown>): Partial<Record<UsageMetric, string>> {\n const deltas: Partial<Record<UsageMetric, string>> = {};\n const add = (metric: UsageMetric, value: unknown) => {\n if (typeof value === \"number\" && Number.isSafeInteger(value) && value > 0) deltas[metric] = String(value);\n };\n if (source === \"tunnel_events\") {\n deltas[\"tunnels.http_requests\"] = \"1\";\n add(\"tunnels.bytes.http.in\", record.bytes_in); add(\"tunnels.bytes.http.out\", record.bytes_out);\n } else if (source === \"tunnel_protocol_events\" && [\"tcp\", \"udp\"].includes(String(record.protocol))) {\n if (record.protocol === \"tcp\") {\n if (record.event_type === \"connection\") deltas[\"tunnels.tcp_connections\"] = \"1\";\n if (record.event_type === \"data\") { add(\"tunnels.bytes.tcp.in\", record.bytes_in); add(\"tunnels.bytes.tcp.out\", record.bytes_out); }\n } else if (record.event_type === \"packet\") {\n const direction = record.direction === \"in\" || record.direction === \"out\" ? record.direction : Number(record.bytes_in) > 0 ? \"in\" : \"out\";\n deltas[`tunnels.udp_datagrams.${direction}`] = \"1\";\n add(\"tunnels.bytes.udp.in\", record.bytes_in); add(\"tunnels.bytes.udp.out\", record.bytes_out);\n }\n }\n return deltas;\n}\n\nexport interface UsageQueue { enqueue(events: UsageEvent[]): Promise<void> }\n/** A failed enqueue retains its sealed IDs. Concurrent logging goes into a new\n * bucket; retrying an ambiguous Redis acknowledgement cannot create new IDs. */\nexport class TunnelUsageBuffer {\n private pending = new Map<string, UsageEvent>();\n private sealed: UsageEvent[] = [];\n private flushing: Promise<void> | null = null;\n constructor(private queue: UsageQueue, private maxBuckets = 2_000) {}\n log(organizationId: string, timestamp: string, deltas: UsageEvent[\"deltas\"]) {\n if (!organizationId || !Object.keys(deltas).length) return;\n const date = new Date(timestamp);\n if (!Number.isFinite(date.getTime())) throw new Error(\"Invalid tunnel usage timestamp\");\n const day = date.toISOString().slice(0, 10);\n const key = `${organizationId}:${day}`;\n let bucket = this.pending.get(key);\n if (!bucket) {\n if (this.pending.size + this.sealed.length >= this.maxBuckets) throw new Error(\"Tunnel usage buffer is full\");\n bucket = { organizationId, source: \"tunnel-traffic\", id: randomUUID(), timestamp: `${day}T00:00:00.000Z`, deltas: {} };\n }\n const nextDeltas = { ...bucket.deltas };\n for (const [metric, amount] of Object.entries(deltas)) {\n if (!/^\\d+$/.test(String(amount))) throw new Error(\"Invalid tunnel usage counter\");\n const next = BigInt(amount!) + BigInt(bucket.deltas[metric as UsageMetric] || \"0\");\n if (next < 0n || next > 9223372036854775807n) throw new Error(\"Tunnel usage counter overflow\");\n nextDeltas[metric as UsageMetric] = String(next);\n }\n bucket.deltas = nextDeltas;\n this.pending.set(key, bucket);\n }\n flush(): Promise<void> {\n if (this.flushing) return this.flushing;\n this.flushing = (async () => {\n if (!this.sealed.length) { this.sealed = [...this.pending.values()]; this.pending.clear(); }\n if (!this.sealed.length) return;\n await this.queue.enqueue(this.sealed);\n this.sealed = [];\n })().finally(() => { this.flushing = null; });\n return this.flushing;\n }\n}\nexport default { tunnelTrafficDeltas, TunnelUsageBuffer };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAA2B;AAIpB,SAAS,oBAAoB,QAAgB,QAAuE;AACzH,QAAM,SAA+C,CAAC;AACtD,QAAM,MAAM,CAAC,QAAqB,UAAmB;AACnD,QAAI,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK,KAAK,QAAQ,EAAG,QAAO,MAAM,IAAI,OAAO,KAAK;AAAA,EAC1G;AACA,MAAI,WAAW,iBAAiB;AAC9B,WAAO,uBAAuB,IAAI;AAClC,QAAI,yBAAyB,OAAO,QAAQ;AAAG,QAAI,0BAA0B,OAAO,SAAS;AAAA,EAC/F,WAAW,WAAW,4BAA4B,CAAC,OAAO,KAAK,EAAE,SAAS,OAAO,OAAO,QAAQ,CAAC,GAAG;AAClG,QAAI,OAAO,aAAa,OAAO;AAC7B,UAAI,OAAO,eAAe,aAAc,QAAO,yBAAyB,IAAI;AAC5E,UAAI,OAAO,eAAe,QAAQ;AAAE,YAAI,wBAAwB,OAAO,QAAQ;AAAG,YAAI,yBAAyB,OAAO,SAAS;AAAA,MAAG;AAAA,IACpI,WAAW,OAAO,eAAe,UAAU;AACzC,YAAM,YAAY,OAAO,cAAc,QAAQ,OAAO,cAAc,QAAQ,OAAO,YAAY,OAAO,OAAO,QAAQ,IAAI,IAAI,OAAO;AACpI,aAAO,yBAAyB,SAAS,EAAE,IAAI;AAC/C,UAAI,wBAAwB,OAAO,QAAQ;AAAG,UAAI,yBAAyB,OAAO,SAAS;AAAA,IAC7F;AAAA,EACF;AACA,SAAO;AACT;AAKO,IAAM,oBAAN,MAAwB;AAAA,EAI7B,YAAoB,OAA2B,aAAa,KAAO;AAA/C;AAA2B;AAAA,EAAqB;AAAA,EAAhD;AAAA,EAA2B;AAAA,EAHvC,UAAU,oBAAI,IAAwB;AAAA,EACtC,SAAuB,CAAC;AAAA,EACxB,WAAiC;AAAA,EAEzC,IAAI,gBAAwB,WAAmB,QAA8B;AAC3E,QAAI,CAAC,kBAAkB,CAAC,OAAO,KAAK,MAAM,EAAE,OAAQ;AACpD,UAAM,OAAO,IAAI,KAAK,SAAS;AAC/B,QAAI,CAAC,OAAO,SAAS,KAAK,QAAQ,CAAC,EAAG,OAAM,IAAI,MAAM,gCAAgC;AACtF,UAAM,MAAM,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE;AAC1C,UAAM,MAAM,GAAG,cAAc,IAAI,GAAG;AACpC,QAAI,SAAS,KAAK,QAAQ,IAAI,GAAG;AACjC,QAAI,CAAC,QAAQ;AACX,UAAI,KAAK,QAAQ,OAAO,KAAK,OAAO,UAAU,KAAK,WAAY,OAAM,IAAI,MAAM,6BAA6B;AAC5G,eAAS,EAAE,gBAAgB,QAAQ,kBAAkB,QAAI,+BAAW,GAAG,WAAW,GAAG,GAAG,kBAAkB,QAAQ,CAAC,EAAE;AAAA,IACvH;AACA,UAAM,aAAa,EAAE,GAAG,OAAO,OAAO;AACtC,eAAW,CAAC,QAAQ,MAAM,KAAK,OAAO,QAAQ,MAAM,GAAG;AACrD,UAAI,CAAC,QAAQ,KAAK,OAAO,MAAM,CAAC,EAAG,OAAM,IAAI,MAAM,8BAA8B;AACjF,YAAM,OAAO,OAAO,MAAO,IAAI,OAAO,OAAO,OAAO,MAAqB,KAAK,GAAG;AACjF,UAAI,OAAO,MAAM,OAAO,qBAAsB,OAAM,IAAI,MAAM,+BAA+B;AAC7F,iBAAW,MAAqB,IAAI,OAAO,IAAI;AAAA,IACjD;AACA,WAAO,SAAS;AAChB,SAAK,QAAQ,IAAI,KAAK,MAAM;AAAA,EAC9B;AAAA,EACA,QAAuB;AACrB,QAAI,KAAK,SAAU,QAAO,KAAK;AAC/B,SAAK,YAAY,YAAY;AAC3B,UAAI,CAAC,KAAK,OAAO,QAAQ;AAAE,aAAK,SAAS,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC;AAAG,aAAK,QAAQ,MAAM;AAAA,MAAG;AAC3F,UAAI,CAAC,KAAK,OAAO,OAAQ;AACzB,YAAM,KAAK,MAAM,QAAQ,KAAK,MAAM;AACpC,WAAK,SAAS,CAAC;AAAA,IACjB,GAAG,EAAE,QAAQ,MAAM;AAAE,WAAK,WAAW;AAAA,IAAM,CAAC;AAC5C,WAAO,KAAK;AAAA,EACd;AACF;AACA,IAAO,+BAAQ,EAAE,qBAAqB,kBAAkB;","names":[]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { UsageEvent, UsageMetric } from './tunnel-accounting.cjs';
|
|
2
|
+
import './usage-writer.cjs';
|
|
3
|
+
|
|
4
|
+
/** Project measured counters only. Never queue tunnel addresses or payloads. */
|
|
5
|
+
declare function tunnelTrafficDeltas(source: string, record: Record<string, unknown>): Partial<Record<UsageMetric, string>>;
|
|
6
|
+
interface UsageQueue {
|
|
7
|
+
enqueue(events: UsageEvent[]): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/** A failed enqueue retains its sealed IDs. Concurrent logging goes into a new
|
|
10
|
+
* bucket; retrying an ambiguous Redis acknowledgement cannot create new IDs. */
|
|
11
|
+
declare class TunnelUsageBuffer {
|
|
12
|
+
private queue;
|
|
13
|
+
private maxBuckets;
|
|
14
|
+
private pending;
|
|
15
|
+
private sealed;
|
|
16
|
+
private flushing;
|
|
17
|
+
constructor(queue: UsageQueue, maxBuckets?: number);
|
|
18
|
+
log(organizationId: string, timestamp: string, deltas: UsageEvent["deltas"]): void;
|
|
19
|
+
flush(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
declare const _default: {
|
|
22
|
+
tunnelTrafficDeltas: typeof tunnelTrafficDeltas;
|
|
23
|
+
TunnelUsageBuffer: typeof TunnelUsageBuffer;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export { TunnelUsageBuffer, type UsageQueue, _default as default, tunnelTrafficDeltas };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { UsageEvent, UsageMetric } from './tunnel-accounting.js';
|
|
2
|
+
import './usage-writer.js';
|
|
3
|
+
|
|
4
|
+
/** Project measured counters only. Never queue tunnel addresses or payloads. */
|
|
5
|
+
declare function tunnelTrafficDeltas(source: string, record: Record<string, unknown>): Partial<Record<UsageMetric, string>>;
|
|
6
|
+
interface UsageQueue {
|
|
7
|
+
enqueue(events: UsageEvent[]): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/** A failed enqueue retains its sealed IDs. Concurrent logging goes into a new
|
|
10
|
+
* bucket; retrying an ambiguous Redis acknowledgement cannot create new IDs. */
|
|
11
|
+
declare class TunnelUsageBuffer {
|
|
12
|
+
private queue;
|
|
13
|
+
private maxBuckets;
|
|
14
|
+
private pending;
|
|
15
|
+
private sealed;
|
|
16
|
+
private flushing;
|
|
17
|
+
constructor(queue: UsageQueue, maxBuckets?: number);
|
|
18
|
+
log(organizationId: string, timestamp: string, deltas: UsageEvent["deltas"]): void;
|
|
19
|
+
flush(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
declare const _default: {
|
|
22
|
+
tunnelTrafficDeltas: typeof tunnelTrafficDeltas;
|
|
23
|
+
TunnelUsageBuffer: typeof TunnelUsageBuffer;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export { TunnelUsageBuffer, type UsageQueue, _default as default, tunnelTrafficDeltas };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/tunnel-product-usage.ts
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
function tunnelTrafficDeltas(source, record) {
|
|
4
|
+
const deltas = {};
|
|
5
|
+
const add = (metric, value) => {
|
|
6
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value > 0) deltas[metric] = String(value);
|
|
7
|
+
};
|
|
8
|
+
if (source === "tunnel_events") {
|
|
9
|
+
deltas["tunnels.http_requests"] = "1";
|
|
10
|
+
add("tunnels.bytes.http.in", record.bytes_in);
|
|
11
|
+
add("tunnels.bytes.http.out", record.bytes_out);
|
|
12
|
+
} else if (source === "tunnel_protocol_events" && ["tcp", "udp"].includes(String(record.protocol))) {
|
|
13
|
+
if (record.protocol === "tcp") {
|
|
14
|
+
if (record.event_type === "connection") deltas["tunnels.tcp_connections"] = "1";
|
|
15
|
+
if (record.event_type === "data") {
|
|
16
|
+
add("tunnels.bytes.tcp.in", record.bytes_in);
|
|
17
|
+
add("tunnels.bytes.tcp.out", record.bytes_out);
|
|
18
|
+
}
|
|
19
|
+
} else if (record.event_type === "packet") {
|
|
20
|
+
const direction = record.direction === "in" || record.direction === "out" ? record.direction : Number(record.bytes_in) > 0 ? "in" : "out";
|
|
21
|
+
deltas[`tunnels.udp_datagrams.${direction}`] = "1";
|
|
22
|
+
add("tunnels.bytes.udp.in", record.bytes_in);
|
|
23
|
+
add("tunnels.bytes.udp.out", record.bytes_out);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return deltas;
|
|
27
|
+
}
|
|
28
|
+
var TunnelUsageBuffer = class {
|
|
29
|
+
constructor(queue, maxBuckets = 2e3) {
|
|
30
|
+
this.queue = queue;
|
|
31
|
+
this.maxBuckets = maxBuckets;
|
|
32
|
+
}
|
|
33
|
+
queue;
|
|
34
|
+
maxBuckets;
|
|
35
|
+
pending = /* @__PURE__ */ new Map();
|
|
36
|
+
sealed = [];
|
|
37
|
+
flushing = null;
|
|
38
|
+
log(organizationId, timestamp, deltas) {
|
|
39
|
+
if (!organizationId || !Object.keys(deltas).length) return;
|
|
40
|
+
const date = new Date(timestamp);
|
|
41
|
+
if (!Number.isFinite(date.getTime())) throw new Error("Invalid tunnel usage timestamp");
|
|
42
|
+
const day = date.toISOString().slice(0, 10);
|
|
43
|
+
const key = `${organizationId}:${day}`;
|
|
44
|
+
let bucket = this.pending.get(key);
|
|
45
|
+
if (!bucket) {
|
|
46
|
+
if (this.pending.size + this.sealed.length >= this.maxBuckets) throw new Error("Tunnel usage buffer is full");
|
|
47
|
+
bucket = { organizationId, source: "tunnel-traffic", id: randomUUID(), timestamp: `${day}T00:00:00.000Z`, deltas: {} };
|
|
48
|
+
}
|
|
49
|
+
const nextDeltas = { ...bucket.deltas };
|
|
50
|
+
for (const [metric, amount] of Object.entries(deltas)) {
|
|
51
|
+
if (!/^\d+$/.test(String(amount))) throw new Error("Invalid tunnel usage counter");
|
|
52
|
+
const next = BigInt(amount) + BigInt(bucket.deltas[metric] || "0");
|
|
53
|
+
if (next < 0n || next > 9223372036854775807n) throw new Error("Tunnel usage counter overflow");
|
|
54
|
+
nextDeltas[metric] = String(next);
|
|
55
|
+
}
|
|
56
|
+
bucket.deltas = nextDeltas;
|
|
57
|
+
this.pending.set(key, bucket);
|
|
58
|
+
}
|
|
59
|
+
flush() {
|
|
60
|
+
if (this.flushing) return this.flushing;
|
|
61
|
+
this.flushing = (async () => {
|
|
62
|
+
if (!this.sealed.length) {
|
|
63
|
+
this.sealed = [...this.pending.values()];
|
|
64
|
+
this.pending.clear();
|
|
65
|
+
}
|
|
66
|
+
if (!this.sealed.length) return;
|
|
67
|
+
await this.queue.enqueue(this.sealed);
|
|
68
|
+
this.sealed = [];
|
|
69
|
+
})().finally(() => {
|
|
70
|
+
this.flushing = null;
|
|
71
|
+
});
|
|
72
|
+
return this.flushing;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var tunnel_product_usage_default = { tunnelTrafficDeltas, TunnelUsageBuffer };
|
|
76
|
+
export {
|
|
77
|
+
TunnelUsageBuffer,
|
|
78
|
+
tunnel_product_usage_default as default,
|
|
79
|
+
tunnelTrafficDeltas
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=tunnel-product-usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tunnel-product-usage.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { UsageEvent, UsageMetric } from \"./tunnel-accounting\";\n\n/** Project measured counters only. Never queue tunnel addresses or payloads. */\nexport function tunnelTrafficDeltas(source: string, record: Record<string, unknown>): Partial<Record<UsageMetric, string>> {\n const deltas: Partial<Record<UsageMetric, string>> = {};\n const add = (metric: UsageMetric, value: unknown) => {\n if (typeof value === \"number\" && Number.isSafeInteger(value) && value > 0) deltas[metric] = String(value);\n };\n if (source === \"tunnel_events\") {\n deltas[\"tunnels.http_requests\"] = \"1\";\n add(\"tunnels.bytes.http.in\", record.bytes_in); add(\"tunnels.bytes.http.out\", record.bytes_out);\n } else if (source === \"tunnel_protocol_events\" && [\"tcp\", \"udp\"].includes(String(record.protocol))) {\n if (record.protocol === \"tcp\") {\n if (record.event_type === \"connection\") deltas[\"tunnels.tcp_connections\"] = \"1\";\n if (record.event_type === \"data\") { add(\"tunnels.bytes.tcp.in\", record.bytes_in); add(\"tunnels.bytes.tcp.out\", record.bytes_out); }\n } else if (record.event_type === \"packet\") {\n const direction = record.direction === \"in\" || record.direction === \"out\" ? record.direction : Number(record.bytes_in) > 0 ? \"in\" : \"out\";\n deltas[`tunnels.udp_datagrams.${direction}`] = \"1\";\n add(\"tunnels.bytes.udp.in\", record.bytes_in); add(\"tunnels.bytes.udp.out\", record.bytes_out);\n }\n }\n return deltas;\n}\n\nexport interface UsageQueue { enqueue(events: UsageEvent[]): Promise<void> }\n/** A failed enqueue retains its sealed IDs. Concurrent logging goes into a new\n * bucket; retrying an ambiguous Redis acknowledgement cannot create new IDs. */\nexport class TunnelUsageBuffer {\n private pending = new Map<string, UsageEvent>();\n private sealed: UsageEvent[] = [];\n private flushing: Promise<void> | null = null;\n constructor(private queue: UsageQueue, private maxBuckets = 2_000) {}\n log(organizationId: string, timestamp: string, deltas: UsageEvent[\"deltas\"]) {\n if (!organizationId || !Object.keys(deltas).length) return;\n const date = new Date(timestamp);\n if (!Number.isFinite(date.getTime())) throw new Error(\"Invalid tunnel usage timestamp\");\n const day = date.toISOString().slice(0, 10);\n const key = `${organizationId}:${day}`;\n let bucket = this.pending.get(key);\n if (!bucket) {\n if (this.pending.size + this.sealed.length >= this.maxBuckets) throw new Error(\"Tunnel usage buffer is full\");\n bucket = { organizationId, source: \"tunnel-traffic\", id: randomUUID(), timestamp: `${day}T00:00:00.000Z`, deltas: {} };\n }\n const nextDeltas = { ...bucket.deltas };\n for (const [metric, amount] of Object.entries(deltas)) {\n if (!/^\\d+$/.test(String(amount))) throw new Error(\"Invalid tunnel usage counter\");\n const next = BigInt(amount!) + BigInt(bucket.deltas[metric as UsageMetric] || \"0\");\n if (next < 0n || next > 9223372036854775807n) throw new Error(\"Tunnel usage counter overflow\");\n nextDeltas[metric as UsageMetric] = String(next);\n }\n bucket.deltas = nextDeltas;\n this.pending.set(key, bucket);\n }\n flush(): Promise<void> {\n if (this.flushing) return this.flushing;\n this.flushing = (async () => {\n if (!this.sealed.length) { this.sealed = [...this.pending.values()]; this.pending.clear(); }\n if (!this.sealed.length) return;\n await this.queue.enqueue(this.sealed);\n this.sealed = [];\n })().finally(() => { this.flushing = null; });\n return this.flushing;\n }\n}\nexport default { tunnelTrafficDeltas, TunnelUsageBuffer };\n"],"mappings":"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","names":[]}
|
package/dist/types.cjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// src/types.ts
|
|
17
|
+
var types_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(types_exports);
|
|
19
|
+
//# sourceMappingURL=types.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["export type TunnelProtocol = \"http\" | \"tcp\" | \"udp\";\n\nexport interface HelloMessage {\n type: \"hello\";\n clientId: string;\n version: string;\n}\n\nexport interface OpenTunnelMessage {\n type: \"open_tunnel\";\n subdomain?: string;\n customDomain?: string;\n apiKey?: string;\n forceTakeover?: boolean;\n protocol?: TunnelProtocol;\n remotePort?: number; // For TCP/UDP: the port to expose on the server\n password?: string;\n}\n\nexport interface TunnelOpenedMessage {\n type: \"tunnel_opened\";\n tunnelId: string;\n url: string;\n plan?: string;\n protocol?: TunnelProtocol;\n port?: number; // For TCP/UDP: the assigned port on the server\n}\n\n// TCP/UDP specific messages\nexport interface TCPConnectionMessage {\n type: \"tcp_connection\";\n connectionId: string;\n}\n\nexport interface TCPDataMessage {\n type: \"tcp_data\";\n connectionId: string;\n data: string; // base64 encoded\n}\n\nexport interface TCPCloseMessage {\n type: \"tcp_close\";\n connectionId: string;\n}\n\nexport interface UDPDataMessage {\n type: \"udp_data\";\n packetId: string;\n sourceAddress: string;\n sourcePort: number;\n data: string; // base64 encoded\n}\n\n// WebSocket passthrough messages\nexport interface WSUpgradeMessage {\n type: \"ws_upgrade\";\n wsConnectionId: string;\n path: string;\n headers: Record<string, string | string[]>;\n protocol?: string; // Sec-WebSocket-Protocol\n}\n\nexport interface WSUpgradeResponseMessage {\n type: \"ws_upgrade_response\";\n wsConnectionId: string;\n success: boolean;\n error?: string;\n}\n\nexport interface WSFrameMessage {\n type: \"ws_frame\";\n wsConnectionId: string;\n data: string; // base64 encoded\n isBinary: boolean;\n}\n\nexport interface WSCloseMessage {\n type: \"ws_close\";\n wsConnectionId: string;\n code?: number;\n reason?: string;\n}\n\nexport interface UDPResponseMessage {\n type: \"udp_response\";\n packetId: string;\n targetAddress: string;\n targetPort: number;\n data: string; // base64 encoded\n}\n\nexport interface RequestMessage {\n type: \"request\";\n requestId: string;\n method: string;\n path: string;\n headers: Record<string, string | string[]>;\n body?: string;\n}\n\nexport interface ResponseMessage {\n type: \"response\";\n requestId: string;\n statusCode: number;\n headers: Record<string, string | string[]>;\n body?: string;\n}\n\nexport interface PingMessage {\n type: \"ping\";\n}\n\nexport interface PongMessage {\n type: \"pong\";\n}\n\nexport interface ErrorMessage {\n type: \"error\";\n code: string;\n message: string;\n}\n\nexport type Message =\n | HelloMessage\n | OpenTunnelMessage\n | TunnelOpenedMessage\n | RequestMessage\n | ResponseMessage\n | PingMessage\n | PongMessage\n | ErrorMessage\n | TCPConnectionMessage\n | TCPDataMessage\n | TCPCloseMessage\n | UDPDataMessage\n | UDPResponseMessage\n | WSUpgradeMessage\n | WSUpgradeResponseMessage\n | WSFrameMessage\n | WSCloseMessage;\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
package/dist/types.d.cts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
type TunnelProtocol = "http" | "tcp" | "udp";
|
|
2
|
+
interface HelloMessage {
|
|
3
|
+
type: "hello";
|
|
4
|
+
clientId: string;
|
|
5
|
+
version: string;
|
|
6
|
+
}
|
|
7
|
+
interface OpenTunnelMessage {
|
|
8
|
+
type: "open_tunnel";
|
|
9
|
+
subdomain?: string;
|
|
10
|
+
customDomain?: string;
|
|
11
|
+
apiKey?: string;
|
|
12
|
+
forceTakeover?: boolean;
|
|
13
|
+
protocol?: TunnelProtocol;
|
|
14
|
+
remotePort?: number;
|
|
15
|
+
password?: string;
|
|
16
|
+
}
|
|
17
|
+
interface TunnelOpenedMessage {
|
|
18
|
+
type: "tunnel_opened";
|
|
19
|
+
tunnelId: string;
|
|
20
|
+
url: string;
|
|
21
|
+
plan?: string;
|
|
22
|
+
protocol?: TunnelProtocol;
|
|
23
|
+
port?: number;
|
|
24
|
+
}
|
|
25
|
+
interface TCPConnectionMessage {
|
|
26
|
+
type: "tcp_connection";
|
|
27
|
+
connectionId: string;
|
|
28
|
+
}
|
|
29
|
+
interface TCPDataMessage {
|
|
30
|
+
type: "tcp_data";
|
|
31
|
+
connectionId: string;
|
|
32
|
+
data: string;
|
|
33
|
+
}
|
|
34
|
+
interface TCPCloseMessage {
|
|
35
|
+
type: "tcp_close";
|
|
36
|
+
connectionId: string;
|
|
37
|
+
}
|
|
38
|
+
interface UDPDataMessage {
|
|
39
|
+
type: "udp_data";
|
|
40
|
+
packetId: string;
|
|
41
|
+
sourceAddress: string;
|
|
42
|
+
sourcePort: number;
|
|
43
|
+
data: string;
|
|
44
|
+
}
|
|
45
|
+
interface WSUpgradeMessage {
|
|
46
|
+
type: "ws_upgrade";
|
|
47
|
+
wsConnectionId: string;
|
|
48
|
+
path: string;
|
|
49
|
+
headers: Record<string, string | string[]>;
|
|
50
|
+
protocol?: string;
|
|
51
|
+
}
|
|
52
|
+
interface WSUpgradeResponseMessage {
|
|
53
|
+
type: "ws_upgrade_response";
|
|
54
|
+
wsConnectionId: string;
|
|
55
|
+
success: boolean;
|
|
56
|
+
error?: string;
|
|
57
|
+
}
|
|
58
|
+
interface WSFrameMessage {
|
|
59
|
+
type: "ws_frame";
|
|
60
|
+
wsConnectionId: string;
|
|
61
|
+
data: string;
|
|
62
|
+
isBinary: boolean;
|
|
63
|
+
}
|
|
64
|
+
interface WSCloseMessage {
|
|
65
|
+
type: "ws_close";
|
|
66
|
+
wsConnectionId: string;
|
|
67
|
+
code?: number;
|
|
68
|
+
reason?: string;
|
|
69
|
+
}
|
|
70
|
+
interface UDPResponseMessage {
|
|
71
|
+
type: "udp_response";
|
|
72
|
+
packetId: string;
|
|
73
|
+
targetAddress: string;
|
|
74
|
+
targetPort: number;
|
|
75
|
+
data: string;
|
|
76
|
+
}
|
|
77
|
+
interface RequestMessage {
|
|
78
|
+
type: "request";
|
|
79
|
+
requestId: string;
|
|
80
|
+
method: string;
|
|
81
|
+
path: string;
|
|
82
|
+
headers: Record<string, string | string[]>;
|
|
83
|
+
body?: string;
|
|
84
|
+
}
|
|
85
|
+
interface ResponseMessage {
|
|
86
|
+
type: "response";
|
|
87
|
+
requestId: string;
|
|
88
|
+
statusCode: number;
|
|
89
|
+
headers: Record<string, string | string[]>;
|
|
90
|
+
body?: string;
|
|
91
|
+
}
|
|
92
|
+
interface PingMessage {
|
|
93
|
+
type: "ping";
|
|
94
|
+
}
|
|
95
|
+
interface PongMessage {
|
|
96
|
+
type: "pong";
|
|
97
|
+
}
|
|
98
|
+
interface ErrorMessage {
|
|
99
|
+
type: "error";
|
|
100
|
+
code: string;
|
|
101
|
+
message: string;
|
|
102
|
+
}
|
|
103
|
+
type Message = HelloMessage | OpenTunnelMessage | TunnelOpenedMessage | RequestMessage | ResponseMessage | PingMessage | PongMessage | ErrorMessage | TCPConnectionMessage | TCPDataMessage | TCPCloseMessage | UDPDataMessage | UDPResponseMessage | WSUpgradeMessage | WSUpgradeResponseMessage | WSFrameMessage | WSCloseMessage;
|
|
104
|
+
|
|
105
|
+
export type { ErrorMessage, HelloMessage, Message, OpenTunnelMessage, PingMessage, PongMessage, RequestMessage, ResponseMessage, TCPCloseMessage, TCPConnectionMessage, TCPDataMessage, TunnelOpenedMessage, TunnelProtocol, UDPDataMessage, UDPResponseMessage, WSCloseMessage, WSFrameMessage, WSUpgradeMessage, WSUpgradeResponseMessage };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
type TunnelProtocol = "http" | "tcp" | "udp";
|
|
2
|
+
interface HelloMessage {
|
|
3
|
+
type: "hello";
|
|
4
|
+
clientId: string;
|
|
5
|
+
version: string;
|
|
6
|
+
}
|
|
7
|
+
interface OpenTunnelMessage {
|
|
8
|
+
type: "open_tunnel";
|
|
9
|
+
subdomain?: string;
|
|
10
|
+
customDomain?: string;
|
|
11
|
+
apiKey?: string;
|
|
12
|
+
forceTakeover?: boolean;
|
|
13
|
+
protocol?: TunnelProtocol;
|
|
14
|
+
remotePort?: number;
|
|
15
|
+
password?: string;
|
|
16
|
+
}
|
|
17
|
+
interface TunnelOpenedMessage {
|
|
18
|
+
type: "tunnel_opened";
|
|
19
|
+
tunnelId: string;
|
|
20
|
+
url: string;
|
|
21
|
+
plan?: string;
|
|
22
|
+
protocol?: TunnelProtocol;
|
|
23
|
+
port?: number;
|
|
24
|
+
}
|
|
25
|
+
interface TCPConnectionMessage {
|
|
26
|
+
type: "tcp_connection";
|
|
27
|
+
connectionId: string;
|
|
28
|
+
}
|
|
29
|
+
interface TCPDataMessage {
|
|
30
|
+
type: "tcp_data";
|
|
31
|
+
connectionId: string;
|
|
32
|
+
data: string;
|
|
33
|
+
}
|
|
34
|
+
interface TCPCloseMessage {
|
|
35
|
+
type: "tcp_close";
|
|
36
|
+
connectionId: string;
|
|
37
|
+
}
|
|
38
|
+
interface UDPDataMessage {
|
|
39
|
+
type: "udp_data";
|
|
40
|
+
packetId: string;
|
|
41
|
+
sourceAddress: string;
|
|
42
|
+
sourcePort: number;
|
|
43
|
+
data: string;
|
|
44
|
+
}
|
|
45
|
+
interface WSUpgradeMessage {
|
|
46
|
+
type: "ws_upgrade";
|
|
47
|
+
wsConnectionId: string;
|
|
48
|
+
path: string;
|
|
49
|
+
headers: Record<string, string | string[]>;
|
|
50
|
+
protocol?: string;
|
|
51
|
+
}
|
|
52
|
+
interface WSUpgradeResponseMessage {
|
|
53
|
+
type: "ws_upgrade_response";
|
|
54
|
+
wsConnectionId: string;
|
|
55
|
+
success: boolean;
|
|
56
|
+
error?: string;
|
|
57
|
+
}
|
|
58
|
+
interface WSFrameMessage {
|
|
59
|
+
type: "ws_frame";
|
|
60
|
+
wsConnectionId: string;
|
|
61
|
+
data: string;
|
|
62
|
+
isBinary: boolean;
|
|
63
|
+
}
|
|
64
|
+
interface WSCloseMessage {
|
|
65
|
+
type: "ws_close";
|
|
66
|
+
wsConnectionId: string;
|
|
67
|
+
code?: number;
|
|
68
|
+
reason?: string;
|
|
69
|
+
}
|
|
70
|
+
interface UDPResponseMessage {
|
|
71
|
+
type: "udp_response";
|
|
72
|
+
packetId: string;
|
|
73
|
+
targetAddress: string;
|
|
74
|
+
targetPort: number;
|
|
75
|
+
data: string;
|
|
76
|
+
}
|
|
77
|
+
interface RequestMessage {
|
|
78
|
+
type: "request";
|
|
79
|
+
requestId: string;
|
|
80
|
+
method: string;
|
|
81
|
+
path: string;
|
|
82
|
+
headers: Record<string, string | string[]>;
|
|
83
|
+
body?: string;
|
|
84
|
+
}
|
|
85
|
+
interface ResponseMessage {
|
|
86
|
+
type: "response";
|
|
87
|
+
requestId: string;
|
|
88
|
+
statusCode: number;
|
|
89
|
+
headers: Record<string, string | string[]>;
|
|
90
|
+
body?: string;
|
|
91
|
+
}
|
|
92
|
+
interface PingMessage {
|
|
93
|
+
type: "ping";
|
|
94
|
+
}
|
|
95
|
+
interface PongMessage {
|
|
96
|
+
type: "pong";
|
|
97
|
+
}
|
|
98
|
+
interface ErrorMessage {
|
|
99
|
+
type: "error";
|
|
100
|
+
code: string;
|
|
101
|
+
message: string;
|
|
102
|
+
}
|
|
103
|
+
type Message = HelloMessage | OpenTunnelMessage | TunnelOpenedMessage | RequestMessage | ResponseMessage | PingMessage | PongMessage | ErrorMessage | TCPConnectionMessage | TCPDataMessage | TCPCloseMessage | UDPDataMessage | UDPResponseMessage | WSUpgradeMessage | WSUpgradeResponseMessage | WSFrameMessage | WSCloseMessage;
|
|
104
|
+
|
|
105
|
+
export type { ErrorMessage, HelloMessage, Message, OpenTunnelMessage, PingMessage, PongMessage, RequestMessage, ResponseMessage, TCPCloseMessage, TCPConnectionMessage, TCPDataMessage, TunnelOpenedMessage, TunnelProtocol, UDPDataMessage, UDPResponseMessage, WSCloseMessage, WSFrameMessage, WSUpgradeMessage, WSUpgradeResponseMessage };
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/usage-writer.ts
|
|
21
|
+
var usage_writer_exports = {};
|
|
22
|
+
__export(usage_writer_exports, {
|
|
23
|
+
recordUsageCounters: () => recordUsageCounters
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(usage_writer_exports);
|
|
26
|
+
async function recordUsageCounters(database, event, allowedMetrics) {
|
|
27
|
+
if (!event.organizationId || event.organizationId.length > 128 || !/^[a-z][a-z0-9-]{0,63}$/.test(event.source) || !event.id || event.id.length > 256 || !Number.isFinite(Date.parse(event.timestamp))) throw new Error("Invalid usage event");
|
|
28
|
+
const deltas = {};
|
|
29
|
+
for (const [key, value] of Object.entries(event.deltas).sort(([a], [b]) => a.localeCompare(b))) {
|
|
30
|
+
if (!allowedMetrics.has(key) || typeof value === "number" && !Number.isSafeInteger(value) || !/^\d+$/.test(String(value))) throw new Error("Invalid usage delta");
|
|
31
|
+
const count = BigInt(value);
|
|
32
|
+
if (count > 9223372036854775807n) throw new Error("Usage delta overflow");
|
|
33
|
+
if (count > 0n) deltas[key] = String(count);
|
|
34
|
+
}
|
|
35
|
+
if (!Object.keys(deltas).length) return false;
|
|
36
|
+
const result = await database.query("SELECT record_product_usage($1,$2,$3,$4::timestamptz,$5::jsonb) AS recorded", [event.organizationId, event.source, event.id, event.timestamp, JSON.stringify(deltas)]);
|
|
37
|
+
return result.rows[0]?.recorded === true;
|
|
38
|
+
}
|
|
39
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
40
|
+
0 && (module.exports = {
|
|
41
|
+
recordUsageCounters
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=usage-writer.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/usage-writer.ts"],"sourcesContent":["export interface UsageDatabase { query(text: string, values?: any[]): Promise<any> }\nexport interface CounterEvent<Metric extends string = string> {\n organizationId: string; source: string; id: string; timestamp: string;\n deltas: Partial<Record<Metric, string | number>>;\n}\n/** Transport and validation only; callers supply their product's metric catalog. */\nexport async function recordUsageCounters<Metric extends string>(database: UsageDatabase, event: CounterEvent<Metric>, allowedMetrics: ReadonlySet<string>): Promise<boolean> {\n if (!event.organizationId || event.organizationId.length > 128 || !/^[a-z][a-z0-9-]{0,63}$/.test(event.source) || !event.id || event.id.length > 256 || !Number.isFinite(Date.parse(event.timestamp))) throw new Error(\"Invalid usage event\");\n const deltas: Record<string, string> = {};\n for (const [key, value] of Object.entries(event.deltas).sort(([a], [b]) => a.localeCompare(b))) {\n if (!allowedMetrics.has(key) || (typeof value === \"number\" && !Number.isSafeInteger(value)) || !/^\\d+$/.test(String(value))) throw new Error(\"Invalid usage delta\");\n const count = BigInt(value as string | number);\n if (count > 9223372036854775807n) throw new Error(\"Usage delta overflow\");\n if (count > 0n) deltas[key] = String(count);\n }\n if (!Object.keys(deltas).length) return false;\n const result = await database.query(\"SELECT record_product_usage($1,$2,$3,$4::timestamptz,$5::jsonb) AS recorded\", [event.organizationId, event.source, event.id, event.timestamp, JSON.stringify(deltas)]);\n return result.rows[0]?.recorded === true;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,eAAsB,oBAA2C,UAAyB,OAA6B,gBAAuD;AAC5K,MAAI,CAAC,MAAM,kBAAkB,MAAM,eAAe,SAAS,OAAO,CAAC,yBAAyB,KAAK,MAAM,MAAM,KAAK,CAAC,MAAM,MAAM,MAAM,GAAG,SAAS,OAAO,CAAC,OAAO,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,EAAG,OAAM,IAAI,MAAM,qBAAqB;AAC5O,QAAM,SAAiC,CAAC;AACxC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,GAAG;AAC9F,QAAI,CAAC,eAAe,IAAI,GAAG,KAAM,OAAO,UAAU,YAAY,CAAC,OAAO,cAAc,KAAK,KAAM,CAAC,QAAQ,KAAK,OAAO,KAAK,CAAC,EAAG,OAAM,IAAI,MAAM,qBAAqB;AAClK,UAAM,QAAQ,OAAO,KAAwB;AAC7C,QAAI,QAAQ,qBAAsB,OAAM,IAAI,MAAM,sBAAsB;AACxE,QAAI,QAAQ,GAAI,QAAO,GAAG,IAAI,OAAO,KAAK;AAAA,EAC5C;AACA,MAAI,CAAC,OAAO,KAAK,MAAM,EAAE,OAAQ,QAAO;AACxC,QAAM,SAAS,MAAM,SAAS,MAAM,+EAA+E,CAAC,MAAM,gBAAgB,MAAM,QAAQ,MAAM,IAAI,MAAM,WAAW,KAAK,UAAU,MAAM,CAAC,CAAC;AAC1M,SAAO,OAAO,KAAK,CAAC,GAAG,aAAa;AACtC;","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface UsageDatabase {
|
|
2
|
+
query(text: string, values?: any[]): Promise<any>;
|
|
3
|
+
}
|
|
4
|
+
interface CounterEvent<Metric extends string = string> {
|
|
5
|
+
organizationId: string;
|
|
6
|
+
source: string;
|
|
7
|
+
id: string;
|
|
8
|
+
timestamp: string;
|
|
9
|
+
deltas: Partial<Record<Metric, string | number>>;
|
|
10
|
+
}
|
|
11
|
+
/** Transport and validation only; callers supply their product's metric catalog. */
|
|
12
|
+
declare function recordUsageCounters<Metric extends string>(database: UsageDatabase, event: CounterEvent<Metric>, allowedMetrics: ReadonlySet<string>): Promise<boolean>;
|
|
13
|
+
|
|
14
|
+
export { type CounterEvent, type UsageDatabase, recordUsageCounters };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface UsageDatabase {
|
|
2
|
+
query(text: string, values?: any[]): Promise<any>;
|
|
3
|
+
}
|
|
4
|
+
interface CounterEvent<Metric extends string = string> {
|
|
5
|
+
organizationId: string;
|
|
6
|
+
source: string;
|
|
7
|
+
id: string;
|
|
8
|
+
timestamp: string;
|
|
9
|
+
deltas: Partial<Record<Metric, string | number>>;
|
|
10
|
+
}
|
|
11
|
+
/** Transport and validation only; callers supply their product's metric catalog. */
|
|
12
|
+
declare function recordUsageCounters<Metric extends string>(database: UsageDatabase, event: CounterEvent<Metric>, allowedMetrics: ReadonlySet<string>): Promise<boolean>;
|
|
13
|
+
|
|
14
|
+
export { type CounterEvent, type UsageDatabase, recordUsageCounters };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|