@forklaunch/core 3.0.0 → 3.1.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/bin/gateway-mock/server.mjs +39 -21
- package/lib/http/index.d.ts +1 -0
- package/lib/http/index.d.ts.map +1 -1
- package/lib/http/index.js +94 -3
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +93 -3
- package/lib/http/index.mjs.map +1 -1
- package/lib/http/paymentsClient.d.ts +19 -8
- package/lib/http/paymentsClient.d.ts.map +1 -1
- package/lib/http/previewModelGatewayClient.d.ts +48 -0
- package/lib/http/previewModelGatewayClient.d.ts.map +1 -0
- package/package.json +33 -33
|
@@ -49,7 +49,9 @@
|
|
|
49
49
|
* MOCK_INSTANCE_HMAC_KEY verify instance signatures and sign events
|
|
50
50
|
* (unset: accept any signature; events unsigned)
|
|
51
51
|
* MOCK_EVENTS_URL the app's base URL for event delivery
|
|
52
|
-
* MOCK_EVENTS_URL_<FEATURE> per-feature override (the service that declared it)
|
|
52
|
+
* MOCK_EVENTS_URL_<FEATURE> per-feature override (the service that declared it);
|
|
53
|
+
* a comma-separated list when several projects hold
|
|
54
|
+
* the feature — each event goes to every entry
|
|
53
55
|
* (plus each feature's own MOCK_* settings; see its route file)
|
|
54
56
|
*
|
|
55
57
|
* Usage: npx -p @forklaunch/core forklaunch-gateway-mock
|
|
@@ -141,34 +143,50 @@ export async function startGatewayMock(env = process.env) {
|
|
|
141
143
|
occurredAt: new Date().toISOString(),
|
|
142
144
|
data: data ?? {}
|
|
143
145
|
};
|
|
144
|
-
const record = { event, status: null, error: null };
|
|
146
|
+
const record = { event, status: null, error: null, deliveries: [] };
|
|
145
147
|
events.push(record);
|
|
146
|
-
// A feature's events can go to its own service (MOCK_EVENTS_URL_<FEATURE>)
|
|
147
|
-
|
|
148
|
+
// A feature's events can go to its own service (MOCK_EVENTS_URL_<FEATURE>),
|
|
149
|
+
// or to several: every project that holds the feature, comma-separated.
|
|
150
|
+
const targets = (
|
|
148
151
|
env[`MOCK_EVENTS_URL_${feature.toUpperCase().replace(/-/g, '_')}`] ||
|
|
149
|
-
eventsUrl
|
|
150
|
-
|
|
152
|
+
eventsUrl
|
|
153
|
+
)
|
|
154
|
+
.split(',')
|
|
155
|
+
.map((t) => t.trim())
|
|
156
|
+
.filter(Boolean);
|
|
157
|
+
if (targets.length === 0) {
|
|
151
158
|
record.error = 'MOCK_EVENTS_URL is not set; event recorded, not delivered';
|
|
152
159
|
return record;
|
|
153
160
|
}
|
|
154
161
|
const path = `/platform-events/${feature}`;
|
|
155
162
|
const body = JSON.stringify(event);
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
163
|
+
for (const target of targets) {
|
|
164
|
+
// A fresh signature per destination: each app rejects a reused nonce.
|
|
165
|
+
const headers = { 'content-type': 'application/json' };
|
|
166
|
+
if (hmacKey) {
|
|
167
|
+
const ts = new Date().toISOString();
|
|
168
|
+
const nonce = randomUUID();
|
|
169
|
+
headers.authorization = `HMAC keyId=platform ts=${ts} nonce=${nonce} signature=${sign(hmacKey, 'POST', path, `${body}\n`, ts, nonce)}`;
|
|
170
|
+
}
|
|
171
|
+
const delivery = { target, status: null, error: null };
|
|
172
|
+
try {
|
|
173
|
+
const response = await fetch(`${target.replace(/\/$/, '')}${path}`, {
|
|
174
|
+
method: 'POST',
|
|
175
|
+
headers,
|
|
176
|
+
body
|
|
177
|
+
});
|
|
178
|
+
delivery.status = response.status;
|
|
179
|
+
} catch (error) {
|
|
180
|
+
delivery.error = String(error?.message ?? error);
|
|
181
|
+
}
|
|
182
|
+
record.deliveries.push(delivery);
|
|
171
183
|
}
|
|
184
|
+
// The summary: the first failed delivery, else the last one.
|
|
185
|
+
const summary =
|
|
186
|
+
record.deliveries.find((d) => d.error || d.status >= 300) ??
|
|
187
|
+
record.deliveries[record.deliveries.length - 1];
|
|
188
|
+
record.status = summary.status;
|
|
189
|
+
record.error = summary.error;
|
|
172
190
|
return record;
|
|
173
191
|
}
|
|
174
192
|
|
package/lib/http/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from './createHmacToken';
|
|
|
6
6
|
export * from './generateHmacAuthHeaders';
|
|
7
7
|
export * from './instanceGateway';
|
|
8
8
|
export * from './modelGatewayClient';
|
|
9
|
+
export * from './previewModelGatewayClient';
|
|
9
10
|
export * from './emailClient';
|
|
10
11
|
export * from './smsClient';
|
|
11
12
|
export * from './whatsappClient';
|
package/lib/http/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACnC,cAAc,sCAAsC,CAAC;AACrD,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0CAA0C,CAAC;AACzD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+CAA+C,CAAC;AAC9D,cAAc,wDAAwD,CAAC;AACvE,cAAc,yCAAyC,CAAC;AACxD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sCAAsC,CAAC;AACrD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oCAAoC,CAAC;AACnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,sCAAsC,CAAC;AACrD,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,cAAc,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACnC,cAAc,sCAAsC,CAAC;AACrD,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0CAA0C,CAAC;AACzD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+CAA+C,CAAC;AAC9D,cAAc,wDAAwD,CAAC;AACvE,cAAc,yCAAyC,CAAC;AACxD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sCAAsC,CAAC;AACrD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oCAAoC,CAAC;AACnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,sCAAsC,CAAC;AACrD,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,cAAc,4BAA4B,CAAC"}
|
package/lib/http/index.js
CHANGED
|
@@ -69,6 +69,7 @@ __export(http_exports, {
|
|
|
69
69
|
createInstanceGatewayTransport: () => createInstanceGatewayTransport,
|
|
70
70
|
createModelGatewayClient: () => createModelGatewayClient,
|
|
71
71
|
createPaymentsClient: () => createPaymentsClient,
|
|
72
|
+
createPreviewModelGatewayClient: () => createPreviewModelGatewayClient,
|
|
72
73
|
createSmsClient: () => createSmsClient,
|
|
73
74
|
createStripeClient: () => createStripeClient,
|
|
74
75
|
createVoiceClient: () => createVoiceClient,
|
|
@@ -2738,6 +2739,92 @@ async function* sseData(stream) {
|
|
|
2738
2739
|
}
|
|
2739
2740
|
}
|
|
2740
2741
|
|
|
2742
|
+
// src/http/previewModelGatewayClient.ts
|
|
2743
|
+
function record(value) {
|
|
2744
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2745
|
+
}
|
|
2746
|
+
function invalidCatalog() {
|
|
2747
|
+
throw new ModelGatewayRequestError(
|
|
2748
|
+
502,
|
|
2749
|
+
"The local preview model catalog is invalid or expired."
|
|
2750
|
+
);
|
|
2751
|
+
}
|
|
2752
|
+
function previewCatalog(raw) {
|
|
2753
|
+
if (!record(raw) || !record(raw.preview)) return invalidCatalog();
|
|
2754
|
+
const preview = raw.preview;
|
|
2755
|
+
if (Object.keys(raw).some((key) => !["models", "preview"].includes(key)) || Object.keys(preview).some(
|
|
2756
|
+
(key) => !["expiresAt", "maxCalls", "maxUnits"].includes(key)
|
|
2757
|
+
) || !Array.isArray(raw.models) || raw.models.length !== 1 || !record(raw.models[0]) || Object.keys(raw.models[0]).some((key) => key !== "id") || typeof raw.models[0].id !== "string" || !/^[\w.:-]{1,160}$/.test(raw.models[0].id) || typeof preview.expiresAt !== "number" || !Number.isSafeInteger(preview.expiresAt) || preview.expiresAt <= Date.now() || preview.expiresAt > Date.now() + 15 * 6e4 || typeof preview.maxCalls !== "number" || !Number.isSafeInteger(preview.maxCalls) || preview.maxCalls < 1 || preview.maxCalls > 20 || typeof preview.maxUnits !== "number" || !Number.isSafeInteger(preview.maxUnits) || preview.maxUnits < 1 || preview.maxUnits > 1e5)
|
|
2758
|
+
return invalidCatalog();
|
|
2759
|
+
return {
|
|
2760
|
+
models: [{ id: raw.models[0].id }],
|
|
2761
|
+
preview: {
|
|
2762
|
+
expiresAt: preview.expiresAt,
|
|
2763
|
+
maxCalls: preview.maxCalls,
|
|
2764
|
+
maxUnits: preview.maxUnits
|
|
2765
|
+
}
|
|
2766
|
+
};
|
|
2767
|
+
}
|
|
2768
|
+
async function readPreviewCatalog(response) {
|
|
2769
|
+
if (!response.body) return invalidCatalog();
|
|
2770
|
+
const reader = response.body.getReader();
|
|
2771
|
+
const decoder = new TextDecoder();
|
|
2772
|
+
let text = "", bytes = 0;
|
|
2773
|
+
try {
|
|
2774
|
+
for (; ; ) {
|
|
2775
|
+
const { done, value } = await reader.read();
|
|
2776
|
+
if (done) break;
|
|
2777
|
+
bytes += value.byteLength;
|
|
2778
|
+
if (bytes > 8192) return invalidCatalog();
|
|
2779
|
+
text += decoder.decode(value, { stream: true });
|
|
2780
|
+
}
|
|
2781
|
+
text += decoder.decode();
|
|
2782
|
+
return previewCatalog(JSON.parse(text));
|
|
2783
|
+
} catch {
|
|
2784
|
+
return invalidCatalog();
|
|
2785
|
+
} finally {
|
|
2786
|
+
await reader.cancel().catch(() => {
|
|
2787
|
+
});
|
|
2788
|
+
reader.releaseLock();
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
function createPreviewModelGatewayClient(options2 = {}) {
|
|
2792
|
+
const transport = createInstanceGatewayTransport(
|
|
2793
|
+
options2,
|
|
2794
|
+
"The local preview model gateway"
|
|
2795
|
+
);
|
|
2796
|
+
async function send(method, route, body) {
|
|
2797
|
+
try {
|
|
2798
|
+
return await transport.request(method, route, body);
|
|
2799
|
+
} catch (error) {
|
|
2800
|
+
if (error instanceof InstanceGatewayRequestError)
|
|
2801
|
+
throw new ModelGatewayRequestError(
|
|
2802
|
+
error.status,
|
|
2803
|
+
"The local preview model request was refused or is unavailable.",
|
|
2804
|
+
error.retryAfterSeconds
|
|
2805
|
+
);
|
|
2806
|
+
throw error;
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
return {
|
|
2810
|
+
async models() {
|
|
2811
|
+
const response = await send("GET", "/models");
|
|
2812
|
+
return readPreviewCatalog(response);
|
|
2813
|
+
},
|
|
2814
|
+
chat: {
|
|
2815
|
+
completions: {
|
|
2816
|
+
async create(request) {
|
|
2817
|
+
return await (await send(
|
|
2818
|
+
"POST",
|
|
2819
|
+
"/models/chat/completions",
|
|
2820
|
+
request
|
|
2821
|
+
)).json();
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
};
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2741
2828
|
// src/http/emailClient.ts
|
|
2742
2829
|
var EmailRequestError = class extends InstanceGatewayRequestError {
|
|
2743
2830
|
name = "EmailRequestError";
|
|
@@ -2823,7 +2910,7 @@ function createEmailClient(options2 = {}) {
|
|
|
2823
2910
|
INSTANCE_HMAC_KEY: options2.hmacKey ?? process.env.INSTANCE_HMAC_KEY
|
|
2824
2911
|
})) {
|
|
2825
2912
|
throw new Error(
|
|
2826
|
-
"Email is sent by the ForkLaunch platform
|
|
2913
|
+
"Email is sent by the ForkLaunch platform: PLATFORM_GATEWAY_URL, INSTANCE_ID and INSTANCE_HMAC_KEY must be set. A managed instance and an app deployed with `forklaunch deploy create` get them from the platform at deploy. Locally, `forklaunch infra add <service> email` sets them to the gateway mock in docker-compose."
|
|
2827
2914
|
);
|
|
2828
2915
|
}
|
|
2829
2916
|
const transport = createInstanceGatewayTransport(options2, "Email");
|
|
@@ -3251,6 +3338,9 @@ function createStripeClient(options2 = {}) {
|
|
|
3251
3338
|
const explicitGateway = Boolean(
|
|
3252
3339
|
options2.gatewayUrl || options2.instanceId || options2.hmacKey
|
|
3253
3340
|
);
|
|
3341
|
+
if (!explicitGateway && options2.apiKey) {
|
|
3342
|
+
return new StripeClass(options2.apiKey, options2.config);
|
|
3343
|
+
}
|
|
3254
3344
|
if (!explicitGateway && !isManagedInstance()) {
|
|
3255
3345
|
if (!options2.apiKey) {
|
|
3256
3346
|
throw new Error(
|
|
@@ -5325,8 +5415,8 @@ var RateLimiter = class {
|
|
|
5325
5415
|
const now = Date.now();
|
|
5326
5416
|
let counter;
|
|
5327
5417
|
try {
|
|
5328
|
-
const
|
|
5329
|
-
counter =
|
|
5418
|
+
const record2 = await this.cache.readRecord(key);
|
|
5419
|
+
counter = record2.value;
|
|
5330
5420
|
if (now >= counter.resetAt) {
|
|
5331
5421
|
counter = { count: 0, resetAt: now + windowMs };
|
|
5332
5422
|
}
|
|
@@ -5410,6 +5500,7 @@ var RateLimiter = class {
|
|
|
5410
5500
|
createInstanceGatewayTransport,
|
|
5411
5501
|
createModelGatewayClient,
|
|
5412
5502
|
createPaymentsClient,
|
|
5503
|
+
createPreviewModelGatewayClient,
|
|
5413
5504
|
createSmsClient,
|
|
5414
5505
|
createStripeClient,
|
|
5415
5506
|
createVoiceClient,
|