Fix tabbing
This commit is contained in:
+4
-1
@@ -2,4 +2,7 @@
|
|||||||
//
|
//
|
||||||
// For a full list of overridable settings, and general information on folder-specific settings,
|
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||||
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
||||||
{}
|
{
|
||||||
|
"tab_size": 4,
|
||||||
|
"hard_tabs": false
|
||||||
|
}
|
||||||
|
|||||||
+12
-12
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "@blade-and-brawn/api",
|
"name": "@blade-and-brawn/api",
|
||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@elysiajs/cors": "^1.4.1",
|
"@elysiajs/cors": "^1.4.2",
|
||||||
"elysia": "^1.4.27",
|
"elysia": "^1.4.28",
|
||||||
"zipcodes-us": "^1.1.2",
|
"zipcodes-us": "^1.1.3",
|
||||||
"@blade-and-brawn/calculator": "workspace:*",
|
"@blade-and-brawn/calculator": "workspace:*",
|
||||||
"@blade-and-brawn/commerce": "workspace:*",
|
"@blade-and-brawn/commerce": "workspace:*",
|
||||||
"ml-levenberg-marquardt": "^5.0.0"
|
"ml-levenberg-marquardt": "^5.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+186
-173
@@ -1,195 +1,208 @@
|
|||||||
import {
|
import {
|
||||||
DEFAULT_STANDARDS_DATA,
|
DEFAULT_STANDARDS_DATA,
|
||||||
LevelCalculator,
|
LevelCalculator,
|
||||||
Standards,
|
Standards,
|
||||||
type ActivityPerformance,
|
type ActivityPerformance,
|
||||||
type Player,
|
type Player,
|
||||||
} from "@blade-and-brawn/calculator";
|
} from "@blade-and-brawn/calculator";
|
||||||
import { cors } from "@elysiajs/cors";
|
import { cors } from "@elysiajs/cors";
|
||||||
import { Elysia, NotFoundError } from "elysia";
|
import { Elysia, NotFoundError } from "elysia";
|
||||||
import { PrintfulService, SyncService, WebflowService, Printful, Webflow, FetchError } from "@blade-and-brawn/commerce";
|
import {
|
||||||
|
PrintfulService,
|
||||||
|
SyncService,
|
||||||
|
WebflowService,
|
||||||
|
Printful,
|
||||||
|
Webflow,
|
||||||
|
FetchError,
|
||||||
|
} from "@blade-and-brawn/commerce";
|
||||||
import zipcodesUs from "zipcodes-us";
|
import zipcodesUs from "zipcodes-us";
|
||||||
|
|
||||||
const levelCalculator = new LevelCalculator(new Standards(DEFAULT_STANDARDS_DATA));
|
const levelCalculator = new LevelCalculator(
|
||||||
|
new Standards(DEFAULT_STANDARDS_DATA),
|
||||||
|
);
|
||||||
|
|
||||||
export const app = new Elysia()
|
export const app = new Elysia()
|
||||||
.use(
|
.use(
|
||||||
cors({
|
cors({
|
||||||
origin: [
|
origin: [
|
||||||
// bladeandbrawn.com (apex + any subdomain)
|
// bladeandbrawn.com (apex + any subdomain)
|
||||||
/^https?:\/\/([a-z0-9-]+\.)?bladeandbrawn\.com$/i,
|
/^https?:\/\/([a-z0-9-]+\.)?bladeandbrawn\.com$/i,
|
||||||
// Webflow preview domains
|
// Webflow preview domains
|
||||||
/^https?:\/\/([a-z0-9-]+\.)?bladeandbrawn\.webflow\.io$/i,
|
/^https?:\/\/([a-z0-9-]+\.)?bladeandbrawn\.webflow\.io$/i,
|
||||||
// Fly app host (this one already works)
|
// Fly app host (this one already works)
|
||||||
/^https?:\/\/blade-and-brawn\.fly\.dev$/i,
|
/^https?:\/\/blade-and-brawn\.fly\.dev$/i,
|
||||||
// Local dev portal
|
// Local dev portal
|
||||||
"http://localhost:5173",
|
"http://localhost:5173",
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
.error({
|
.error({
|
||||||
FetchError,
|
FetchError,
|
||||||
})
|
})
|
||||||
|
|
||||||
.onError(async ({ code, error }) => {
|
.onError(async ({ code, error }) => {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case "FetchError":
|
case "FetchError":
|
||||||
console.error(error.message, await error.parse());
|
console.error(error.message, await error.parse());
|
||||||
return error;
|
return error;
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
.onAfterHandle(({ request, set }) => {
|
|
||||||
console.log(
|
|
||||||
JSON.stringify({
|
|
||||||
lvl: "info",
|
|
||||||
msg: "req",
|
|
||||||
method: request.method,
|
|
||||||
path: new URL(request.url).pathname,
|
|
||||||
status: set.status ?? 200,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
|
|
||||||
.get("/", () => "Hello world")
|
|
||||||
|
|
||||||
// CALCULATOR
|
|
||||||
|
|
||||||
.post("/calculate", async ({ body, query }) => {
|
|
||||||
interface CalcRequest {
|
|
||||||
player: Player;
|
|
||||||
activityPerformances: ActivityPerformance[];
|
|
||||||
}
|
|
||||||
const { player, activityPerformances } = body as CalcRequest;
|
|
||||||
const output = {
|
|
||||||
levels: levelCalculator.calculate(player, activityPerformances),
|
|
||||||
};
|
|
||||||
|
|
||||||
// console.log({ log: query?.log });
|
|
||||||
// if (query?.log === "true") {
|
|
||||||
// void fetch("https://kv-logger.xominus.workers.dev", {
|
|
||||||
// method: "POST",
|
|
||||||
// headers: { "content-type": "application/json" },
|
|
||||||
// body: JSON.stringify({ output, player, activityPerformances }),
|
|
||||||
// }).catch(() => {});
|
|
||||||
// }
|
|
||||||
|
|
||||||
return output;
|
|
||||||
})
|
|
||||||
|
|
||||||
// COMMERCE
|
|
||||||
// TODO: protect with api key/token
|
|
||||||
.group("/products", (app) =>
|
|
||||||
app
|
|
||||||
.group("/sync", (app) =>
|
|
||||||
app
|
|
||||||
.get("/", async ({ }) => SyncService.state)
|
|
||||||
.post("/:printfulProductId?", async ({ params, set }) => {
|
|
||||||
if (SyncService.state.isSyncing) {
|
|
||||||
set.status = 409;
|
|
||||||
return { error: "Sync already in progress" };
|
|
||||||
}
|
|
||||||
const printfulProductId = params.printfulProductId
|
|
||||||
? +params.printfulProductId
|
|
||||||
: undefined;
|
|
||||||
await SyncService.sync(printfulProductId);
|
|
||||||
return { ok: true };
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.get("/:printfulProductId", async ({ params }) => {
|
|
||||||
const printfulProductId = +params.printfulProductId;
|
|
||||||
const printfulProduct =
|
|
||||||
await PrintfulService.Products.get(printfulProductId);
|
|
||||||
if (!printfulProduct) {
|
|
||||||
return new NotFoundError();
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const webflowProductId =
|
.onAfterHandle(({ request, set }) => {
|
||||||
printfulProduct.sync_product.external_id.split("-")[0];
|
console.log(
|
||||||
if (!webflowProductId) {
|
JSON.stringify({
|
||||||
return new NotFoundError();
|
lvl: "info",
|
||||||
}
|
msg: "req",
|
||||||
|
method: request.method,
|
||||||
const webflowProduct =
|
path: new URL(request.url).pathname,
|
||||||
await WebflowService.Products.get(webflowProductId);
|
status: set.status ?? 200,
|
||||||
|
}),
|
||||||
return { printfulProduct, webflowProduct };
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
// WEBHOOKS
|
|
||||||
.post("/webhook/printful", async ({ body }) => {
|
|
||||||
const payload = body as Printful.Webhook.EventPayload;
|
|
||||||
|
|
||||||
switch (payload.type) {
|
|
||||||
case Printful.Webhook.Event.ProductUpdated: {
|
|
||||||
const printfulProduct = payload.data.sync_product;
|
|
||||||
await SyncService.sync(printfulProduct.id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Printful.Webhook.Event.ProductDeleted: {
|
|
||||||
await WebflowService.Products.remove(
|
|
||||||
payload.data.sync_product.external_id,
|
|
||||||
);
|
);
|
||||||
break;
|
})
|
||||||
}
|
|
||||||
case Printful.Webhook.Event.PackageShipped: {
|
|
||||||
const webflowOrderId = payload.data.order.external_id;
|
|
||||||
const shipInfo = payload.data.shipment;
|
|
||||||
|
|
||||||
await WebflowService.Orders.update(webflowOrderId, {
|
.get("/", () => "Hello world")
|
||||||
shippingTrackingURL: shipInfo.tracking_url,
|
|
||||||
shippingTracking: shipInfo.tracking_number,
|
|
||||||
shippingProvider: shipInfo.carrier,
|
|
||||||
});
|
|
||||||
await WebflowService.Orders.fulfill(webflowOrderId, {
|
|
||||||
sendOrderFulfilledEmail: true,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ok: true };
|
// CALCULATOR
|
||||||
})
|
|
||||||
.post("/webhook/webflow", async ({ request, body, set }) => {
|
|
||||||
if (!WebflowService.Util.verifyWebflowSignature(request, body)) {
|
|
||||||
set.status = 400;
|
|
||||||
return "Invalid signature";
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload = body as Webflow.Webhook.EventPayload;
|
.post("/calculate", async ({ body, query }) => {
|
||||||
|
interface CalcRequest {
|
||||||
|
player: Player;
|
||||||
|
activityPerformances: ActivityPerformance[];
|
||||||
|
}
|
||||||
|
const { player, activityPerformances } = body as CalcRequest;
|
||||||
|
const output = {
|
||||||
|
levels: levelCalculator.calculate(player, activityPerformances),
|
||||||
|
};
|
||||||
|
|
||||||
switch (payload.triggerType) {
|
// console.log({ log: query?.log });
|
||||||
case Webflow.Webhook.Event.OrderCreated: {
|
// if (query?.log === "true") {
|
||||||
const webflowOrder = payload.payload;
|
// void fetch("https://kv-logger.xominus.workers.dev", {
|
||||||
|
// method: "POST",
|
||||||
|
// headers: { "content-type": "application/json" },
|
||||||
|
// body: JSON.stringify({ output, player, activityPerformances }),
|
||||||
|
// }).catch(() => {});
|
||||||
|
// }
|
||||||
|
|
||||||
await PrintfulService.Orders.create({
|
return output;
|
||||||
external_id: webflowOrder.orderId,
|
})
|
||||||
// TODO: derive from webflow
|
|
||||||
shipping: "STANDARD",
|
|
||||||
recipient: {
|
|
||||||
name: webflowOrder.shippingAddress.addressee,
|
|
||||||
address1: webflowOrder.shippingAddress.line1,
|
|
||||||
address2: webflowOrder.shippingAddress.line2,
|
|
||||||
city: webflowOrder.shippingAddress.city,
|
|
||||||
state_code: zipcodesUs.find(
|
|
||||||
webflowOrder.shippingAddress.postalCode.split("-")[0] ?? "",
|
|
||||||
).stateCode,
|
|
||||||
country_code: webflowOrder.shippingAddress.country,
|
|
||||||
zip: webflowOrder.shippingAddress.postalCode,
|
|
||||||
},
|
|
||||||
items: webflowOrder.purchasedItems.map((webflowOrderSku) => ({
|
|
||||||
external_variant_id: webflowOrderSku.variantId,
|
|
||||||
quantity: webflowOrderSku.count,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
// COMMERCE
|
||||||
}
|
// TODO: protect with api key/token
|
||||||
}
|
.group("/products", (app) =>
|
||||||
|
app
|
||||||
|
.group("/sync", (app) =>
|
||||||
|
app
|
||||||
|
.get("/", async ({}) => SyncService.state)
|
||||||
|
.post("/:printfulProductId?", async ({ params, set }) => {
|
||||||
|
if (SyncService.state.isSyncing) {
|
||||||
|
set.status = 409;
|
||||||
|
return { error: "Sync already in progress" };
|
||||||
|
}
|
||||||
|
const printfulProductId = params.printfulProductId
|
||||||
|
? +params.printfulProductId
|
||||||
|
: undefined;
|
||||||
|
await SyncService.sync(printfulProductId);
|
||||||
|
return { ok: true };
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.get("/:printfulProductId", async ({ params }) => {
|
||||||
|
const printfulProductId = +params.printfulProductId;
|
||||||
|
const printfulProduct =
|
||||||
|
await PrintfulService.Products.get(printfulProductId);
|
||||||
|
if (!printfulProduct) {
|
||||||
|
return new NotFoundError();
|
||||||
|
}
|
||||||
|
|
||||||
return { ok: true };
|
const webflowProductId =
|
||||||
});
|
printfulProduct.sync_product.external_id.split("-")[0];
|
||||||
|
if (!webflowProductId) {
|
||||||
|
return new NotFoundError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const webflowProduct =
|
||||||
|
await WebflowService.Products.get(webflowProductId);
|
||||||
|
|
||||||
|
return { printfulProduct, webflowProduct };
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
// WEBHOOKS
|
||||||
|
.post("/webhook/printful", async ({ body }) => {
|
||||||
|
const payload = body as Printful.Webhook.EventPayload;
|
||||||
|
|
||||||
|
switch (payload.type) {
|
||||||
|
case Printful.Webhook.Event.ProductUpdated: {
|
||||||
|
const printfulProduct = payload.data.sync_product;
|
||||||
|
await SyncService.sync(printfulProduct.id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Printful.Webhook.Event.ProductDeleted: {
|
||||||
|
await WebflowService.Products.remove(
|
||||||
|
payload.data.sync_product.external_id,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Printful.Webhook.Event.PackageShipped: {
|
||||||
|
const webflowOrderId = payload.data.order.external_id;
|
||||||
|
const shipInfo = payload.data.shipment;
|
||||||
|
|
||||||
|
await WebflowService.Orders.update(webflowOrderId, {
|
||||||
|
shippingTrackingURL: shipInfo.tracking_url,
|
||||||
|
shippingTracking: shipInfo.tracking_number,
|
||||||
|
shippingProvider: shipInfo.carrier,
|
||||||
|
});
|
||||||
|
await WebflowService.Orders.fulfill(webflowOrderId, {
|
||||||
|
sendOrderFulfilledEmail: true,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ok: true };
|
||||||
|
})
|
||||||
|
.post("/webhook/webflow", async ({ request, body, set }) => {
|
||||||
|
if (!WebflowService.Util.verifyWebflowSignature(request, body)) {
|
||||||
|
set.status = 400;
|
||||||
|
return "Invalid signature";
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = body as Webflow.Webhook.EventPayload;
|
||||||
|
|
||||||
|
switch (payload.triggerType) {
|
||||||
|
case Webflow.Webhook.Event.OrderCreated: {
|
||||||
|
const webflowOrder = payload.payload;
|
||||||
|
|
||||||
|
await PrintfulService.Orders.create({
|
||||||
|
external_id: webflowOrder.orderId,
|
||||||
|
// TODO: derive from webflow
|
||||||
|
shipping: "STANDARD",
|
||||||
|
recipient: {
|
||||||
|
name: webflowOrder.shippingAddress.addressee,
|
||||||
|
address1: webflowOrder.shippingAddress.line1,
|
||||||
|
address2: webflowOrder.shippingAddress.line2,
|
||||||
|
city: webflowOrder.shippingAddress.city,
|
||||||
|
state_code: zipcodesUs.find(
|
||||||
|
webflowOrder.shippingAddress.postalCode.split(
|
||||||
|
"-",
|
||||||
|
)[0] ?? "",
|
||||||
|
).stateCode,
|
||||||
|
country_code: webflowOrder.shippingAddress.country,
|
||||||
|
zip: webflowOrder.shippingAddress.postalCode,
|
||||||
|
},
|
||||||
|
items: webflowOrder.purchasedItems.map(
|
||||||
|
(webflowOrderSku) => ({
|
||||||
|
external_variant_id: webflowOrderSku.variantId,
|
||||||
|
quantity: webflowOrderSku.count,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ok: true };
|
||||||
|
});
|
||||||
|
|
||||||
app.listen(3000);
|
app.listen(3000);
|
||||||
|
|||||||
@@ -1,87 +1,87 @@
|
|||||||
import {
|
import {
|
||||||
type Levels,
|
type Levels,
|
||||||
DEFAULT_STANDARDS_DATA,
|
DEFAULT_STANDARDS_DATA,
|
||||||
} from "@blade-and-brawn/calculator";
|
} from "@blade-and-brawn/calculator";
|
||||||
import { Activity, clamp, Gender, range } from "@blade-and-brawn/calculator";
|
import { Activity, clamp, Gender, range } from "@blade-and-brawn/calculator";
|
||||||
import { levenbergMarquardt as LM } from "ml-levenberg-marquardt";
|
import { levenbergMarquardt as LM } from "ml-levenberg-marquardt";
|
||||||
|
|
||||||
for (const activity of Object.values(Activity)) {
|
for (const activity of Object.values(Activity)) {
|
||||||
const DATASET_MAX_LEVEL = 5;
|
const DATASET_MAX_LEVEL = 5;
|
||||||
const NEW_LEVEL_COUNT = 2;
|
const NEW_LEVEL_COUNT = 2;
|
||||||
const RATIO_CLAMP = 1.4;
|
const RATIO_CLAMP = 1.4;
|
||||||
|
|
||||||
function expDecayModel([A, B, C]: number[]) {
|
function expDecayModel([A, B, C]: number[]) {
|
||||||
return (i: number) => A! * Math.exp(-B! * i) + C!;
|
return (i: number) => A! * Math.exp(-B! * i) + C!;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const gender of Object.values(Gender)) {
|
for (const gender of Object.values(Gender)) {
|
||||||
const standardsByGender = DEFAULT_STANDARDS_DATA[activity].standards.filter(
|
const standardsByGender = DEFAULT_STANDARDS_DATA[
|
||||||
(s) => s["metrics"].gender === gender,
|
activity
|
||||||
);
|
].standards.filter((s) => s["metrics"].gender === gender);
|
||||||
const ages = [...new Set(standardsByGender.map((s) => s.metrics.age))];
|
const ages = [...new Set(standardsByGender.map((s) => s.metrics.age))];
|
||||||
for (const age of ages) {
|
for (const age of ages) {
|
||||||
const standards = standardsByGender.filter(
|
const standards = standardsByGender.filter(
|
||||||
(s) => s.metrics.age === age,
|
(s) => s.metrics.age === age,
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
x: [] as number[],
|
x: [] as number[],
|
||||||
y: [] as number[],
|
y: [] as number[],
|
||||||
};
|
};
|
||||||
|
|
||||||
const isIncreasing =
|
const isIncreasing =
|
||||||
standards[0]!.levels["2"]! > standards[0]!.levels["1"]!;
|
standards[0]!.levels["2"]! > standards[0]!.levels["1"]!;
|
||||||
|
|
||||||
for (const i of range(DATASET_MAX_LEVEL).slice(0, -1)) {
|
for (const i of range(DATASET_MAX_LEVEL).slice(0, -1)) {
|
||||||
const level = i + 1;
|
const level = i + 1;
|
||||||
const progressionRatios: number[] = [];
|
const progressionRatios: number[] = [];
|
||||||
for (const standard of standards) {
|
for (const standard of standards) {
|
||||||
const curr = standard.levels[level]!;
|
const curr = standard.levels[level]!;
|
||||||
const next = standard.levels[level]!;
|
const next = standard.levels[level]!;
|
||||||
progressionRatios.push(
|
progressionRatios.push(
|
||||||
isIncreasing ? next / curr : curr / next,
|
isIncreasing ? next / curr : curr / next,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sum = progressionRatios.reduce((p, c) => p + c, 0);
|
const sum = progressionRatios.reduce((p, c) => p + c, 0);
|
||||||
const progressionRatioAvg = sum / progressionRatios.length;
|
const progressionRatioAvg = sum / progressionRatios.length;
|
||||||
|
|
||||||
data.x.push(i);
|
data.x.push(i);
|
||||||
data.y.push(progressionRatioAvg);
|
data.y.push(progressionRatioAvg);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fittedParams = LM(data, expDecayModel, {
|
const fittedParams = LM(data, expDecayModel, {
|
||||||
initialValues: [0.4, 0.5, 1.1],
|
initialValues: [0.4, 0.5, 1.1],
|
||||||
minValues: [0.0, 0.0, 1.02],
|
minValues: [0.0, 0.0, 1.02],
|
||||||
maxValues: [1.0, 2.0, 1.25],
|
maxValues: [1.0, 2.0, 1.25],
|
||||||
maxIterations: 200,
|
maxIterations: 200,
|
||||||
});
|
});
|
||||||
const getDecayRatio = (i: number) =>
|
const getDecayRatio = (i: number) =>
|
||||||
clamp(
|
clamp(
|
||||||
expDecayModel(fittedParams.parameterValues)(i),
|
expDecayModel(fittedParams.parameterValues)(i),
|
||||||
1,
|
1,
|
||||||
RATIO_CLAMP,
|
RATIO_CLAMP,
|
||||||
);
|
);
|
||||||
|
|
||||||
// UPDATE THE DATA
|
// UPDATE THE DATA
|
||||||
for (const standard of standards) {
|
for (const standard of standards) {
|
||||||
const newLevels: Levels = {};
|
const newLevels: Levels = {};
|
||||||
|
|
||||||
let prev = standard.levels["1"]!;
|
let prev = standard.levels["1"]!;
|
||||||
for (const i of range(NEW_LEVEL_COUNT)) {
|
for (const i of range(NEW_LEVEL_COUNT)) {
|
||||||
const level = i + 1;
|
const level = i + 1;
|
||||||
const ratio = getDecayRatio(level - NEW_LEVEL_COUNT);
|
const ratio = getDecayRatio(level - NEW_LEVEL_COUNT);
|
||||||
prev = isIncreasing ? prev / ratio : prev * ratio;
|
prev = isIncreasing ? prev / ratio : prev * ratio;
|
||||||
newLevels[level] = Math.round(prev);
|
newLevels[level] = Math.round(prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldLevels = standard.levels as Levels;
|
const oldLevels = standard.levels as Levels;
|
||||||
for (const i of range(DATASET_MAX_LEVEL)) {
|
for (const i of range(DATASET_MAX_LEVEL)) {
|
||||||
const level = i + 1;
|
const level = i + 1;
|
||||||
newLevels[level + NEW_LEVEL_COUNT] = oldLevels[level]!;
|
newLevels[level + NEW_LEVEL_COUNT] = oldLevels[level]!;
|
||||||
}
|
}
|
||||||
standard.levels = newLevels;
|
standard.levels = newLevels;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,75 +1,79 @@
|
|||||||
import { PrintfulService, SyncService, WebflowService } from "@blade-and-brawn/commerce";
|
import {
|
||||||
|
PrintfulService,
|
||||||
|
SyncService,
|
||||||
|
WebflowService,
|
||||||
|
} from "@blade-and-brawn/commerce";
|
||||||
|
|
||||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
type Validation = {
|
type Validation = {
|
||||||
isValid: boolean;
|
isValid: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateEquality = (wValue: any, pValue: any): Validation => {
|
const validateEquality = (wValue: any, pValue: any): Validation => {
|
||||||
const validation = {
|
const validation = {
|
||||||
isValid: true,
|
isValid: true,
|
||||||
message: "Valid",
|
message: "Valid",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (wValue !== pValue) {
|
if (wValue !== pValue) {
|
||||||
validation.isValid = false;
|
validation.isValid = false;
|
||||||
validation.message = `${wValue} !== ${pValue}`;
|
validation.message = `${wValue} !== ${pValue}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return validation;
|
return validation;
|
||||||
};
|
};
|
||||||
|
|
||||||
const wProducts = await WebflowService.Products.getAll();
|
const wProducts = await WebflowService.Products.getAll();
|
||||||
|
|
||||||
let invalidCount = 0;
|
let invalidCount = 0;
|
||||||
for (const wProduct of wProducts) {
|
for (const wProduct of wProducts) {
|
||||||
for (const sku of wProduct.skus ?? []) {
|
for (const sku of wProduct.skus ?? []) {
|
||||||
console.log("Webflow: " + sku.id, sku.fieldData.name);
|
console.log("Webflow: " + sku.id, sku.fieldData.name);
|
||||||
|
|
||||||
const pVariant = await PrintfulService.Products.Variants.get(
|
const pVariant = await PrintfulService.Products.Variants.get(
|
||||||
`@${sku.id}`,
|
`@${sku.id}`,
|
||||||
);
|
);
|
||||||
if (pVariant) {
|
if (pVariant) {
|
||||||
console.log("Printful: " + pVariant.id, pVariant.name);
|
console.log("Printful: " + pVariant.id, pVariant.name);
|
||||||
const validations = [
|
const validations = [
|
||||||
// validateEquality(sku.fieldData.name, pVariant.name),
|
// validateEquality(sku.fieldData.name, pVariant.name),
|
||||||
validateEquality(
|
validateEquality(
|
||||||
sku.fieldData["sku-values"]?.["color"],
|
sku.fieldData["sku-values"]?.["color"],
|
||||||
SyncService.findColorInProductName(pVariant.name),
|
SyncService.findColorInProductName(pVariant.name),
|
||||||
),
|
),
|
||||||
validateEquality(
|
validateEquality(
|
||||||
sku.fieldData["sku-values"]?.["size"],
|
sku.fieldData["sku-values"]?.["size"],
|
||||||
pVariant.size,
|
pVariant.size,
|
||||||
),
|
),
|
||||||
validateEquality(
|
validateEquality(
|
||||||
sku.fieldData.price.value,
|
sku.fieldData.price.value,
|
||||||
Math.floor(+pVariant.retail_price * 100),
|
Math.floor(+pVariant.retail_price * 100),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
// if (!validateEquality(sku.fieldData.name, pVariant.name).isValid) {
|
// if (!validateEquality(sku.fieldData.name, pVariant.name).isValid) {
|
||||||
// sku.fieldData.name = pVariant.name;
|
// sku.fieldData.name = pVariant.name;
|
||||||
// await WebflowService.Products.Skus.update(wProduct.product.id, sku.id, sku);
|
// await WebflowService.Products.Skus.update(wProduct.product.id, sku.id, sku);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const errors = validations.filter((v) => !v.isValid);
|
const errors = validations.filter((v) => !v.isValid);
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
++invalidCount;
|
++invalidCount;
|
||||||
console.log("INVALID: ");
|
console.log("INVALID: ");
|
||||||
console.log(errors.map((e) => e.message).join(", "));
|
console.log(errors.map((e) => e.message).join(", "));
|
||||||
} else {
|
} else {
|
||||||
console.log("VALID");
|
console.log("VALID");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
++invalidCount;
|
++invalidCount;
|
||||||
console.log("INVALID");
|
console.log("INVALID");
|
||||||
console.log("Not found");
|
console.log("Not found");
|
||||||
|
}
|
||||||
|
console.log();
|
||||||
}
|
}
|
||||||
console.log();
|
await sleep(3000);
|
||||||
}
|
|
||||||
await sleep(3000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("INVALID COUNT: " + invalidCount);
|
console.log("INVALID COUNT: " + invalidCount);
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json"
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-28
@@ -1,30 +1,30 @@
|
|||||||
{
|
{
|
||||||
"name": "@blade-and-brawn/portal",
|
"name": "@blade-and-brawn/portal",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/kit": "^2.54.0",
|
"@sveltejs/kit": "^2.65.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
||||||
"@types/bun": "^1.3.10",
|
"@types/bun": "^1.3.14",
|
||||||
"svelte": "^5.53.10",
|
"svelte": "^5.56.3",
|
||||||
"svelte-adapter-bun": "^0.5.2",
|
"svelte-adapter-bun": "^0.5.2",
|
||||||
"svelte-check": "^4.4.5",
|
"svelte-check": "^4.6.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.3.1"
|
"vite": "^7.3.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"prepare": "svelte-kit sync || echo ''",
|
"prepare": "svelte-kit sync || echo ''",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.3.1",
|
||||||
"daisyui": "^5.5.19",
|
"daisyui": "^5.5.23",
|
||||||
"memoirist": "^0.4.0",
|
"memoirist": "^0.4.0",
|
||||||
"tailwindcss": "^4.2.1",
|
"tailwindcss": "^4.3.1",
|
||||||
"@blade-and-brawn/calculator": "workspace:*",
|
"@blade-and-brawn/calculator": "workspace:*",
|
||||||
"@blade-and-brawn/commerce": "workspace:*"
|
"@blade-and-brawn/commerce": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { PrintfulService, WebflowService } from "@blade-and-brawn/commerce";
|
import { PrintfulService, WebflowService } from "@blade-and-brawn/commerce";
|
||||||
import type { PageServerLoad } from "./$types";
|
import type { PageServerLoad } from "./$types";
|
||||||
|
|
||||||
export const load = async ({ }: Parameters<PageServerLoad>[0]) => {
|
export const load = async ({}: Parameters<PageServerLoad>[0]) => {
|
||||||
return {
|
return {
|
||||||
products: {
|
products: {
|
||||||
printful: PrintfulService.Products.getAll(),
|
printful: PrintfulService.Products.getAll(),
|
||||||
webflow: WebflowService.Products.getAll(),
|
webflow: WebflowService.Products.getAll(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,6 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler"
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 0,
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "blade-and-brawn",
|
"name": "blade-and-brawn",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.3.9",
|
"@types/bun": "^1.3.14",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5",
|
"typescript": "^5.9.3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"apps/api": {
|
"apps/api": {
|
||||||
@@ -15,10 +16,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blade-and-brawn/calculator": "workspace:*",
|
"@blade-and-brawn/calculator": "workspace:*",
|
||||||
"@blade-and-brawn/commerce": "workspace:*",
|
"@blade-and-brawn/commerce": "workspace:*",
|
||||||
"@elysiajs/cors": "^1.4.1",
|
"@elysiajs/cors": "^1.4.2",
|
||||||
"elysia": "^1.4.27",
|
"elysia": "^1.4.28",
|
||||||
"ml-levenberg-marquardt": "^5.0.0",
|
"ml-levenberg-marquardt": "^5.0.1",
|
||||||
"zipcodes-us": "^1.1.2",
|
"zipcodes-us": "^1.1.3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"apps/portal": {
|
"apps/portal": {
|
||||||
@@ -32,12 +33,12 @@
|
|||||||
"tailwindcss": "^4.2.1",
|
"tailwindcss": "^4.2.1",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/kit": "^2.53.4",
|
"@sveltejs/kit": "^2.54.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
||||||
"@types/bun": "^1.3.9",
|
"@types/bun": "^1.3.10",
|
||||||
"svelte": "^5.53.6",
|
"svelte": "^5.53.10",
|
||||||
"svelte-adapter-bun": "^0.5.2",
|
"svelte-adapter-bun": "^0.5.2",
|
||||||
"svelte-check": "^4.4.4",
|
"svelte-check": "^4.4.5",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
},
|
},
|
||||||
@@ -65,7 +66,7 @@
|
|||||||
|
|
||||||
"@borewit/text-codec": ["@borewit/text-codec@0.2.1", "", {}, "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw=="],
|
"@borewit/text-codec": ["@borewit/text-codec@0.2.1", "", {}, "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw=="],
|
||||||
|
|
||||||
"@elysiajs/cors": ["@elysiajs/cors@1.4.1", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-lQfad+F3r4mNwsxRKbXyJB8Jg43oAOXjRwn7sKUL6bcOW3KjUqUimTS+woNpO97efpzjtDE0tEjGk9DTw8lqTQ=="],
|
"@elysiajs/cors": ["@elysiajs/cors@1.4.2", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-FTCcbH35brTLigF1W7BYySRZomgI/dBEMK9BgK9RP9Nez7zmpGh4koL/Yr1BFv8nYz7CfhRvcM8d/c+XnwMaVQ=="],
|
||||||
|
|
||||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
|
||||||
|
|
||||||
@@ -185,49 +186,51 @@
|
|||||||
|
|
||||||
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||||
|
|
||||||
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="],
|
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.10", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA=="],
|
||||||
|
|
||||||
"@sveltejs/kit": ["@sveltejs/kit@2.54.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.6.4", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-WDJApQ1ipZLbaC4YjqJjwYR9y7QQgTqVwEObgNZ8Mu/eVQJqn4Qzw9a+n7mr5xnBYiAYz9UdJOOl+aqVbfGXcA=="],
|
"@sveltejs/kit": ["@sveltejs/kit@2.65.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-Sa1rFYYqBB+zv3rIxAg/CsFskR/x4aj5BY/hvLxBd9r/mqbipxM945As1K3PqsDicJAyekPR0BlWoVIiw2OHYg=="],
|
||||||
|
|
||||||
|
"@sveltejs/load-config": ["@sveltejs/load-config@0.1.1", "", {}, "sha512-BXXm+VOH/9X4N7Dd1iZ2MqA1h7M+9i2noI8QYuLDY8QcN2WHYn7D/VK/+IJNfcAmRw7ACNJ538UT9GXIhnBTiA=="],
|
||||||
|
|
||||||
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", "magic-string": "^0.30.21", "obug": "^2.1.0", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA=="],
|
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", "magic-string": "^0.30.21", "obug": "^2.1.0", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA=="],
|
||||||
|
|
||||||
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.2", "", { "dependencies": { "obug": "^2.1.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig=="],
|
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.2", "", { "dependencies": { "obug": "^2.1.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig=="],
|
||||||
|
|
||||||
"@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="],
|
"@tailwindcss/node": ["@tailwindcss/node@4.3.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "5.21.6", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.1" } }, "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.1", "@tailwindcss/oxide-darwin-arm64": "4.2.1", "@tailwindcss/oxide-darwin-x64": "4.2.1", "@tailwindcss/oxide-freebsd-x64": "4.2.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", "@tailwindcss/oxide-linux-x64-musl": "4.2.1", "@tailwindcss/oxide-wasm32-wasi": "4.2.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw=="],
|
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.1", "@tailwindcss/oxide-darwin-arm64": "4.3.1", "@tailwindcss/oxide-darwin-x64": "4.3.1", "@tailwindcss/oxide-freebsd-x64": "4.3.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.1", "@tailwindcss/oxide-linux-arm64-musl": "4.3.1", "@tailwindcss/oxide-linux-x64-gnu": "4.3.1", "@tailwindcss/oxide-linux-x64-musl": "4.3.1", "@tailwindcss/oxide-wasm32-wasi": "4.3.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.1", "@tailwindcss/oxide-win32-x64-msvc": "4.3.1" } }, "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg=="],
|
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.1", "", { "os": "android", "cpu": "arm64" }, "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw=="],
|
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw=="],
|
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA=="],
|
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw=="],
|
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1", "", { "os": "linux", "cpu": "arm" }, "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ=="],
|
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ=="],
|
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g=="],
|
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g=="],
|
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.1", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q=="],
|
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.1", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA=="],
|
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ=="],
|
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.1", "", { "os": "win32", "cpu": "x64" }, "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA=="],
|
||||||
|
|
||||||
"@tailwindcss/vite": ["@tailwindcss/vite@4.2.1", "", { "dependencies": { "@tailwindcss/node": "4.2.1", "@tailwindcss/oxide": "4.2.1", "tailwindcss": "4.2.1" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w=="],
|
"@tailwindcss/vite": ["@tailwindcss/vite@4.3.1", "", { "dependencies": { "@tailwindcss/node": "4.3.1", "@tailwindcss/oxide": "4.3.1", "tailwindcss": "4.3.1" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ=="],
|
||||||
|
|
||||||
"@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="],
|
"@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="],
|
||||||
|
|
||||||
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
|
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
|
"@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
|
||||||
|
|
||||||
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
||||||
|
|
||||||
@@ -243,7 +246,7 @@
|
|||||||
|
|
||||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
"bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
|
||||||
|
|
||||||
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||||
|
|
||||||
@@ -251,7 +254,7 @@
|
|||||||
|
|
||||||
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
|
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
|
||||||
|
|
||||||
"daisyui": ["daisyui@5.5.19", "", {}, "sha512-pbFAkl1VCEh/MPCeclKL61I/MqRIFFhNU7yiXoDDRapXN4/qNCoMxeCCswyxEEhqL5eiTTfwHvucFtOE71C9sA=="],
|
"daisyui": ["daisyui@5.5.23", "", {}, "sha512-xuheNUSL4T6ZVtWXoioqcNkjoyGX85QTDz4HTw2aBPfqk4fuMjax5HDo8qCmpV6M1YN8bGvfx5BpYCoDeRlt+A=="],
|
||||||
|
|
||||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||||
|
|
||||||
@@ -259,17 +262,17 @@
|
|||||||
|
|
||||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||||
|
|
||||||
"devalue": ["devalue@5.6.4", "", {}, "sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA=="],
|
"devalue": ["devalue@5.8.1", "", {}, "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw=="],
|
||||||
|
|
||||||
"elysia": ["elysia@1.4.27", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-2UlmNEjPJVA/WZVPYKy+KdsrfFwwNlqSBW1lHz6i2AHc75k7gV4Rhm01kFeotH7PDiHIX2G8X3KnRPc33SGVIg=="],
|
"elysia": ["elysia@1.4.28", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-Vrx8sBnvq8squS/3yNBzR1jBXI+SgmnmvwawPjNuEHndUe5l1jV2Gp6JJ4ulDkEB8On6bWmmuyPpA+bq4t+WYg=="],
|
||||||
|
|
||||||
"enhanced-resolve": ["enhanced-resolve@5.20.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ=="],
|
"enhanced-resolve": ["enhanced-resolve@5.21.6", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ=="],
|
||||||
|
|
||||||
"esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
|
"esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
|
||||||
|
|
||||||
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
|
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
|
||||||
|
|
||||||
"esrap": ["esrap@2.2.3", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ=="],
|
"esrap": ["esrap@2.2.11", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-gPdx+I+BjYEinNMQaBXFjbaJVyoPMU4ZODg5mE+M4DqVG9VusAVHHjcBX+zqyITlI0DIARwDMMzZwAWj36dRoQ=="],
|
||||||
|
|
||||||
"exact-mirror": ["exact-mirror@0.2.7", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-+MeEmDcLA4o/vjK2zujgk+1VTxPR4hdp23qLqkWfStbECtAq9gmsvQa3LW6z/0GXZyHJobrCnmy1cdeE7BjsYg=="],
|
"exact-mirror": ["exact-mirror@0.2.7", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-+MeEmDcLA4o/vjK2zujgk+1VTxPR4hdp23qLqkWfStbECtAq9gmsvQa3LW6z/0GXZyHJobrCnmy1cdeE7BjsYg=="],
|
||||||
|
|
||||||
@@ -289,37 +292,37 @@
|
|||||||
|
|
||||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
"is-any-array": ["is-any-array@2.0.1", "", {}, "sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ=="],
|
"is-any-array": ["is-any-array@3.0.0", "", {}, "sha512-o4h+tylWykC4BD1vaejp6gDxoM13bwW8FGuNs4yIKpj8xbBJcRxJx8vZpq0dCr7ZDEfeKjmsi/euolKhX6f/ww=="],
|
||||||
|
|
||||||
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
|
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
|
||||||
|
|
||||||
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
"jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
|
||||||
|
|
||||||
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
||||||
|
|
||||||
"lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="],
|
"lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
|
||||||
|
|
||||||
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="],
|
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
|
||||||
|
|
||||||
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.31.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg=="],
|
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
|
||||||
|
|
||||||
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.31.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA=="],
|
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
|
||||||
|
|
||||||
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.31.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A=="],
|
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="],
|
||||||
|
|
||||||
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.31.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g=="],
|
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="],
|
||||||
|
|
||||||
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg=="],
|
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="],
|
||||||
|
|
||||||
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg=="],
|
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="],
|
||||||
|
|
||||||
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA=="],
|
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="],
|
||||||
|
|
||||||
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA=="],
|
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="],
|
||||||
|
|
||||||
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.31.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w=="],
|
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="],
|
||||||
|
|
||||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="],
|
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
|
||||||
|
|
||||||
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
||||||
|
|
||||||
@@ -327,15 +330,15 @@
|
|||||||
|
|
||||||
"memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="],
|
"memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="],
|
||||||
|
|
||||||
"ml-array-max": ["ml-array-max@1.2.4", "", { "dependencies": { "is-any-array": "^2.0.0" } }, "sha512-BlEeg80jI0tW6WaPyGxf5Sa4sqvcyY6lbSn5Vcv44lp1I2GR6AWojfUvLnGTNsIXrZ8uqWmo8VcG1WpkI2ONMQ=="],
|
"ml-array-max": ["ml-array-max@2.0.0", "", { "dependencies": { "is-any-array": "^3.0.0" } }, "sha512-QQZ4kENwpWmyNb98UXRDFXrmtIXuXtt1+bSbda/2KA85+F+rrJP8hZk6QOkCQXM2Th9mUDYdq/PNByPdT9ID4A=="],
|
||||||
|
|
||||||
"ml-array-min": ["ml-array-min@1.2.3", "", { "dependencies": { "is-any-array": "^2.0.0" } }, "sha512-VcZ5f3VZ1iihtrGvgfh/q0XlMobG6GQ8FsNyQXD3T+IlstDv85g8kfV0xUG1QPRO/t21aukaJowDzMTc7j5V6Q=="],
|
"ml-array-min": ["ml-array-min@2.0.0", "", { "dependencies": { "is-any-array": "^3.0.0" } }, "sha512-GRj6Ky6sW9vGL6yIjgsHmXZ9YgrdmcQ8nCxPqEGeKc6dkfYg1XDYxGFxADUjNuZyoCd5PUscWAS4N+cFaX6hFg=="],
|
||||||
|
|
||||||
"ml-array-rescale": ["ml-array-rescale@1.3.7", "", { "dependencies": { "is-any-array": "^2.0.0", "ml-array-max": "^1.2.4", "ml-array-min": "^1.2.3" } }, "sha512-48NGChTouvEo9KBctDfHC3udWnQKNKEWN0ziELvY3KG25GR5cA8K8wNVzracsqSW1QEkAXjTNx+ycgAv06/1mQ=="],
|
"ml-array-rescale": ["ml-array-rescale@2.0.0", "", { "dependencies": { "is-any-array": "^3.0.0", "ml-array-max": "^2.0.0", "ml-array-min": "^2.0.0" } }, "sha512-2GGtKfSno94/kIloWGvpp/U5Q5vLvLrza+SAaGsLeo6Xj4mEbA6Gqx+oTfZFkxnd1grT2X007HfJNs3T5BsiVg=="],
|
||||||
|
|
||||||
"ml-levenberg-marquardt": ["ml-levenberg-marquardt@5.0.0", "", { "dependencies": { "is-any-array": "^2.0.1", "ml-matrix": "^6.12.1" } }, "sha512-vCFoO2DyYKGZFp+KxofN2cAvSc/3gufJHj/oaDHA2nV+NZH509fD7m35zQR2ZXA8fepxpVrwgCAQ0aAIsV2WtA=="],
|
"ml-levenberg-marquardt": ["ml-levenberg-marquardt@5.0.1", "", { "dependencies": { "is-any-array": "^3.0.0", "ml-matrix": "^6.12.2" } }, "sha512-sOaxcZ2aIBiRrPi3v7RmqSuNmjNKHURUB9Ft2Vcr8MWes152VF1DDkcZz3N8Nj/cWbFB359cNg/o7Z99vt58Iw=="],
|
||||||
|
|
||||||
"ml-matrix": ["ml-matrix@6.12.1", "", { "dependencies": { "is-any-array": "^2.0.1", "ml-array-rescale": "^1.3.7" } }, "sha512-TJ+8eOFdp+INvzR4zAuwBQJznDUfktMtOB6g/hUcGh3rcyjxbz4Te57Pgri8Q9bhSQ7Zys4IYOGhFdnlgeB6Lw=="],
|
"ml-matrix": ["ml-matrix@6.12.2", "", { "dependencies": { "is-any-array": "^3.0.0", "ml-array-rescale": "^2.0.0" } }, "sha512-GC+BnW+pBh8Auap8goAxY0senAmF0IEoc3HNVSfnfbvGw0buuDIYb9kAKMS1l+GiwJ1rfK2bzJ8IHhwjzATSFA=="],
|
||||||
|
|
||||||
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
|
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
|
||||||
|
|
||||||
@@ -369,15 +372,15 @@
|
|||||||
|
|
||||||
"strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="],
|
"strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="],
|
||||||
|
|
||||||
"svelte": ["svelte@5.53.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.3", "esm-env": "^1.2.1", "esrap": "^2.2.2", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-UcNfWzbrjvYXYSk+U2hME25kpb87oq6/WVLeBF4khyQrb3Ob/URVlN23khal+RbdCUTMfg4qWjI9KZjCNFtYMQ=="],
|
"svelte": ["svelte@5.56.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.10", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.8.1", "esm-env": "^1.2.1", "esrap": "^2.2.11", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-w7JvrM5IFl5cmfbY0TLik9o7mjRUJmRMhOR51tBPu708Gr/MjbGs7VnJnr/B0CaXeI4vtnOh7RKxDr0cwhMdDA=="],
|
||||||
|
|
||||||
"svelte-adapter-bun": ["svelte-adapter-bun@0.5.2", "", { "dependencies": { "tiny-glob": "^0.2.9" } }, "sha512-xEtFgaal6UgrCwwkSIcapO9kopoFNUYCYqyKCikdqxX9bz2TDYnrWQZ7qBnkunMxi1HOIERUCvTcebYGiarZLA=="],
|
"svelte-adapter-bun": ["svelte-adapter-bun@0.5.2", "", { "dependencies": { "tiny-glob": "^0.2.9" } }, "sha512-xEtFgaal6UgrCwwkSIcapO9kopoFNUYCYqyKCikdqxX9bz2TDYnrWQZ7qBnkunMxi1HOIERUCvTcebYGiarZLA=="],
|
||||||
|
|
||||||
"svelte-check": ["svelte-check@4.4.5", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-1bSwIRCvvmSHrlK52fOlZmVtUZgil43jNL/2H18pRpa+eQjzGt6e3zayxhp1S7GajPFKNM/2PMCG+DZFHlG9fw=="],
|
"svelte-check": ["svelte-check@4.6.0", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "@sveltejs/load-config": "0.1.1", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-KhVnDFDSid57mmZtHz8gfW8AAGylOZ0vPnOIzVmAL+urzwK8sBYXRss953gD8T0OdgAQ11mdWhE6uadmtOz8TQ=="],
|
||||||
|
|
||||||
"tailwindcss": ["tailwindcss@4.2.1", "", {}, "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw=="],
|
"tailwindcss": ["tailwindcss@4.3.1", "", {}, "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q=="],
|
||||||
|
|
||||||
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
"tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="],
|
||||||
|
|
||||||
"tiny-glob": ["tiny-glob@0.2.9", "", { "dependencies": { "globalyzer": "0.1.0", "globrex": "^0.1.2" } }, "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg=="],
|
"tiny-glob": ["tiny-glob@0.2.9", "", { "dependencies": { "globalyzer": "0.1.0", "globrex": "^0.1.2" } }, "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg=="],
|
||||||
|
|
||||||
@@ -393,32 +396,26 @@
|
|||||||
|
|
||||||
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||||
|
|
||||||
"vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
|
"vite": ["vite@7.3.5", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww=="],
|
||||||
|
|
||||||
"vitefu": ["vitefu@1.1.2", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw=="],
|
"vitefu": ["vitefu@1.1.2", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw=="],
|
||||||
|
|
||||||
"zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="],
|
"zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="],
|
||||||
|
|
||||||
"zipcodes-us": ["zipcodes-us@1.1.2", "", {}, "sha512-UmUTS0O0/IHb0hbxv5r6IfATvD2B7FKcJTLsSv/dENBRiNACFUrGmhPuVss3kAhuhEpPH6NwKHJ7/rGMNXq5yA=="],
|
"zipcodes-us": ["zipcodes-us@1.1.3", "", {}, "sha512-gOz8WAO6iWBU4aUc0lljpa+TKZckRKmgoHKcc1jWnn4eTbdc9XO5cXEaGccYn7GAjykueO0Wn+N7zdmzG0Uf5w=="],
|
||||||
|
|
||||||
"@blade-and-brawn/portal/@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
|
|
||||||
|
|
||||||
"@sveltejs/kit/cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
|
"@sveltejs/kit/cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
|
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
|
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
|
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
|
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.5", "", { "dependencies": { "@tybys/wasm-util": "^0.10.2" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
"svelte/devalue": ["devalue@5.6.3", "", {}, "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg=="],
|
|
||||||
|
|
||||||
"@blade-and-brawn/portal/@types/bun/bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-18
@@ -6,7 +6,7 @@ function playAudio(newAudio) {
|
|||||||
// HELPERS
|
// HELPERS
|
||||||
// --------------
|
// --------------
|
||||||
const TIME_STEP = 500;
|
const TIME_STEP = 500;
|
||||||
const VOL_STEP = .05;
|
const VOL_STEP = 0.05;
|
||||||
|
|
||||||
function fadeOut(audio) {
|
function fadeOut(audio) {
|
||||||
const intervalId = setInterval(() => {
|
const intervalId = setInterval(() => {
|
||||||
@@ -14,7 +14,7 @@ function playAudio(newAudio) {
|
|||||||
if (audio.volume <= 0) {
|
if (audio.volume <= 0) {
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId);
|
||||||
}
|
}
|
||||||
}, TIME_STEP)
|
}, TIME_STEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fadeIn(audio) {
|
function fadeIn(audio) {
|
||||||
@@ -25,7 +25,7 @@ function playAudio(newAudio) {
|
|||||||
if (audio.volume >= 1) {
|
if (audio.volume >= 1) {
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId);
|
||||||
}
|
}
|
||||||
}, TIME_STEP)
|
}, TIME_STEP);
|
||||||
}
|
}
|
||||||
// --------------
|
// --------------
|
||||||
|
|
||||||
@@ -35,8 +35,7 @@ function playAudio(newAudio) {
|
|||||||
|
|
||||||
const prevAudio = currentAudio;
|
const prevAudio = currentAudio;
|
||||||
|
|
||||||
if (prevAudio === newAudio)
|
if (prevAudio === newAudio) return;
|
||||||
return;
|
|
||||||
|
|
||||||
if (prevAudio) {
|
if (prevAudio) {
|
||||||
fadeOut(prevAudio);
|
fadeOut(prevAudio);
|
||||||
@@ -48,7 +47,7 @@ function playAudio(newAudio) {
|
|||||||
// --------------
|
// --------------
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async function() {
|
document.addEventListener("DOMContentLoaded", async function () {
|
||||||
const $ = (id) => document.getElementById(id);
|
const $ = (id) => document.getElementById(id);
|
||||||
|
|
||||||
const classNames = ["rogue", "knight", "warrior"];
|
const classNames = ["rogue", "knight", "warrior"];
|
||||||
@@ -64,26 +63,28 @@ document.addEventListener("DOMContentLoaded", async function() {
|
|||||||
audios[className] = audio;
|
audios[className] = audio;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(classNames.map(async (className) => {
|
await Promise.all(
|
||||||
const audio = audios[className];
|
classNames.map(async (className) => {
|
||||||
try {
|
const audio = audios[className];
|
||||||
await audio.play();
|
try {
|
||||||
audio.pause();
|
await audio.play();
|
||||||
audio.currentTime = 0;
|
audio.pause();
|
||||||
} catch (e) {
|
audio.currentTime = 0;
|
||||||
console.warn(`Autoplay failed for ${className}:`, e);
|
} catch (e) {
|
||||||
}
|
console.warn(`Autoplay failed for ${className}:`, e);
|
||||||
}));
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
// Start all in perfect sync
|
// Start all in perfect sync
|
||||||
classNames.forEach(className => {
|
classNames.forEach((className) => {
|
||||||
const audio = audios[className];
|
const audio = audios[className];
|
||||||
audio.currentTime = 0;
|
audio.currentTime = 0;
|
||||||
audio.play();
|
audio.play();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Hook up buttons
|
// Hook up buttons
|
||||||
classNames.forEach(className => {
|
classNames.forEach((className) => {
|
||||||
$(`btn-${className}`).onclick = () => {
|
$(`btn-${className}`).onclick = () => {
|
||||||
$("currentClass").textContent = className;
|
$("currentClass").textContent = className;
|
||||||
playAudio(audios[className]);
|
playAudio(audios[className]);
|
||||||
|
|||||||
+2
-2
@@ -6,9 +6,9 @@
|
|||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.3.9"
|
"@types/bun": "^1.3.14"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,6 @@
|
|||||||
"ml-levenberg-marquardt": "^5.0.0"
|
"ml-levenberg-marquardt": "^5.0.0"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+805
-812
File diff suppressed because it is too large
Load Diff
@@ -6,34 +6,34 @@ import avgWeightDataRaw from "./data/avg-weights.json";
|
|||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
export type Player = {
|
export type Player = {
|
||||||
name?: string;
|
name?: string;
|
||||||
metrics: Metrics;
|
metrics: Metrics;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum Attribute {
|
export enum Attribute {
|
||||||
Strength = "Strength",
|
Strength = "Strength",
|
||||||
Power = "Power",
|
Power = "Power",
|
||||||
Endurance = "Endurance",
|
Endurance = "Endurance",
|
||||||
Agility = "Agility",
|
Agility = "Agility",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Activity {
|
export enum Activity {
|
||||||
BackSquat = "BackSquat",
|
BackSquat = "BackSquat",
|
||||||
Deadlift = "Deadlift",
|
Deadlift = "Deadlift",
|
||||||
BenchPress = "BenchPress",
|
BenchPress = "BenchPress",
|
||||||
Run = "Run",
|
Run = "Run",
|
||||||
BroadJump = "BroadJump",
|
BroadJump = "BroadJump",
|
||||||
ConeDrill = "ConeDrill",
|
ConeDrill = "ConeDrill",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ActivityPerformance {
|
export interface ActivityPerformance {
|
||||||
activity: Activity;
|
activity: Activity;
|
||||||
performance: number;
|
performance: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Gender {
|
export enum Gender {
|
||||||
Male = "Male",
|
Male = "Male",
|
||||||
Female = "Female",
|
Female = "Female",
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
@@ -54,47 +54,47 @@ export const inToCm = (inches: number) => inches * 2.54;
|
|||||||
export const cmToIn = (cm: number) => cm / 2.54;
|
export const cmToIn = (cm: number) => cm / 2.54;
|
||||||
|
|
||||||
export const msToTime = (ms: number, includeMs = false): string => {
|
export const msToTime = (ms: number, includeMs = false): string => {
|
||||||
const minutes = Math.floor(ms / 60000);
|
const minutes = Math.floor(ms / 60000);
|
||||||
const seconds = Math.floor((ms % 60000) / 1000);
|
const seconds = Math.floor((ms % 60000) / 1000);
|
||||||
const milliseconds = Math.floor(ms % 1000);
|
const milliseconds = Math.floor(ms % 1000);
|
||||||
|
|
||||||
let formattedTime = `${minutes}:${seconds.toString().padStart(2, "0")}`;
|
let formattedTime = `${minutes}:${seconds.toString().padStart(2, "0")}`;
|
||||||
if (includeMs) {
|
if (includeMs) {
|
||||||
formattedTime += `.${milliseconds.toString().padStart(3, "0")}`;
|
formattedTime += `.${milliseconds.toString().padStart(3, "0")}`;
|
||||||
}
|
}
|
||||||
return formattedTime;
|
return formattedTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const range = (length: number) =>
|
export const range = (length: number) =>
|
||||||
Array.from({ length: length }, (_, i) => i);
|
Array.from({ length: length }, (_, i) => i);
|
||||||
|
|
||||||
export const clamp = (x: number, lo: number, hi: number) =>
|
export const clamp = (x: number, lo: number, hi: number) =>
|
||||||
Math.max(lo, Math.min(hi, x));
|
Math.max(lo, Math.min(hi, x));
|
||||||
|
|
||||||
export const getAvgWeight = (gender: Gender, age: number) => {
|
export const getAvgWeight = (gender: Gender, age: number) => {
|
||||||
type AvgWeightData = {
|
type AvgWeightData = {
|
||||||
metadata: {
|
metadata: {
|
||||||
unit: StandardUnit;
|
unit: StandardUnit;
|
||||||
|
};
|
||||||
|
weights: Metrics[];
|
||||||
};
|
};
|
||||||
weights: Metrics[];
|
let avgWeightData = avgWeightDataRaw as AvgWeightData;
|
||||||
};
|
|
||||||
let avgWeightData = avgWeightDataRaw as AvgWeightData;
|
|
||||||
|
|
||||||
const avgWeights = avgWeightData.weights.filter((a) => a.gender === gender);
|
const avgWeights = avgWeightData.weights.filter((a) => a.gender === gender);
|
||||||
const firstAvgWeight = avgWeights[0];
|
const firstAvgWeight = avgWeights[0];
|
||||||
if (!firstAvgWeight) throw new Error("No average weights");
|
if (!firstAvgWeight) throw new Error("No average weights");
|
||||||
|
|
||||||
const closest = {
|
const closest = {
|
||||||
diff: Math.abs(firstAvgWeight.age - age),
|
diff: Math.abs(firstAvgWeight.age - age),
|
||||||
avg: firstAvgWeight,
|
avg: firstAvgWeight,
|
||||||
};
|
};
|
||||||
for (const avg of avgWeights) {
|
for (const avg of avgWeights) {
|
||||||
const diff = Math.abs(avg.age - age);
|
const diff = Math.abs(avg.age - age);
|
||||||
if (diff < closest.diff) {
|
if (diff < closest.diff) {
|
||||||
closest.diff = diff;
|
closest.diff = diff;
|
||||||
closest.avg = avg;
|
closest.avg = avg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return closest.avg.weight;
|
return closest.avg.weight;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"noUncheckedIndexedAccess": false,
|
"noUncheckedIndexedAccess": false
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,5 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
export * from "./printful"
|
export * from "./printful";
|
||||||
export * from "./sync"
|
export * from "./sync";
|
||||||
export * from "./webflow"
|
export * from "./webflow";
|
||||||
export * from "./webflow"
|
export * from "./util/misc";
|
||||||
export * from "./util/misc"
|
export * from "./util/types";
|
||||||
export * from "./util/types"
|
|
||||||
|
|||||||
+179
-179
@@ -2,206 +2,206 @@ import type { Printful } from "./util/types";
|
|||||||
import { FetchError, type DeepPartial } from "./util/misc";
|
import { FetchError, type DeepPartial } from "./util/misc";
|
||||||
|
|
||||||
export class PrintfulService {
|
export class PrintfulService {
|
||||||
static Products = class {
|
static Products = class {
|
||||||
static Variants = class {
|
static Variants = class {
|
||||||
static async get(
|
static async get(
|
||||||
variantId: number | string,
|
variantId: number | string,
|
||||||
): Promise<Printful.Products.SyncVariant | undefined> {
|
): Promise<Printful.Products.SyncVariant | undefined> {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${env().API_URL}/store/variants/${variantId}`,
|
`${env().API_URL}/store/variants/${variantId}`,
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: { ...env().AUTH_HEADERS },
|
headers: { ...env().AUTH_HEADERS },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res.status === 404 || res.status === 400) {
|
if (res.status === 404 || res.status === 400) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new FetchError("Failed to get Printful variant", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload =
|
||||||
|
(await res.json()) as Printful.Products.MetaDataSingle<Printful.Products.SyncVariant>;
|
||||||
|
return payload.result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static async getAll(
|
||||||
|
offset: number = 0,
|
||||||
|
): Promise<Printful.Products.SyncProduct[]> {
|
||||||
|
const allSyncProducts: Printful.Products.SyncProduct[] = [];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`${env().API_URL}/store/products?offset=${offset}`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: { ...env().AUTH_HEADERS },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new FetchError(
|
||||||
|
"Failed to get all Printful products",
|
||||||
|
res,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload =
|
||||||
|
(await res.json()) as Printful.Products.MetaDataMulti<Printful.Products.SyncProduct>;
|
||||||
|
|
||||||
|
allSyncProducts.push(...payload.result);
|
||||||
|
offset = allSyncProducts.length;
|
||||||
|
|
||||||
|
if (allSyncProducts.length >= payload.paging.total) break;
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allSyncProducts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!res.ok) {
|
static async get(
|
||||||
throw new FetchError("Failed to get Printful variant", res);
|
productId: number | string,
|
||||||
|
): Promise<Printful.Products.Product | undefined> {
|
||||||
|
const res = await fetch(
|
||||||
|
`${env().API_URL}/store/products/${productId}`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: { ...env().AUTH_HEADERS },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.status === 404 || res.status === 400) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new FetchError("Failed to get Printful product", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload =
|
||||||
|
(await res.json()) as Printful.Products.MetaDataSingle<Printful.Products.Product>;
|
||||||
|
return payload.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload =
|
static async update(
|
||||||
(await res.json()) as Printful.Products.MetaDataSingle<Printful.Products.SyncVariant>;
|
printfulProductId: number,
|
||||||
return payload.result;
|
printfulProduct: DeepPartial<Printful.Products.Product>,
|
||||||
}
|
) {
|
||||||
|
const res = await fetch(
|
||||||
|
`${env().API_URL}/store/products/${printfulProductId}`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...env().AUTH_HEADERS,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(printfulProduct),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new FetchError("Failed to update Printful product", res);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static async getAll(
|
static Orders = class {
|
||||||
offset: number = 0,
|
static async create(printfulOrder: Printful.Orders.Order) {
|
||||||
): Promise<Printful.Products.SyncProduct[]> {
|
const res = await fetch(`${env().API_URL}/orders`, {
|
||||||
const allSyncProducts: Printful.Products.SyncProduct[] = [];
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...env().AUTH_HEADERS,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(printfulOrder),
|
||||||
|
});
|
||||||
|
|
||||||
while (true) {
|
if (!res.ok) {
|
||||||
try {
|
throw new FetchError("Failed to create printful order", res);
|
||||||
const res = await fetch(
|
}
|
||||||
`${env().API_URL}/store/products?offset=${offset}`,
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: { ...env().AUTH_HEADERS },
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new FetchError(
|
|
||||||
"Failed to get all Printful products",
|
|
||||||
res,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload =
|
|
||||||
(await res.json()) as Printful.Products.MetaDataMulti<Printful.Products.SyncProduct>;
|
|
||||||
|
|
||||||
allSyncProducts.push(...payload.result);
|
|
||||||
offset = allSyncProducts.length;
|
|
||||||
|
|
||||||
if (allSyncProducts.length >= payload.paging.total) break;
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return allSyncProducts;
|
static async getAll(
|
||||||
}
|
offset: number = 0,
|
||||||
|
): Promise<Printful.Orders.Order[]> {
|
||||||
|
const allPrintfulOrders: Printful.Orders.Order[] = [];
|
||||||
|
|
||||||
static async get(
|
while (true) {
|
||||||
productId: number | string,
|
try {
|
||||||
): Promise<Printful.Products.Product | undefined> {
|
const res = await fetch(
|
||||||
const res = await fetch(
|
`${env().API_URL}/orders?offset=${offset}`,
|
||||||
`${env().API_URL}/store/products/${productId}`,
|
{
|
||||||
{
|
method: "GET",
|
||||||
method: "GET",
|
headers: { ...env().AUTH_HEADERS },
|
||||||
headers: { ...env().AUTH_HEADERS },
|
},
|
||||||
},
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if (res.status === 404 || res.status === 400) {
|
if (!res.ok) {
|
||||||
return;
|
throw new FetchError(
|
||||||
}
|
"Failed to get all Printful orders",
|
||||||
|
res,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!res.ok) {
|
const payload =
|
||||||
throw new FetchError("Failed to get Printful product", res);
|
(await res.json()) as Printful.Products.MetaDataMulti<Printful.Orders.Order>;
|
||||||
}
|
|
||||||
|
|
||||||
const payload =
|
allPrintfulOrders.push(...payload.result);
|
||||||
(await res.json()) as Printful.Products.MetaDataSingle<Printful.Products.Product>;
|
offset = allPrintfulOrders.length;
|
||||||
return payload.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async update(
|
if (allPrintfulOrders.length >= payload.paging.total) break;
|
||||||
printfulProductId: number,
|
} catch (error) {
|
||||||
printfulProduct: DeepPartial<Printful.Products.Product>,
|
throw error;
|
||||||
) {
|
}
|
||||||
const res = await fetch(
|
}
|
||||||
`${env().API_URL}/store/products/${printfulProductId}`,
|
|
||||||
{
|
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
...env().AUTH_HEADERS,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(printfulProduct),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!res.ok) {
|
return allPrintfulOrders;
|
||||||
throw new FetchError("Failed to update Printful product", res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static Orders = class {
|
|
||||||
static async create(printfulOrder: Printful.Orders.Order) {
|
|
||||||
const res = await fetch(`${env().API_URL}/orders`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
...env().AUTH_HEADERS,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(printfulOrder),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new FetchError("Failed to create printful order", res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async getAll(
|
|
||||||
offset: number = 0,
|
|
||||||
): Promise<Printful.Orders.Order[]> {
|
|
||||||
const allPrintfulOrders: Printful.Orders.Order[] = [];
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
const res = await fetch(
|
|
||||||
`${env().API_URL}/orders?offset=${offset}`,
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: { ...env().AUTH_HEADERS },
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new FetchError(
|
|
||||||
"Failed to get all Printful orders",
|
|
||||||
res,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload =
|
|
||||||
(await res.json()) as Printful.Products.MetaDataMulti<Printful.Orders.Order>;
|
|
||||||
|
|
||||||
allPrintfulOrders.push(...payload.result);
|
|
||||||
offset = allPrintfulOrders.length;
|
|
||||||
|
|
||||||
if (allPrintfulOrders.length >= payload.paging.total) break;
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return allPrintfulOrders;
|
static Util = class {
|
||||||
}
|
static getVariantMainImage(
|
||||||
};
|
syncVariant: Printful.Products.SyncVariant,
|
||||||
|
): string {
|
||||||
static Util = class {
|
const previewFile = syncVariant.files.find(
|
||||||
static getVariantMainImage(
|
(f) => f.type === "preview",
|
||||||
syncVariant: Printful.Products.SyncVariant,
|
);
|
||||||
): string {
|
return previewFile?.preview_url ?? syncVariant.product.image;
|
||||||
const previewFile = syncVariant.files.find(
|
}
|
||||||
(f) => f.type === "preview",
|
};
|
||||||
);
|
|
||||||
return previewFile?.preview_url ?? syncVariant.product.image;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const env = () => {
|
export const env = () => {
|
||||||
if (typeof Bun === "undefined") {
|
if (typeof Bun === "undefined") {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Must be in a server context. Make sure to run using --bun.",
|
"Must be in a server context. Make sure to run using --bun.",
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const vars = {
|
|
||||||
AUTH_TOKEN: Bun.env.PRINTFUL_AUTH,
|
|
||||||
STORE_ID: Bun.env.PRINTFUL_STORE_ID,
|
|
||||||
} as Record<string, string>;
|
|
||||||
|
|
||||||
for (const varKey in vars) {
|
|
||||||
if (!vars[varKey]) {
|
|
||||||
console.log(`Missing ${varKey} environment variable`);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
const vars = {
|
||||||
...vars,
|
AUTH_TOKEN: Bun.env.PRINTFUL_AUTH,
|
||||||
API_URL: "https://api.printful.com",
|
STORE_ID: Bun.env.PRINTFUL_STORE_ID,
|
||||||
AUTH_HEADERS: {
|
} as Record<string, string>;
|
||||||
Authorization: `Bearer ${vars.AUTH_TOKEN}`,
|
|
||||||
"X-PF-Store-Id": `${vars.STORE_ID}`,
|
for (const varKey in vars) {
|
||||||
},
|
if (!vars[varKey]) {
|
||||||
};
|
console.log(`Missing ${varKey} environment variable`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...vars,
|
||||||
|
API_URL: "https://api.printful.com",
|
||||||
|
AUTH_HEADERS: {
|
||||||
|
Authorization: `Bearer ${vars.AUTH_TOKEN}`,
|
||||||
|
"X-PF-Store-Id": `${vars.STORE_ID}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+331
-331
@@ -9,351 +9,351 @@ export const R2_WORKER_URL = "https://r2-worker.xominus.workers.dev";
|
|||||||
export const WEBSITE_MEDIA_URL = "https://website-media.bladeandbrawn.com";
|
export const WEBSITE_MEDIA_URL = "https://website-media.bladeandbrawn.com";
|
||||||
|
|
||||||
type MainProduct = {
|
type MainProduct = {
|
||||||
name: string;
|
name: string;
|
||||||
externalId: string;
|
externalId: string;
|
||||||
colors: Set<string>;
|
colors: Set<string>;
|
||||||
variants: {
|
variants: {
|
||||||
color: string;
|
color: string;
|
||||||
product: Printful.Products.Product;
|
product: Printful.Products.Product;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SyncService {
|
export class SyncService {
|
||||||
static state = {
|
static state = {
|
||||||
isSyncing: false,
|
isSyncing: false,
|
||||||
syncingIds: [] as number[],
|
syncingIds: [] as number[],
|
||||||
};
|
};
|
||||||
|
|
||||||
static async sync(printfulProductId: number | undefined) {
|
static async sync(printfulProductId: number | undefined) {
|
||||||
this.state.isSyncing = true;
|
|
||||||
|
|
||||||
const mainPrintfulProducts: MainProduct[] = [];
|
|
||||||
const webflowProducts: Webflow.Products.ProductAndSkus[] = [];
|
|
||||||
try {
|
|
||||||
let printfulProducts = await PrintfulService.Products.getAll();
|
|
||||||
if (printfulProductId) {
|
|
||||||
const printfulProduct = printfulProducts.find(
|
|
||||||
(p) => p.id === printfulProductId,
|
|
||||||
);
|
|
||||||
if (printfulProduct) {
|
|
||||||
const mainProductName = this.getMainProductName(
|
|
||||||
printfulProduct.name,
|
|
||||||
);
|
|
||||||
printfulProducts = printfulProducts.filter((p) =>
|
|
||||||
p.name.includes(mainProductName),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
webflowProducts.push(...(await WebflowService.Products.getAll()));
|
|
||||||
|
|
||||||
// Generate main printful products
|
|
||||||
console.log("Generating main products...");
|
|
||||||
for (const printfulProduct of printfulProducts) {
|
|
||||||
const mainProductName = this.getMainProductName(
|
|
||||||
printfulProduct.name,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mainPrintfulProduct = mainPrintfulProducts.find((mp) =>
|
|
||||||
mp.name.includes(mainProductName),
|
|
||||||
);
|
|
||||||
if (!mainPrintfulProduct) {
|
|
||||||
mainPrintfulProduct = {
|
|
||||||
name: mainProductName,
|
|
||||||
variants: [],
|
|
||||||
externalId: printfulProduct.external_id,
|
|
||||||
colors: new Set(),
|
|
||||||
};
|
|
||||||
mainPrintfulProducts.push(mainPrintfulProduct);
|
|
||||||
}
|
|
||||||
|
|
||||||
const productColor = this.findColorInProductName(
|
|
||||||
printfulProduct.name,
|
|
||||||
);
|
|
||||||
mainPrintfulProduct.variants.push({
|
|
||||||
color: productColor,
|
|
||||||
product: (await PrintfulService.Products.get(
|
|
||||||
printfulProduct.id,
|
|
||||||
))!,
|
|
||||||
});
|
|
||||||
mainPrintfulProduct.colors.add(productColor);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
this.state.isSyncing = false;
|
|
||||||
this.state.syncingIds = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Validate main printful products
|
|
||||||
|
|
||||||
// Sync the main printful products
|
|
||||||
console.log("Syncing main products...");
|
|
||||||
for (const mainPrintfulProduct of mainPrintfulProducts) {
|
|
||||||
try {
|
|
||||||
this.state.isSyncing = true;
|
this.state.isSyncing = true;
|
||||||
this.state.syncingIds = mainPrintfulProduct.variants.map(
|
|
||||||
(v) => v.product.sync_product.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
const foundColors = mainPrintfulProduct.colors;
|
const mainPrintfulProducts: MainProduct[] = [];
|
||||||
const foundSizes: Set<string> = new Set();
|
const webflowProducts: Webflow.Products.ProductAndSkus[] = [];
|
||||||
|
try {
|
||||||
const webflowSkus: DeepPartial<Webflow.Products.Skus.Sku>[] =
|
let printfulProducts = await PrintfulService.Products.getAll();
|
||||||
[];
|
if (printfulProductId) {
|
||||||
|
const printfulProduct = printfulProducts.find(
|
||||||
for (const specialVariant of mainPrintfulProduct.variants) {
|
(p) => p.id === printfulProductId,
|
||||||
for (const printfulVariant of specialVariant.product
|
|
||||||
.sync_variants) {
|
|
||||||
// check if this variant size is in all other special variants
|
|
||||||
let sizeIsInAllVariants = true;
|
|
||||||
for (const specialVariant of mainPrintfulProduct.variants) {
|
|
||||||
const sizes =
|
|
||||||
specialVariant.product.sync_variants.map(
|
|
||||||
(v) => v.size,
|
|
||||||
);
|
);
|
||||||
if (!sizes.includes(printfulVariant.size))
|
if (printfulProduct) {
|
||||||
sizeIsInAllVariants = false;
|
const mainProductName = this.getMainProductName(
|
||||||
|
printfulProduct.name,
|
||||||
|
);
|
||||||
|
printfulProducts = printfulProducts.filter((p) =>
|
||||||
|
p.name.includes(mainProductName),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
webflowProducts.push(...(await WebflowService.Products.getAll()));
|
||||||
|
|
||||||
if (sizeIsInAllVariants) {
|
// Generate main printful products
|
||||||
foundSizes.add(printfulVariant.size);
|
console.log("Generating main products...");
|
||||||
|
for (const printfulProduct of printfulProducts) {
|
||||||
webflowSkus.push({
|
const mainProductName = this.getMainProductName(
|
||||||
id: printfulVariant.external_id,
|
printfulProduct.name,
|
||||||
fieldData: {
|
|
||||||
name: printfulVariant.name,
|
|
||||||
slug: formatSlug(printfulVariant.name),
|
|
||||||
"sku-values": {
|
|
||||||
color: specialVariant.color,
|
|
||||||
size: printfulVariant.size,
|
|
||||||
},
|
|
||||||
price: {
|
|
||||||
value: Math.floor(
|
|
||||||
+printfulVariant.retail_price * 100,
|
|
||||||
),
|
|
||||||
unit: printfulVariant.currency,
|
|
||||||
currency: printfulVariant.currency,
|
|
||||||
},
|
|
||||||
"main-image":
|
|
||||||
PrintfulService.Util.getVariantMainImage(
|
|
||||||
printfulVariant,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SYNC PRODUCT DATA
|
|
||||||
let existingWebflowProduct = webflowProducts.find(
|
|
||||||
(webflowProduct) =>
|
|
||||||
webflowProduct.product.id ===
|
|
||||||
mainPrintfulProduct.externalId.split("-")[0],
|
|
||||||
);
|
|
||||||
if (existingWebflowProduct) {
|
|
||||||
// SYNC PRODUCT UPDATE
|
|
||||||
|
|
||||||
// do not update images
|
|
||||||
for (const webflowSku of webflowSkus) {
|
|
||||||
delete webflowSku.fieldData?.["main-image"];
|
|
||||||
}
|
|
||||||
|
|
||||||
const webflowProductId =
|
|
||||||
mainPrintfulProduct.externalId.split("-")[0];
|
|
||||||
if (!webflowProductId) {
|
|
||||||
throw new Error("Malformed printful product ID");
|
|
||||||
}
|
|
||||||
WebflowService.Products.update(webflowProductId, {
|
|
||||||
product: {
|
|
||||||
fieldData: {
|
|
||||||
name: mainPrintfulProduct.name,
|
|
||||||
slug: formatSlug(mainPrintfulProduct.name),
|
|
||||||
shippable: true,
|
|
||||||
"tax-category": "standard-taxable",
|
|
||||||
"sku-properties": [
|
|
||||||
{
|
|
||||||
id: "color",
|
|
||||||
name: "Color",
|
|
||||||
enum: Array.from(foundColors).map(
|
|
||||||
(color) => ({
|
|
||||||
id: color,
|
|
||||||
slug: formatSlug(color),
|
|
||||||
name: color,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "size",
|
|
||||||
name: "Size",
|
|
||||||
enum: Array.from(foundSizes).map(
|
|
||||||
(size) => ({
|
|
||||||
id: size,
|
|
||||||
slug: formatSlug(size),
|
|
||||||
name: size,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sku: webflowSkus[0],
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const webflowSku of webflowSkus.slice(1)) {
|
|
||||||
const existingWebflowSku =
|
|
||||||
existingWebflowProduct.skus.find(
|
|
||||||
(sku) => sku.id === webflowSku.id,
|
|
||||||
);
|
|
||||||
if (webflowSku.id && existingWebflowSku) {
|
|
||||||
await WebflowService.Products.Skus.update(
|
|
||||||
webflowProductId,
|
|
||||||
webflowSku.id,
|
|
||||||
webflowSku,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await WebflowService.Products.Skus.create(
|
|
||||||
webflowProductId,
|
|
||||||
[webflowSku],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// SYNC PRODUCT CREATE
|
|
||||||
const webflowProductId =
|
|
||||||
await WebflowService.Products.create({
|
|
||||||
product: {
|
|
||||||
fieldData: {
|
|
||||||
name: mainPrintfulProduct.name,
|
|
||||||
slug: formatSlug(mainPrintfulProduct.name),
|
|
||||||
shippable: true,
|
|
||||||
"tax-category": "standard-taxable",
|
|
||||||
"sku-properties": [
|
|
||||||
{
|
|
||||||
id: "color",
|
|
||||||
name: "Color",
|
|
||||||
enum: Array.from(foundColors).map(
|
|
||||||
(color) => ({
|
|
||||||
id: color,
|
|
||||||
slug: formatSlug(color),
|
|
||||||
name: color,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "size",
|
|
||||||
name: "Size",
|
|
||||||
enum: Array.from(foundSizes).map(
|
|
||||||
(size) => ({
|
|
||||||
id: size,
|
|
||||||
slug: formatSlug(size),
|
|
||||||
name: size,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sku: webflowSkus[0],
|
|
||||||
});
|
|
||||||
|
|
||||||
// create webflow product SKUs
|
|
||||||
await WebflowService.Products.Skus.create(
|
|
||||||
webflowProductId,
|
|
||||||
webflowSkus.slice(1),
|
|
||||||
);
|
|
||||||
|
|
||||||
existingWebflowProduct =
|
|
||||||
await WebflowService.Products.get(webflowProductId);
|
|
||||||
if (!existingWebflowProduct) {
|
|
||||||
console.error("Missing webflow product");
|
|
||||||
throw new Error("Failed to create Webflow product");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const specialVariant of mainPrintfulProduct.variants) {
|
|
||||||
const newPrintfulVariants: DeepPartial<Printful.Products.SyncVariant>[] =
|
|
||||||
[];
|
|
||||||
for (const printfulVariant of specialVariant.product
|
|
||||||
.sync_variants) {
|
|
||||||
const associatedWebflowSku =
|
|
||||||
existingWebflowProduct.skus.find(
|
|
||||||
(sku) =>
|
|
||||||
sku.fieldData["sku-values"]?.[
|
|
||||||
"color"
|
|
||||||
] === printfulVariant.color &&
|
|
||||||
sku.fieldData["sku-values"]?.[
|
|
||||||
"size"
|
|
||||||
] === printfulVariant.size,
|
|
||||||
);
|
);
|
||||||
if (associatedWebflowSku) {
|
|
||||||
newPrintfulVariants.push({
|
let mainPrintfulProduct = mainPrintfulProducts.find((mp) =>
|
||||||
id: printfulVariant.id, // printful variant id
|
mp.name.includes(mainProductName),
|
||||||
external_id: String(
|
);
|
||||||
associatedWebflowSku.id,
|
if (!mainPrintfulProduct) {
|
||||||
), // webflow variant id
|
mainPrintfulProduct = {
|
||||||
|
name: mainProductName,
|
||||||
|
variants: [],
|
||||||
|
externalId: printfulProduct.external_id,
|
||||||
|
colors: new Set(),
|
||||||
|
};
|
||||||
|
mainPrintfulProducts.push(mainPrintfulProduct);
|
||||||
|
}
|
||||||
|
|
||||||
|
const productColor = this.findColorInProductName(
|
||||||
|
printfulProduct.name,
|
||||||
|
);
|
||||||
|
mainPrintfulProduct.variants.push({
|
||||||
|
color: productColor,
|
||||||
|
product: (await PrintfulService.Products.get(
|
||||||
|
printfulProduct.id,
|
||||||
|
))!,
|
||||||
});
|
});
|
||||||
}
|
mainPrintfulProduct.colors.add(productColor);
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.state.isSyncing = false;
|
||||||
|
this.state.syncingIds = [];
|
||||||
|
}
|
||||||
|
|
||||||
await sleep(10000);
|
// TODO: Validate main printful products
|
||||||
await PrintfulService.Products.update(
|
|
||||||
specialVariant.product.sync_product.id,
|
// Sync the main printful products
|
||||||
{
|
console.log("Syncing main products...");
|
||||||
sync_product: {
|
for (const mainPrintfulProduct of mainPrintfulProducts) {
|
||||||
id: specialVariant.product.sync_product.id,
|
try {
|
||||||
external_id: `${webflowProductId}-${specialVariant.color}`,
|
this.state.isSyncing = true;
|
||||||
},
|
this.state.syncingIds = mainPrintfulProduct.variants.map(
|
||||||
sync_variants: newPrintfulVariants,
|
(v) => v.product.sync_product.id,
|
||||||
},
|
);
|
||||||
|
|
||||||
|
const foundColors = mainPrintfulProduct.colors;
|
||||||
|
const foundSizes: Set<string> = new Set();
|
||||||
|
|
||||||
|
const webflowSkus: DeepPartial<Webflow.Products.Skus.Sku>[] =
|
||||||
|
[];
|
||||||
|
|
||||||
|
for (const specialVariant of mainPrintfulProduct.variants) {
|
||||||
|
for (const printfulVariant of specialVariant.product
|
||||||
|
.sync_variants) {
|
||||||
|
// check if this variant size is in all other special variants
|
||||||
|
let sizeIsInAllVariants = true;
|
||||||
|
for (const specialVariant of mainPrintfulProduct.variants) {
|
||||||
|
const sizes =
|
||||||
|
specialVariant.product.sync_variants.map(
|
||||||
|
(v) => v.size,
|
||||||
|
);
|
||||||
|
if (!sizes.includes(printfulVariant.size))
|
||||||
|
sizeIsInAllVariants = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sizeIsInAllVariants) {
|
||||||
|
foundSizes.add(printfulVariant.size);
|
||||||
|
|
||||||
|
webflowSkus.push({
|
||||||
|
id: printfulVariant.external_id,
|
||||||
|
fieldData: {
|
||||||
|
name: printfulVariant.name,
|
||||||
|
slug: formatSlug(printfulVariant.name),
|
||||||
|
"sku-values": {
|
||||||
|
color: specialVariant.color,
|
||||||
|
size: printfulVariant.size,
|
||||||
|
},
|
||||||
|
price: {
|
||||||
|
value: Math.floor(
|
||||||
|
+printfulVariant.retail_price * 100,
|
||||||
|
),
|
||||||
|
unit: printfulVariant.currency,
|
||||||
|
currency: printfulVariant.currency,
|
||||||
|
},
|
||||||
|
"main-image":
|
||||||
|
PrintfulService.Util.getVariantMainImage(
|
||||||
|
printfulVariant,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SYNC PRODUCT DATA
|
||||||
|
let existingWebflowProduct = webflowProducts.find(
|
||||||
|
(webflowProduct) =>
|
||||||
|
webflowProduct.product.id ===
|
||||||
|
mainPrintfulProduct.externalId.split("-")[0],
|
||||||
|
);
|
||||||
|
if (existingWebflowProduct) {
|
||||||
|
// SYNC PRODUCT UPDATE
|
||||||
|
|
||||||
|
// do not update images
|
||||||
|
for (const webflowSku of webflowSkus) {
|
||||||
|
delete webflowSku.fieldData?.["main-image"];
|
||||||
|
}
|
||||||
|
|
||||||
|
const webflowProductId =
|
||||||
|
mainPrintfulProduct.externalId.split("-")[0];
|
||||||
|
if (!webflowProductId) {
|
||||||
|
throw new Error("Malformed printful product ID");
|
||||||
|
}
|
||||||
|
await WebflowService.Products.update(webflowProductId, {
|
||||||
|
product: {
|
||||||
|
fieldData: {
|
||||||
|
name: mainPrintfulProduct.name,
|
||||||
|
slug: formatSlug(mainPrintfulProduct.name),
|
||||||
|
shippable: true,
|
||||||
|
"tax-category": "standard-taxable",
|
||||||
|
"sku-properties": [
|
||||||
|
{
|
||||||
|
id: "color",
|
||||||
|
name: "Color",
|
||||||
|
enum: Array.from(foundColors).map(
|
||||||
|
(color) => ({
|
||||||
|
id: color,
|
||||||
|
slug: formatSlug(color),
|
||||||
|
name: color,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "size",
|
||||||
|
name: "Size",
|
||||||
|
enum: Array.from(foundSizes).map(
|
||||||
|
(size) => ({
|
||||||
|
id: size,
|
||||||
|
slug: formatSlug(size),
|
||||||
|
name: size,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sku: webflowSkus[0],
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const webflowSku of webflowSkus.slice(1)) {
|
||||||
|
const existingWebflowSku =
|
||||||
|
existingWebflowProduct.skus.find(
|
||||||
|
(sku) => sku.id === webflowSku.id,
|
||||||
|
);
|
||||||
|
if (webflowSku.id && existingWebflowSku) {
|
||||||
|
await WebflowService.Products.Skus.update(
|
||||||
|
webflowProductId,
|
||||||
|
webflowSku.id,
|
||||||
|
webflowSku,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await WebflowService.Products.Skus.create(
|
||||||
|
webflowProductId,
|
||||||
|
[webflowSku],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// SYNC PRODUCT CREATE
|
||||||
|
const webflowProductId =
|
||||||
|
await WebflowService.Products.create({
|
||||||
|
product: {
|
||||||
|
fieldData: {
|
||||||
|
name: mainPrintfulProduct.name,
|
||||||
|
slug: formatSlug(mainPrintfulProduct.name),
|
||||||
|
shippable: true,
|
||||||
|
"tax-category": "standard-taxable",
|
||||||
|
"sku-properties": [
|
||||||
|
{
|
||||||
|
id: "color",
|
||||||
|
name: "Color",
|
||||||
|
enum: Array.from(foundColors).map(
|
||||||
|
(color) => ({
|
||||||
|
id: color,
|
||||||
|
slug: formatSlug(color),
|
||||||
|
name: color,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "size",
|
||||||
|
name: "Size",
|
||||||
|
enum: Array.from(foundSizes).map(
|
||||||
|
(size) => ({
|
||||||
|
id: size,
|
||||||
|
slug: formatSlug(size),
|
||||||
|
name: size,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sku: webflowSkus[0],
|
||||||
|
});
|
||||||
|
|
||||||
|
// create webflow product SKUs
|
||||||
|
await WebflowService.Products.Skus.create(
|
||||||
|
webflowProductId,
|
||||||
|
webflowSkus.slice(1),
|
||||||
|
);
|
||||||
|
|
||||||
|
existingWebflowProduct =
|
||||||
|
await WebflowService.Products.get(webflowProductId);
|
||||||
|
if (!existingWebflowProduct) {
|
||||||
|
console.error("Missing webflow product");
|
||||||
|
throw new Error("Failed to create Webflow product");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const specialVariant of mainPrintfulProduct.variants) {
|
||||||
|
const newPrintfulVariants: DeepPartial<Printful.Products.SyncVariant>[] =
|
||||||
|
[];
|
||||||
|
for (const printfulVariant of specialVariant.product
|
||||||
|
.sync_variants) {
|
||||||
|
const associatedWebflowSku =
|
||||||
|
existingWebflowProduct.skus.find(
|
||||||
|
(sku) =>
|
||||||
|
sku.fieldData["sku-values"]?.[
|
||||||
|
"color"
|
||||||
|
] === printfulVariant.color &&
|
||||||
|
sku.fieldData["sku-values"]?.[
|
||||||
|
"size"
|
||||||
|
] === printfulVariant.size,
|
||||||
|
);
|
||||||
|
if (associatedWebflowSku) {
|
||||||
|
newPrintfulVariants.push({
|
||||||
|
id: printfulVariant.id, // printful variant id
|
||||||
|
external_id: String(
|
||||||
|
associatedWebflowSku.id,
|
||||||
|
), // webflow variant id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(10000);
|
||||||
|
await PrintfulService.Products.update(
|
||||||
|
specialVariant.product.sync_product.id,
|
||||||
|
{
|
||||||
|
sync_product: {
|
||||||
|
id: specialVariant.product.sync_product.id,
|
||||||
|
external_id: `${webflowProductId}-${specialVariant.color}`,
|
||||||
|
},
|
||||||
|
sync_variants: newPrintfulVariants,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// SYNC IMAGES
|
||||||
|
// for (const sku of existingWebflowProduct.skus) {
|
||||||
|
// const skuColor = sku.fieldData["sku-values"]?.["color"]?.toLowerCase() ?? "None";
|
||||||
|
// const skuSlug = `${existingWebflowProduct.product.fieldData.slug}-${skuColor}`;
|
||||||
|
// const skuImageUrls = await this.getProductImageUrls(skuSlug);
|
||||||
|
//
|
||||||
|
// sku.fieldData["main-image"] = skuImageUrls[0] ?? sku.fieldData["main-image"];
|
||||||
|
// sku.fieldData["more-images"] = skuImageUrls.slice(1).map(url => ({ url }));
|
||||||
|
//
|
||||||
|
// await WebflowService.Products.Skus.update(existingWebflowProduct.product.id, sku.id, sku);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
if (err instanceof FetchError) {
|
||||||
|
console.error(err.message, err.payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.state.isSyncing = false;
|
||||||
|
this.state.syncingIds = [];
|
||||||
|
console.log("Done");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getProductImageUrls = async (slug: string) => {
|
||||||
|
const res = await fetch(`${R2_WORKER_URL}/product-images/${slug}`);
|
||||||
|
if (!res.ok) {
|
||||||
|
console.error(
|
||||||
|
"Failed to get product image keys:",
|
||||||
|
await res.json(),
|
||||||
);
|
);
|
||||||
}
|
throw new Error("Failed to get product image keys");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
const productImageKeys: string[] = (await res.json()) as string[];
|
||||||
// SYNC IMAGES
|
return productImageKeys.map((key) => `${WEBSITE_MEDIA_URL}/${key}`);
|
||||||
// for (const sku of existingWebflowProduct.skus) {
|
};
|
||||||
// const skuColor = sku.fieldData["sku-values"]?.["color"]?.toLowerCase() ?? "None";
|
|
||||||
// const skuSlug = `${existingWebflowProduct.product.fieldData.slug}-${skuColor}`;
|
public static findColorInProductName = (productName: string): string => {
|
||||||
// const skuImageUrls = await this.getProductImageUrls(skuSlug);
|
const m = productName.match(/\[([^\]]+)\]/);
|
||||||
//
|
return m?.[1] ? m[1] : "N/A";
|
||||||
// sku.fieldData["main-image"] = skuImageUrls[0] ?? sku.fieldData["main-image"];
|
};
|
||||||
// sku.fieldData["more-images"] = skuImageUrls.slice(1).map(url => ({ url }));
|
|
||||||
//
|
public static getMainProductName = (productName: string) => {
|
||||||
// await WebflowService.Products.Skus.update(existingWebflowProduct.product.id, sku.id, sku);
|
const colorInName = this.findColorInProductName(productName);
|
||||||
// }
|
if (colorInName) {
|
||||||
//
|
return productName.replace(`[${colorInName}]`, "").trimEnd();
|
||||||
if (err instanceof FetchError) {
|
} else {
|
||||||
console.error(err.message, err.payload);
|
return productName;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
this.state.isSyncing = false;
|
|
||||||
this.state.syncingIds = [];
|
|
||||||
console.log("Done");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static getProductImageUrls = async (slug: string) => {
|
|
||||||
const res = await fetch(`${R2_WORKER_URL}/product-images/${slug}`);
|
|
||||||
if (!res.ok) {
|
|
||||||
console.error(
|
|
||||||
"Failed to get product image keys:",
|
|
||||||
await res.json(),
|
|
||||||
);
|
|
||||||
throw new Error("Failed to get product image keys");
|
|
||||||
}
|
|
||||||
const productImageKeys: string[] = (await res.json()) as string[];
|
|
||||||
return productImageKeys.map((key) => `${WEBSITE_MEDIA_URL}/${key}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
public static findColorInProductName = (productName: string): string => {
|
|
||||||
const m = productName.match(/\[([^\]]+)\]/);
|
|
||||||
return m?.[1] ? m[1] : "N/A";
|
|
||||||
};
|
|
||||||
|
|
||||||
public static getMainProductName = (productName: string) => {
|
|
||||||
const colorInName = this.findColorInProductName(productName);
|
|
||||||
if (colorInName) {
|
|
||||||
return productName.replace(`[${colorInName}]`, "").trimEnd();
|
|
||||||
} else {
|
|
||||||
return productName;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
+301
-301
@@ -3,328 +3,328 @@ import { FetchError, type DeepPartial } from "./util/misc";
|
|||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
|
|
||||||
export class WebflowService {
|
export class WebflowService {
|
||||||
static Products = class {
|
static Products = class {
|
||||||
static Skus = class {
|
static Skus = class {
|
||||||
static async create(
|
static async create(
|
||||||
webflowProductId: string,
|
webflowProductId: string,
|
||||||
skus: DeepPartial<Webflow.Products.Skus.Sku>[],
|
skus: DeepPartial<Webflow.Products.Skus.Sku>[],
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${env().API_SITES_URL}/products/${webflowProductId}/skus`,
|
`${env().API_SITES_URL}/products/${webflowProductId}/skus`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
...env().AUTH_HEADER,
|
...env().AUTH_HEADER,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
skus: skus,
|
skus: skus,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new FetchError(
|
throw new FetchError(
|
||||||
"Failed to create Webflow product SKU",
|
"Failed to create Webflow product SKU",
|
||||||
res,
|
res,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = (await res.json()) as {
|
const payload = (await res.json()) as {
|
||||||
skus: { id: string }[];
|
skus: { id: string }[];
|
||||||
|
};
|
||||||
|
return payload.skus.map((sku) => sku.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(
|
||||||
|
webflowProductId: string,
|
||||||
|
webflowSkuId: string,
|
||||||
|
webflowSku: DeepPartial<Webflow.Products.Skus.Sku>,
|
||||||
|
): Promise<void> {
|
||||||
|
const res = await fetch(
|
||||||
|
`${env().API_SITES_URL}/products/${webflowProductId}/skus/${webflowSkuId}`,
|
||||||
|
{
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...env().AUTH_HEADER,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sku: webflowSku,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new FetchError(
|
||||||
|
"Failed to update Webflow product SKU",
|
||||||
|
res,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return payload.skus.map((sku) => sku.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async update(
|
static async create(
|
||||||
webflowProductId: string,
|
webflowProductAndSku: DeepPartial<Webflow.Products.ProductAndSku>,
|
||||||
webflowSkuId: string,
|
): Promise<string> {
|
||||||
webflowSku: DeepPartial<Webflow.Products.Skus.Sku>,
|
const res = await fetch(`${env().API_SITES_URL}/products`, {
|
||||||
): Promise<void> {
|
method: "POST",
|
||||||
const res = await fetch(
|
headers: {
|
||||||
`${env().API_SITES_URL}/products/${webflowProductId}/skus/${webflowSkuId}`,
|
"Content-Type": "application/json",
|
||||||
{
|
...env().AUTH_HEADER,
|
||||||
method: "PATCH",
|
},
|
||||||
headers: {
|
body: JSON.stringify(webflowProductAndSku),
|
||||||
"Content-Type": "application/json",
|
});
|
||||||
...env().AUTH_HEADER,
|
|
||||||
},
|
if (!res.ok) {
|
||||||
body: JSON.stringify({
|
throw new FetchError("Failed to create Webflow product", res);
|
||||||
sku: webflowSku,
|
}
|
||||||
}),
|
|
||||||
},
|
const createdWebflowProduct = (await res.json()) as {
|
||||||
);
|
product: { id: string };
|
||||||
if (!res.ok) {
|
};
|
||||||
throw new FetchError(
|
return createdWebflowProduct.product.id;
|
||||||
"Failed to update Webflow product SKU",
|
}
|
||||||
res,
|
|
||||||
);
|
static async getAll(): Promise<Webflow.Products.ProductAndSkus[]> {
|
||||||
|
const res = await fetch(`${env().API_SITES_URL}/products`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
...env().AUTH_HEADER,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new FetchError("Failed to get all Webflow products", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = (await res.json()) as {
|
||||||
|
items: Webflow.Products.ProductAndSkus[];
|
||||||
|
};
|
||||||
|
return payload.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async get(
|
||||||
|
webflowProductId: string,
|
||||||
|
): Promise<Webflow.Products.ProductAndSkus | undefined> {
|
||||||
|
const res = await fetch(
|
||||||
|
`${env().API_SITES_URL}/products/${webflowProductId}`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
...env().AUTH_HEADER,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.status === 404 || res.status === 400) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new FetchError("Failed to get Webflow product", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload =
|
||||||
|
(await res.json()) as Webflow.Products.ProductAndSkus;
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(
|
||||||
|
webflowProductId: string,
|
||||||
|
webflowProduct: DeepPartial<Webflow.Products.ProductAndSku>,
|
||||||
|
): Promise<void> {
|
||||||
|
const res = await fetch(
|
||||||
|
`${env().API_SITES_URL}/products/${webflowProductId}`,
|
||||||
|
{
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...env().AUTH_HEADER,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(webflowProduct),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new FetchError("Failed to update Webflow product", res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async remove(webflowProductId: string): Promise<void> {
|
||||||
|
const res = await fetch(
|
||||||
|
`${env().API_COLLECTIONS_URL}/items/${webflowProductId}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...env().AUTH_HEADER,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new FetchError("Failed to remove Webflow product", res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static async create(
|
static Orders = class {
|
||||||
webflowProductAndSku: DeepPartial<Webflow.Products.ProductAndSku>,
|
static async getAll(
|
||||||
): Promise<string> {
|
opt: { status?: Webflow.Orders.Order["status"] } = {},
|
||||||
const res = await fetch(`${env().API_SITES_URL}/products`, {
|
): Promise<Webflow.Orders.Order[]> {
|
||||||
method: "POST",
|
// TODO: pagination
|
||||||
headers: {
|
const params = new URLSearchParams();
|
||||||
"Content-Type": "application/json",
|
if (opt.status !== undefined) {
|
||||||
...env().AUTH_HEADER,
|
params.set("status", opt.status);
|
||||||
},
|
}
|
||||||
body: JSON.stringify(webflowProductAndSku),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res.ok) {
|
const res = await fetch(
|
||||||
throw new FetchError("Failed to create Webflow product", res);
|
`${env().API_SITES_URL}/orders?${params.toString()}`,
|
||||||
}
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: { ...env().AUTH_HEADER },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const createdWebflowProduct = (await res.json()) as {
|
if (!res.ok) {
|
||||||
product: { id: string };
|
throw new FetchError("Failed to get all Webflow orders", res);
|
||||||
};
|
}
|
||||||
return createdWebflowProduct.product.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async getAll(): Promise<Webflow.Products.ProductAndSkus[]> {
|
const payload = (await res.json()) as {
|
||||||
const res = await fetch(`${env().API_SITES_URL}/products`, {
|
orders: Webflow.Orders.Order[];
|
||||||
method: "GET",
|
};
|
||||||
headers: {
|
return payload.orders;
|
||||||
...env().AUTH_HEADER,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new FetchError("Failed to get all Webflow products", res);
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload = (await res.json()) as {
|
|
||||||
items: Webflow.Products.ProductAndSkus[];
|
|
||||||
};
|
|
||||||
return payload.items;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async get(
|
|
||||||
webflowProductId: string,
|
|
||||||
): Promise<Webflow.Products.ProductAndSkus | undefined> {
|
|
||||||
const res = await fetch(
|
|
||||||
`${env().API_SITES_URL}/products/${webflowProductId}`,
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
...env().AUTH_HEADER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (res.status === 404 || res.status === 400) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new FetchError("Failed to get Webflow product", res);
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload =
|
|
||||||
(await res.json()) as Webflow.Products.ProductAndSkus;
|
|
||||||
return payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async update(
|
|
||||||
webflowProductId: string,
|
|
||||||
webflowProduct: DeepPartial<Webflow.Products.ProductAndSku>,
|
|
||||||
): Promise<void> {
|
|
||||||
const res = await fetch(
|
|
||||||
`${env().API_SITES_URL}/products/${webflowProductId}`,
|
|
||||||
{
|
|
||||||
method: "PATCH",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
...env().AUTH_HEADER,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(webflowProduct),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new FetchError("Failed to update Webflow product", res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async remove(webflowProductId: string): Promise<void> {
|
|
||||||
const res = await fetch(
|
|
||||||
`${env().API_COLLECTIONS_URL}/items/${webflowProductId}`,
|
|
||||||
{
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
...env().AUTH_HEADER,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new FetchError("Failed to remove Webflow product", res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static Orders = class {
|
|
||||||
static async getAll(
|
|
||||||
opt: { status?: Webflow.Orders.Order["status"] } = {},
|
|
||||||
): Promise<Webflow.Orders.Order[]> {
|
|
||||||
// TODO: pagination
|
|
||||||
const params = new URLSearchParams();
|
|
||||||
if (opt.status !== undefined) {
|
|
||||||
params.set("status", opt.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await fetch(
|
|
||||||
`${env().API_SITES_URL}/orders?${params.toString()}`,
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: { ...env().AUTH_HEADER },
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new FetchError("Failed to get all Webflow orders", res);
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload = (await res.json()) as {
|
|
||||||
orders: Webflow.Orders.Order[];
|
|
||||||
};
|
|
||||||
return payload.orders;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async update(
|
|
||||||
webflowOrderId: string,
|
|
||||||
webflowOrderUpdate: {
|
|
||||||
comment?: string;
|
|
||||||
shippingProvider?: string;
|
|
||||||
shippingTracking?: string;
|
|
||||||
shippingTrackingURL: string;
|
|
||||||
},
|
|
||||||
): Promise<void> {
|
|
||||||
const res = await fetch(
|
|
||||||
`${env().API_SITES_URL}/orders/${webflowOrderId}`,
|
|
||||||
{
|
|
||||||
method: "PATCH",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
...env().AUTH_HEADER,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(webflowOrderUpdate),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new FetchError("Failed to update Webflow order", res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async fulfill(
|
|
||||||
webflowOrderId: string,
|
|
||||||
opt: { sendOrderFulfilledEmail?: boolean } = {},
|
|
||||||
): Promise<void> {
|
|
||||||
const res = await fetch(
|
|
||||||
`${env().API_SITES_URL}/orders/${webflowOrderId}/fulfill`,
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
...env().AUTH_HEADER,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(opt),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new FetchError("Failed to fulfill Webflow order", res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static Util = class {
|
|
||||||
static verifyWebflowSignature(
|
|
||||||
request: Request,
|
|
||||||
body: unknown,
|
|
||||||
): Boolean {
|
|
||||||
try {
|
|
||||||
const timestamp = request.headers.get("x-webflow-timestamp");
|
|
||||||
if (!timestamp) {
|
|
||||||
throw new Error("No timestamp provided");
|
|
||||||
}
|
|
||||||
const providedSignature = request.headers.get(
|
|
||||||
"x-webflow-signature",
|
|
||||||
);
|
|
||||||
if (!providedSignature) {
|
|
||||||
throw new Error("No signature provided");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const secret = env().WEBHOOK_SECRET;
|
static async update(
|
||||||
if (!secret) {
|
webflowOrderId: string,
|
||||||
throw new Error("No secret provided");
|
webflowOrderUpdate: {
|
||||||
|
comment?: string;
|
||||||
|
shippingProvider?: string;
|
||||||
|
shippingTracking?: string;
|
||||||
|
shippingTrackingURL: string;
|
||||||
|
},
|
||||||
|
): Promise<void> {
|
||||||
|
const res = await fetch(
|
||||||
|
`${env().API_SITES_URL}/orders/${webflowOrderId}`,
|
||||||
|
{
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...env().AUTH_HEADER,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(webflowOrderUpdate),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new FetchError("Failed to update Webflow order", res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!body) {
|
static async fulfill(
|
||||||
throw new Error("Body is empty");
|
webflowOrderId: string,
|
||||||
|
opt: { sendOrderFulfilledEmail?: boolean } = {},
|
||||||
|
): Promise<void> {
|
||||||
|
const res = await fetch(
|
||||||
|
`${env().API_SITES_URL}/orders/${webflowOrderId}/fulfill`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...env().AUTH_HEADER,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(opt),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new FetchError("Failed to fulfill Webflow order", res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const requestTimestamp = parseInt(timestamp, 10);
|
static Util = class {
|
||||||
const data = `${requestTimestamp}:${JSON.stringify(body)}`;
|
static verifyWebflowSignature(
|
||||||
const hash = crypto
|
request: Request,
|
||||||
.createHmac("sha256", secret)
|
body: unknown,
|
||||||
.update(data)
|
): Boolean {
|
||||||
.digest("hex");
|
try {
|
||||||
|
const timestamp = request.headers.get("x-webflow-timestamp");
|
||||||
|
if (!timestamp) {
|
||||||
|
throw new Error("No timestamp provided");
|
||||||
|
}
|
||||||
|
const providedSignature = request.headers.get(
|
||||||
|
"x-webflow-signature",
|
||||||
|
);
|
||||||
|
if (!providedSignature) {
|
||||||
|
throw new Error("No signature provided");
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
const secret = env().WEBHOOK_SECRET;
|
||||||
!crypto.timingSafeEqual(
|
if (!secret) {
|
||||||
Buffer.from(hash, "hex"),
|
throw new Error("No secret provided");
|
||||||
Buffer.from(providedSignature, "hex"),
|
}
|
||||||
)
|
|
||||||
) {
|
if (!body) {
|
||||||
throw new Error("Invalid signature");
|
throw new Error("Body is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestTimestamp = parseInt(timestamp, 10);
|
||||||
|
const data = `${requestTimestamp}:${JSON.stringify(body)}`;
|
||||||
|
const hash = crypto
|
||||||
|
.createHmac("sha256", secret)
|
||||||
|
.update(data)
|
||||||
|
.digest("hex");
|
||||||
|
|
||||||
|
if (
|
||||||
|
!crypto.timingSafeEqual(
|
||||||
|
Buffer.from(hash, "hex"),
|
||||||
|
Buffer.from(providedSignature, "hex"),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error("Invalid signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTime = Date.now();
|
||||||
|
|
||||||
|
if (currentTime - requestTimestamp > 300000) {
|
||||||
|
throw new Error("Request is older than 5 minutes");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error verifying signature: ${err}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
const currentTime = Date.now();
|
|
||||||
|
|
||||||
if (currentTime - requestTimestamp > 300000) {
|
|
||||||
throw new Error("Request is older than 5 minutes");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`Error verifying signature: ${err}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const env = () => {
|
const env = () => {
|
||||||
if (typeof Bun === "undefined") {
|
if (typeof Bun === "undefined") {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Must be in a server context. Make sure to run using --bun.",
|
"Must be in a server context. Make sure to run using --bun.",
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const vars = {
|
|
||||||
SITE_ID: Bun.env.WEBFLOW_SITE_ID,
|
|
||||||
COLLECTIONS_ID: Bun.env.WEBFLOW_COLLECTION_ID,
|
|
||||||
AUTH_TOKEN: Bun.env.WEBFLOW_AUTH,
|
|
||||||
WEBHOOK_SECRET: Bun.env.WEBFLOW_WEBHOOK_SECRET,
|
|
||||||
} as Record<string, string>;
|
|
||||||
|
|
||||||
for (const varKey in vars) {
|
|
||||||
if (!vars[varKey]) {
|
|
||||||
console.log(`Missing ${varKey} environment variable`);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
const vars = {
|
||||||
...vars,
|
SITE_ID: Bun.env.WEBFLOW_SITE_ID,
|
||||||
API_SITES_URL: `https://api.webflow.com/v2/sites/${vars.SITE_ID}`,
|
COLLECTIONS_ID: Bun.env.WEBFLOW_COLLECTION_ID,
|
||||||
API_COLLECTIONS_URL: `https://api.webflow.com/v2/collections/${vars.COLLECTIONS_ID}`,
|
AUTH_TOKEN: Bun.env.WEBFLOW_AUTH,
|
||||||
AUTH_HEADER: { Authorization: `bearer ${vars.AUTH_TOKEN}` },
|
WEBHOOK_SECRET: Bun.env.WEBFLOW_WEBHOOK_SECRET,
|
||||||
};
|
} as Record<string, string>;
|
||||||
|
|
||||||
|
for (const varKey in vars) {
|
||||||
|
if (!vars[varKey]) {
|
||||||
|
console.log(`Missing ${varKey} environment variable`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...vars,
|
||||||
|
API_SITES_URL: `https://api.webflow.com/v2/sites/${vars.SITE_ID}`,
|
||||||
|
API_COLLECTIONS_URL: `https://api.webflow.com/v2/collections/${vars.COLLECTIONS_ID}`,
|
||||||
|
AUTH_HEADER: { Authorization: `bearer ${vars.AUTH_TOKEN}` },
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -25,6 +25,6 @@
|
|||||||
// Some stricter flags (disabled by default)
|
// Some stricter flags (disabled by default)
|
||||||
"noUnusedLocals": false,
|
"noUnusedLocals": false,
|
||||||
"noUnusedParameters": false,
|
"noUnusedParameters": false,
|
||||||
"noPropertyAccessFromIndexSignature": false,
|
"noPropertyAccessFromIndexSignature": false
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -1,5 +1,7 @@
|
|||||||
fetch("https://kv-logger.xominus.workers.dev", {
|
fetch("https://kv-logger.xominus.workers.dev", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "content-type": "application/json" },
|
headers: { "content-type": "application/json" },
|
||||||
body: JSON.stringify({ "test": 1 })
|
body: JSON.stringify({ test: 1 }),
|
||||||
}).catch((e) => { console.error(e) });
|
}).catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default {
|
|||||||
switch (request.method) {
|
switch (request.method) {
|
||||||
case 'GET':
|
case 'GET':
|
||||||
const listResponse = await env.media.list({ prefix, limit: 1000 });
|
const listResponse = await env.media.list({ prefix, limit: 1000 });
|
||||||
const keys = listResponse.objects.map(o => o.key);
|
const keys = listResponse.objects.map((o) => o.key);
|
||||||
|
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
headers.set('Content-Type', 'application/json');
|
headers.set('Content-Type', 'application/json');
|
||||||
@@ -24,6 +24,5 @@ export default {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
} satisfies ExportedHandler<Env>;
|
} satisfies ExportedHandler<Env>;
|
||||||
|
|||||||
@@ -36,9 +36,7 @@
|
|||||||
|
|
||||||
/* Skip type checking all .d.ts files. */
|
/* Skip type checking all .d.ts files. */
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"types": [
|
"types": ["./worker-configuration.d.ts"]
|
||||||
"./worker-configuration.d.ts"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"exclude": ["test"],
|
"exclude": ["test"],
|
||||||
"include": ["worker-configuration.d.ts", "src/**/*.ts"]
|
"include": ["worker-configuration.d.ts", "src/**/*.ts"]
|
||||||
|
|||||||
Vendored
+6984
-6364
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user