alchemy 0.36.0 → 0.37.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/lib/cloudflare/bindings.d.ts +1 -1
- package/lib/cloudflare/bindings.d.ts.map +1 -1
- package/lib/cloudflare/bound.d.ts +1 -1
- package/lib/cloudflare/bound.d.ts.map +1 -1
- package/lib/cloudflare/d1-database.d.ts.map +1 -1
- package/lib/cloudflare/d1-database.js +0 -3
- package/lib/cloudflare/d1-database.js.map +1 -1
- package/lib/cloudflare/do-state-store/internal.d.ts.map +1 -1
- package/lib/cloudflare/do-state-store/internal.js +35 -28
- package/lib/cloudflare/do-state-store/internal.js.map +1 -1
- package/lib/cloudflare/durable-object-namespace.d.ts +1 -1
- package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -1
- package/lib/cloudflare/durable-object-namespace.js.map +1 -1
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +28 -13
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts +8 -5
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +48 -11
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +18 -12
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +74 -68
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/stripe/price.d.ts +24 -0
- package/lib/stripe/price.d.ts.map +1 -1
- package/lib/stripe/price.js +30 -0
- package/lib/stripe/price.js.map +1 -1
- package/package.json +1 -1
- package/src/cloudflare/bindings.ts +1 -1
- package/src/cloudflare/bound.ts +1 -1
- package/src/cloudflare/d1-database.ts +0 -4
- package/src/cloudflare/do-state-store/internal.ts +45 -29
- package/src/cloudflare/durable-object-namespace.ts +2 -3
- package/src/cloudflare/website.ts +40 -16
- package/src/cloudflare/worker-metadata.ts +101 -19
- package/src/cloudflare/worker.ts +104 -148
- package/src/stripe/price.ts +47 -1
package/src/cloudflare/worker.ts
CHANGED
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
type Bindings,
|
|
26
26
|
Json,
|
|
27
27
|
type WorkerBindingService,
|
|
28
|
-
type WorkerBindingSpec,
|
|
29
28
|
} from "./bindings.ts";
|
|
30
29
|
import type { Bound } from "./bound.ts";
|
|
31
30
|
import { isBucket } from "./bucket.ts";
|
|
@@ -56,11 +55,10 @@ import { Route } from "./route.ts";
|
|
|
56
55
|
import { isVectorizeIndex } from "./vectorize-index.ts";
|
|
57
56
|
import { type AssetUploadResult, uploadAssets } from "./worker-assets.ts";
|
|
58
57
|
import {
|
|
59
|
-
type WorkerMetadata,
|
|
60
58
|
type WorkerScriptMetadata,
|
|
59
|
+
bumpMigrationTagVersion,
|
|
61
60
|
prepareWorkerMetadata,
|
|
62
61
|
} from "./worker-metadata.ts";
|
|
63
|
-
import type { SingleStepMigration } from "./worker-migration.ts";
|
|
64
62
|
import { WorkerStub, isWorkerStub } from "./worker-stub.ts";
|
|
65
63
|
import { Workflow, isWorkflow, upsertWorkflow } from "./workflow.ts";
|
|
66
64
|
|
|
@@ -98,11 +96,12 @@ export interface AssetsConfig {
|
|
|
98
96
|
|
|
99
97
|
/**
|
|
100
98
|
* When true, requests will always invoke the Worker script.
|
|
99
|
+
* If an array is passed, the worker will be invoked for matching requests.
|
|
101
100
|
* Otherwise, attempt to serve an asset matching the request, falling back to the Worker script.
|
|
102
101
|
*
|
|
103
102
|
* @default false
|
|
104
103
|
*/
|
|
105
|
-
run_worker_first?: boolean;
|
|
104
|
+
run_worker_first?: boolean | string[];
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
export interface BaseWorkerProps<
|
|
@@ -172,11 +171,6 @@ export interface BaseWorkerProps<
|
|
|
172
171
|
enabled?: boolean;
|
|
173
172
|
};
|
|
174
173
|
|
|
175
|
-
/**
|
|
176
|
-
* Migrations to apply to the worker
|
|
177
|
-
*/
|
|
178
|
-
migrations?: SingleStepMigration;
|
|
179
|
-
|
|
180
174
|
/**
|
|
181
175
|
* Whether to adopt the Worker if it already exists when creating
|
|
182
176
|
*/
|
|
@@ -894,12 +888,6 @@ export const _Worker = Resource(
|
|
|
894
888
|
const compatibilityFlags = props.compatibilityFlags ?? [];
|
|
895
889
|
|
|
896
890
|
const uploadWorkerScript = async (props: WorkerProps<B>) => {
|
|
897
|
-
const [oldBindings, oldMetadata] = await Promise.all([
|
|
898
|
-
getWorkerBindings(api, workerName),
|
|
899
|
-
getWorkerScriptMetadata(api, workerName),
|
|
900
|
-
]);
|
|
901
|
-
const oldTags = oldMetadata?.default_environment?.script?.tags;
|
|
902
|
-
|
|
903
891
|
// Get the script content - either from props.script, or by bundling
|
|
904
892
|
const scriptBundle =
|
|
905
893
|
props.script ??
|
|
@@ -949,34 +937,17 @@ export const _Worker = Resource(
|
|
|
949
937
|
});
|
|
950
938
|
}
|
|
951
939
|
|
|
952
|
-
// Prepare metadata with bindings
|
|
953
|
-
const scriptMetadata = await prepareWorkerMetadata(
|
|
954
|
-
this,
|
|
955
|
-
oldBindings,
|
|
956
|
-
oldTags,
|
|
957
|
-
{
|
|
958
|
-
...props,
|
|
959
|
-
compatibilityDate,
|
|
960
|
-
compatibilityFlags,
|
|
961
|
-
workerName,
|
|
962
|
-
},
|
|
963
|
-
assetUploadResult,
|
|
964
|
-
);
|
|
965
|
-
|
|
966
940
|
// Deploy worker (either as version or live worker)
|
|
967
|
-
const versionResult = await putWorker(
|
|
968
|
-
|
|
941
|
+
const versionResult = await putWorker(api, {
|
|
942
|
+
...props,
|
|
969
943
|
workerName,
|
|
970
944
|
scriptBundle,
|
|
971
|
-
scriptMetadata,
|
|
972
945
|
dispatchNamespace,
|
|
973
|
-
props.version
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
: undefined,
|
|
979
|
-
);
|
|
946
|
+
version: props.version,
|
|
947
|
+
compatibilityDate,
|
|
948
|
+
compatibilityFlags,
|
|
949
|
+
assetUploadResult,
|
|
950
|
+
});
|
|
980
951
|
|
|
981
952
|
for (const workflow of workflowsBindings) {
|
|
982
953
|
if (
|
|
@@ -1050,7 +1021,7 @@ export const _Worker = Resource(
|
|
|
1050
1021
|
}
|
|
1051
1022
|
}
|
|
1052
1023
|
|
|
1053
|
-
return { scriptBundle,
|
|
1024
|
+
return { scriptBundle, workerUrl, now, versionResult };
|
|
1054
1025
|
};
|
|
1055
1026
|
|
|
1056
1027
|
if (this.phase === "delete") {
|
|
@@ -1059,9 +1030,13 @@ export const _Worker = Resource(
|
|
|
1059
1030
|
|
|
1060
1031
|
await withExponentialBackoff(
|
|
1061
1032
|
() =>
|
|
1062
|
-
deleteWorker(
|
|
1063
|
-
...props,
|
|
1033
|
+
deleteWorker(api, {
|
|
1064
1034
|
workerName,
|
|
1035
|
+
namespace:
|
|
1036
|
+
typeof props.namespace === "string"
|
|
1037
|
+
? props.namespace
|
|
1038
|
+
: props.namespace?.namespaceId,
|
|
1039
|
+
url: this.output.url,
|
|
1065
1040
|
}),
|
|
1066
1041
|
(err) =>
|
|
1067
1042
|
(err.status === 400 &&
|
|
@@ -1076,10 +1051,6 @@ export const _Worker = Resource(
|
|
|
1076
1051
|
|
|
1077
1052
|
return this.destroy();
|
|
1078
1053
|
}
|
|
1079
|
-
// Validate input - we need either script, entryPoint, or bundle
|
|
1080
|
-
if (!props.script && !props.entrypoint) {
|
|
1081
|
-
throw new Error("One of script or entrypoint must be provided");
|
|
1082
|
-
}
|
|
1083
1054
|
|
|
1084
1055
|
if (this.phase === "create") {
|
|
1085
1056
|
if (props.version) {
|
|
@@ -1096,7 +1067,7 @@ export const _Worker = Resource(
|
|
|
1096
1067
|
}
|
|
1097
1068
|
}
|
|
1098
1069
|
|
|
1099
|
-
const {
|
|
1070
|
+
const { workerUrl, now } = await uploadWorkerScript(props);
|
|
1100
1071
|
|
|
1101
1072
|
// Create routes if provided and capture their outputs
|
|
1102
1073
|
let createdRoutes: Route[] = [];
|
|
@@ -1166,7 +1137,7 @@ export const _Worker = Resource(
|
|
|
1166
1137
|
format: props.format || "esm", // Include format in the output
|
|
1167
1138
|
bindings: exportBindings(),
|
|
1168
1139
|
env: props.env,
|
|
1169
|
-
observability:
|
|
1140
|
+
observability: props.observability,
|
|
1170
1141
|
createdAt: now,
|
|
1171
1142
|
updatedAt: now,
|
|
1172
1143
|
eventSources: props.eventSources,
|
|
@@ -1187,10 +1158,13 @@ export const _Worker = Resource(
|
|
|
1187
1158
|
},
|
|
1188
1159
|
);
|
|
1189
1160
|
|
|
1190
|
-
export async function deleteWorker
|
|
1191
|
-
ctx: Context<Worker<B>>,
|
|
1161
|
+
export async function deleteWorker(
|
|
1192
1162
|
api: CloudflareApi,
|
|
1193
|
-
props:
|
|
1163
|
+
props: {
|
|
1164
|
+
workerName: string;
|
|
1165
|
+
namespace?: string | DispatchNamespaceResource;
|
|
1166
|
+
url?: string;
|
|
1167
|
+
},
|
|
1194
1168
|
) {
|
|
1195
1169
|
const workerName = props.workerName;
|
|
1196
1170
|
|
|
@@ -1207,7 +1181,7 @@ export async function deleteWorker<B extends Bindings>(
|
|
|
1207
1181
|
}
|
|
1208
1182
|
|
|
1209
1183
|
// Disable the URL if it was enabled
|
|
1210
|
-
if (
|
|
1184
|
+
if (props.url) {
|
|
1211
1185
|
try {
|
|
1212
1186
|
await api.post(
|
|
1213
1187
|
`/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`,
|
|
@@ -1225,22 +1199,33 @@ export async function deleteWorker<B extends Bindings>(
|
|
|
1225
1199
|
return;
|
|
1226
1200
|
}
|
|
1227
1201
|
|
|
1228
|
-
|
|
1229
|
-
versionLabel?: string;
|
|
1230
|
-
message?: string;
|
|
1202
|
+
type PutWorkerOptions = WorkerProps & {
|
|
1231
1203
|
dispatchNamespace?: string;
|
|
1232
|
-
|
|
1204
|
+
migrationTag?: string;
|
|
1205
|
+
workerName: string;
|
|
1206
|
+
scriptBundle: string | NoBundleResult;
|
|
1207
|
+
version: string | undefined;
|
|
1208
|
+
compatibilityDate: string;
|
|
1209
|
+
compatibilityFlags: string[];
|
|
1210
|
+
assetUploadResult: AssetUploadResult | undefined;
|
|
1211
|
+
};
|
|
1233
1212
|
|
|
1234
|
-
async function
|
|
1213
|
+
export async function putWorker(
|
|
1235
1214
|
api: CloudflareApi,
|
|
1236
|
-
|
|
1237
|
-
scriptBundle: string | NoBundleResult,
|
|
1238
|
-
scriptMetadata: WorkerMetadata,
|
|
1239
|
-
options: PutWorkerOptions = {},
|
|
1215
|
+
props: PutWorkerOptions,
|
|
1240
1216
|
): Promise<{ versionId?: string; previewUrl?: string }> {
|
|
1217
|
+
const {
|
|
1218
|
+
//
|
|
1219
|
+
dispatchNamespace,
|
|
1220
|
+
migrationTag,
|
|
1221
|
+
workerName,
|
|
1222
|
+
scriptBundle,
|
|
1223
|
+
version,
|
|
1224
|
+
} = props;
|
|
1225
|
+
const scriptMetadata = await prepareWorkerMetadata(api, props);
|
|
1226
|
+
|
|
1241
1227
|
return withExponentialBackoff(
|
|
1242
1228
|
async () => {
|
|
1243
|
-
const { versionLabel, message, dispatchNamespace } = options;
|
|
1244
1229
|
const scriptName =
|
|
1245
1230
|
scriptMetadata.main_module ?? scriptMetadata.body_part!;
|
|
1246
1231
|
|
|
@@ -1271,17 +1256,30 @@ async function putWorkerInternal(
|
|
|
1271
1256
|
}
|
|
1272
1257
|
|
|
1273
1258
|
// Prepare metadata - add version annotations if this is a version
|
|
1274
|
-
const finalMetadata =
|
|
1259
|
+
const finalMetadata = version
|
|
1275
1260
|
? {
|
|
1276
1261
|
...scriptMetadata,
|
|
1277
1262
|
// Exclude migrations for worker versions - they're not allowed
|
|
1278
1263
|
migrations: undefined,
|
|
1279
1264
|
annotations: {
|
|
1280
|
-
"workers/tag":
|
|
1281
|
-
|
|
1265
|
+
"workers/tag": version,
|
|
1266
|
+
"workers/message": `Version ${version}`,
|
|
1282
1267
|
},
|
|
1283
1268
|
}
|
|
1284
|
-
:
|
|
1269
|
+
: {
|
|
1270
|
+
...scriptMetadata,
|
|
1271
|
+
migrations: scriptMetadata.migrations
|
|
1272
|
+
? {
|
|
1273
|
+
...scriptMetadata.migrations,
|
|
1274
|
+
old_tag: migrationTag,
|
|
1275
|
+
new_tag: bumpMigrationTagVersion(migrationTag),
|
|
1276
|
+
}
|
|
1277
|
+
: undefined,
|
|
1278
|
+
};
|
|
1279
|
+
|
|
1280
|
+
if (process.env.DEBUG) {
|
|
1281
|
+
console.log(`metadata(${scriptName}):`, finalMetadata);
|
|
1282
|
+
}
|
|
1285
1283
|
|
|
1286
1284
|
// Add metadata as JSON
|
|
1287
1285
|
formData.append(
|
|
@@ -1294,8 +1292,12 @@ async function putWorkerInternal(
|
|
|
1294
1292
|
// Determine endpoint and HTTP method
|
|
1295
1293
|
let endpoint: string;
|
|
1296
1294
|
let method: "PUT" | "POST";
|
|
1297
|
-
|
|
1298
|
-
|
|
1295
|
+
if (version) {
|
|
1296
|
+
if (dispatchNamespace) {
|
|
1297
|
+
throw new Error(
|
|
1298
|
+
"Worker Preview Versions are not supported in Workers for Platforms",
|
|
1299
|
+
);
|
|
1300
|
+
}
|
|
1299
1301
|
// Upload worker version using the versions API
|
|
1300
1302
|
endpoint = `/accounts/${api.accountId}/workers/scripts/${workerName}/versions`;
|
|
1301
1303
|
method = "POST";
|
|
@@ -1322,16 +1324,43 @@ async function putWorkerInternal(
|
|
|
1322
1324
|
|
|
1323
1325
|
// Check if the upload was successful
|
|
1324
1326
|
if (!uploadResponse.ok) {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1327
|
+
try {
|
|
1328
|
+
return await handleApiError(
|
|
1329
|
+
uploadResponse,
|
|
1330
|
+
version ? "uploading worker version" : "uploading worker script",
|
|
1331
|
+
"worker",
|
|
1332
|
+
workerName,
|
|
1333
|
+
);
|
|
1334
|
+
} catch (error) {
|
|
1335
|
+
if (error instanceof CloudflareApiError && error.status === 412) {
|
|
1336
|
+
// this happens when adopting a Worker managed with Wrangler
|
|
1337
|
+
// because wrangler includes a migration tag and we do not
|
|
1338
|
+
// currently, the only way to discover the old_tag is through the error message
|
|
1339
|
+
// Get Worker Script Settings is meant to return it (according to the docs)
|
|
1340
|
+
// but it doesn't work at runtime
|
|
1341
|
+
//
|
|
1342
|
+
// so, we catch the error and parse out the tag and then retry
|
|
1343
|
+
if (error.message.includes("when expected tag is")) {
|
|
1344
|
+
const newTag = error.message.match(
|
|
1345
|
+
/when expected tag is ['"]?(v\d+)['"]?/,
|
|
1346
|
+
)?.[1];
|
|
1347
|
+
if (newTag) {
|
|
1348
|
+
return await putWorker(api, {
|
|
1349
|
+
...props,
|
|
1350
|
+
migrationTag: newTag,
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
} else {
|
|
1354
|
+
throw error;
|
|
1355
|
+
}
|
|
1356
|
+
} else {
|
|
1357
|
+
throw error;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1331
1360
|
}
|
|
1332
1361
|
|
|
1333
1362
|
// Handle version response
|
|
1334
|
-
if (
|
|
1363
|
+
if (props.version) {
|
|
1335
1364
|
const responseData = (await uploadResponse.json()) as {
|
|
1336
1365
|
result: {
|
|
1337
1366
|
id: string;
|
|
@@ -1390,27 +1419,6 @@ async function putWorkerInternal(
|
|
|
1390
1419
|
);
|
|
1391
1420
|
}
|
|
1392
1421
|
|
|
1393
|
-
export async function putWorker(
|
|
1394
|
-
api: CloudflareApi,
|
|
1395
|
-
workerName: string,
|
|
1396
|
-
scriptBundle: string | NoBundleResult,
|
|
1397
|
-
scriptMetadata: WorkerMetadata,
|
|
1398
|
-
dispatchNamespace?: string,
|
|
1399
|
-
version?: { versionLabel: string; message?: string },
|
|
1400
|
-
): Promise<{ versionId?: string; previewUrl?: string }> {
|
|
1401
|
-
return await putWorkerInternal(
|
|
1402
|
-
api,
|
|
1403
|
-
workerName,
|
|
1404
|
-
scriptBundle,
|
|
1405
|
-
scriptMetadata,
|
|
1406
|
-
{
|
|
1407
|
-
dispatchNamespace,
|
|
1408
|
-
versionLabel: version?.versionLabel,
|
|
1409
|
-
message: version?.message,
|
|
1410
|
-
},
|
|
1411
|
-
);
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
1422
|
export async function checkWorkerExists(
|
|
1415
1423
|
api: CloudflareApi,
|
|
1416
1424
|
workerName: string,
|
|
@@ -1494,13 +1502,6 @@ export async function configureURL<B extends Bindings>(
|
|
|
1494
1502
|
|
|
1495
1503
|
if (subdomain) {
|
|
1496
1504
|
workerUrl = `https://${workerName}.${subdomain}.workers.dev`;
|
|
1497
|
-
|
|
1498
|
-
// Add a delay when the subdomain is first created.
|
|
1499
|
-
// This is to prevent an issue where a negative cache-hit
|
|
1500
|
-
// causes the subdomain to be unavailable for 30 seconds.
|
|
1501
|
-
if (ctx.phase === "create" || !ctx.output?.url) {
|
|
1502
|
-
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
1503
|
-
}
|
|
1504
1505
|
}
|
|
1505
1506
|
} else if (url === false && ctx.output?.url) {
|
|
1506
1507
|
// Explicitly disable URL if it was previously enabled
|
|
@@ -1538,51 +1539,6 @@ export async function getWorkerScriptMetadata(
|
|
|
1538
1539
|
return ((await response.json()) as any).result as WorkerScriptMetadata;
|
|
1539
1540
|
}
|
|
1540
1541
|
|
|
1541
|
-
async function getWorkerBindings(api: CloudflareApi, workerName: string) {
|
|
1542
|
-
// Fetch the bindings for a worker by calling the Cloudflare API endpoint:
|
|
1543
|
-
// GET /accounts/:account_id/workers/scripts/:script_name/bindings
|
|
1544
|
-
// See: https://developers.cloudflare.com/api/resources/workers/subresources/scripts/subresources/script_and_version_settings/methods/get/
|
|
1545
|
-
const response = await api.get(
|
|
1546
|
-
`/accounts/${api.accountId}/workers/scripts/${workerName}/settings`,
|
|
1547
|
-
);
|
|
1548
|
-
if (response.status === 404) {
|
|
1549
|
-
return undefined;
|
|
1550
|
-
}
|
|
1551
|
-
if (!response.ok) {
|
|
1552
|
-
throw new Error(
|
|
1553
|
-
`Error getting worker bindings: ${response.status} ${response.statusText}`,
|
|
1554
|
-
);
|
|
1555
|
-
}
|
|
1556
|
-
// The result is an object with a "result" property containing the bindings array
|
|
1557
|
-
const { result, success, errors } = (await response.json()) as {
|
|
1558
|
-
result: {
|
|
1559
|
-
bindings: WorkerBindingSpec[];
|
|
1560
|
-
compatibility_date: string;
|
|
1561
|
-
compatibility_flags: string[];
|
|
1562
|
-
[key: string]: any;
|
|
1563
|
-
};
|
|
1564
|
-
success: boolean;
|
|
1565
|
-
errors: Array<{
|
|
1566
|
-
code: number;
|
|
1567
|
-
message: string;
|
|
1568
|
-
documentation_url: string;
|
|
1569
|
-
[key: string]: any;
|
|
1570
|
-
}>;
|
|
1571
|
-
messages: Array<{
|
|
1572
|
-
code: number;
|
|
1573
|
-
message: string;
|
|
1574
|
-
documentation_url: string;
|
|
1575
|
-
[key: string]: any;
|
|
1576
|
-
}>;
|
|
1577
|
-
};
|
|
1578
|
-
if (!success) {
|
|
1579
|
-
throw new Error(
|
|
1580
|
-
`Error getting worker bindings: ${response.status} ${response.statusText}\nErrors:\n${errors.map((e) => `- [${e.code}] ${e.message} (${e.documentation_url})`).join("\n")}`,
|
|
1581
|
-
);
|
|
1582
|
-
}
|
|
1583
|
-
return result.bindings;
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
1542
|
/**
|
|
1587
1543
|
* Lists and deletes all queue consumers for a specific worker
|
|
1588
1544
|
* @param ctx Worker context containing eventSources
|
package/src/stripe/price.ts
CHANGED
|
@@ -32,6 +32,12 @@ export interface PriceRecurring {
|
|
|
32
32
|
* `last_ever` for picking the last usage record ever (across period bounds) or `max` which picks the usage record with the maximum reported usage during a period.
|
|
33
33
|
*/
|
|
34
34
|
aggregateUsage?: Stripe.PriceCreateParams.Recurring.AggregateUsage;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The ID of the billing meter this price is associated with.
|
|
38
|
+
* Only applicable when usageType = 'metered'.
|
|
39
|
+
*/
|
|
40
|
+
meter?: string;
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
type TaxBehavior = Stripe.PriceCreateParams.TaxBehavior;
|
|
@@ -320,6 +326,25 @@ export interface Price extends Resource<"stripe::Price">, PriceProps {
|
|
|
320
326
|
* }
|
|
321
327
|
* ]
|
|
322
328
|
* });
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* // Create a metered price with billing meter for API usage tracking
|
|
332
|
+
* const meteredPrice = await Price("api-usage-price", {
|
|
333
|
+
* product: "prod_xyz",
|
|
334
|
+
* currency: "usd",
|
|
335
|
+
* billingScheme: "tiered",
|
|
336
|
+
* tiersMode: "graduated",
|
|
337
|
+
* recurring: {
|
|
338
|
+
* interval: "month",
|
|
339
|
+
* usageType: "metered",
|
|
340
|
+
* meter: "meter_123abc" // Associate with billing meter
|
|
341
|
+
* },
|
|
342
|
+
* tiers: [
|
|
343
|
+
* { upTo: 10000, unitAmountDecimal: "0" },
|
|
344
|
+
* { upTo: 25000, unitAmountDecimal: "0.002" },
|
|
345
|
+
* { upTo: "inf", flatAmountDecimal: "3000" }
|
|
346
|
+
* ]
|
|
347
|
+
* });
|
|
323
348
|
*/
|
|
324
349
|
export const Price = Resource(
|
|
325
350
|
"stripe::Price",
|
|
@@ -370,6 +395,9 @@ export const Price = Resource(
|
|
|
370
395
|
expand: ["tiers"],
|
|
371
396
|
};
|
|
372
397
|
|
|
398
|
+
// Note: Stripe doesn't allow updating recurring fields (including meter) after price creation
|
|
399
|
+
// If meter needs to be changed, a new price must be created
|
|
400
|
+
|
|
373
401
|
price = await stripe.prices.update(this.output.id, updateParams);
|
|
374
402
|
} else {
|
|
375
403
|
// Create new price
|
|
@@ -401,6 +429,21 @@ export const Price = Resource(
|
|
|
401
429
|
usage_type: props.recurring.usageType,
|
|
402
430
|
aggregate_usage: props.recurring.aggregateUsage,
|
|
403
431
|
};
|
|
432
|
+
|
|
433
|
+
// Add meter to recurring if present (only for metered usage type)
|
|
434
|
+
if (props.recurring.meter) {
|
|
435
|
+
if (props.recurring.usageType !== "metered") {
|
|
436
|
+
throw new Error(
|
|
437
|
+
"Meter can only be set for prices with recurring.usageType = 'metered'",
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
// Extend the recurring params with meter
|
|
441
|
+
(
|
|
442
|
+
createParams.recurring as Stripe.PriceCreateParams.Recurring & {
|
|
443
|
+
meter: string;
|
|
444
|
+
}
|
|
445
|
+
).meter = props.recurring.meter;
|
|
446
|
+
}
|
|
404
447
|
}
|
|
405
448
|
|
|
406
449
|
// Add tier configuration if present
|
|
@@ -452,7 +495,7 @@ export const Price = Resource(
|
|
|
452
495
|
// Need to retrieve with expanded tiers
|
|
453
496
|
price = await stripe.prices.retrieve(existingPrices.data[0].id, {
|
|
454
497
|
expand: ["tiers"],
|
|
455
|
-
}
|
|
498
|
+
});
|
|
456
499
|
}
|
|
457
500
|
} else {
|
|
458
501
|
price = await stripe.prices.create(createParams);
|
|
@@ -472,6 +515,9 @@ export const Price = Resource(
|
|
|
472
515
|
.usage_type as Stripe.PriceCreateParams.Recurring.UsageType,
|
|
473
516
|
aggregateUsage: price.recurring
|
|
474
517
|
.aggregate_usage as Stripe.PriceCreateParams.Recurring.AggregateUsage,
|
|
518
|
+
meter: (
|
|
519
|
+
price.recurring as Stripe.Price.Recurring & { meter?: string }
|
|
520
|
+
).meter,
|
|
475
521
|
}
|
|
476
522
|
: undefined;
|
|
477
523
|
|