@streamoid/settings 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -88,6 +88,8 @@ interface SettingsWorkspaceCurrent {
88
88
  /** Optional reactive credit summary if the host tracks it. */
89
89
  availableCredits?: number | null;
90
90
  totalCredits?: number | null;
91
+ /** ISO country code for the workspace; drives plan currency selection. */
92
+ country?: string;
91
93
  }
92
94
  interface SettingsWorkspaceListItem {
93
95
  _id: string;
@@ -264,12 +266,18 @@ interface SubscriptionPlan {
264
266
  name: string;
265
267
  amount: number;
266
268
  subscriptionEndDate?: string;
269
+ /** Three-letter currency code (lowercase) when known, e.g. "inr" / "usd". */
270
+ currency?: string;
271
+ /** Stripe billing interval, e.g. "month" / "year". */
272
+ interval?: string;
267
273
  }
268
274
  interface BillingSubscription {
269
275
  createdAt: string;
270
276
  downgradeScheduled: boolean;
271
277
  stripeCustomerId: string;
272
278
  plan: SubscriptionPlan;
279
+ /** Stripe product id; used to look up the matching product in fetched plans. */
280
+ stripeProductId?: string;
273
281
  }
274
282
  interface SubscriptionResponse {
275
283
  data: BillingSubscription | null;
@@ -283,6 +291,8 @@ interface Price {
283
291
  id: string;
284
292
  unit_amount: number;
285
293
  metadata: PriceMetadata;
294
+ /** Three-letter currency code (lowercase) when present, e.g. "inr" / "usd". */
295
+ currency?: string;
286
296
  }
287
297
  interface BillingProduct {
288
298
  id: string;
@@ -573,7 +583,7 @@ type IconProps = Omit<SVGProps<SVGSVGElement>, 'color' | 'strokeWidth' | 'width'
573
583
  };
574
584
  declare const SiconKey: ({ color, strokeWidth, size, ...props }: IconProps) => react_jsx_runtime.JSX.Element;
575
585
 
576
- declare function BillingContent({ embedded }?: {
586
+ declare function BillingContent({ embedded, }?: {
577
587
  embedded?: boolean;
578
588
  }): react_jsx_runtime.JSX.Element;
579
589
 
package/dist/index.d.ts CHANGED
@@ -88,6 +88,8 @@ interface SettingsWorkspaceCurrent {
88
88
  /** Optional reactive credit summary if the host tracks it. */
89
89
  availableCredits?: number | null;
90
90
  totalCredits?: number | null;
91
+ /** ISO country code for the workspace; drives plan currency selection. */
92
+ country?: string;
91
93
  }
92
94
  interface SettingsWorkspaceListItem {
93
95
  _id: string;
@@ -264,12 +266,18 @@ interface SubscriptionPlan {
264
266
  name: string;
265
267
  amount: number;
266
268
  subscriptionEndDate?: string;
269
+ /** Three-letter currency code (lowercase) when known, e.g. "inr" / "usd". */
270
+ currency?: string;
271
+ /** Stripe billing interval, e.g. "month" / "year". */
272
+ interval?: string;
267
273
  }
268
274
  interface BillingSubscription {
269
275
  createdAt: string;
270
276
  downgradeScheduled: boolean;
271
277
  stripeCustomerId: string;
272
278
  plan: SubscriptionPlan;
279
+ /** Stripe product id; used to look up the matching product in fetched plans. */
280
+ stripeProductId?: string;
273
281
  }
274
282
  interface SubscriptionResponse {
275
283
  data: BillingSubscription | null;
@@ -283,6 +291,8 @@ interface Price {
283
291
  id: string;
284
292
  unit_amount: number;
285
293
  metadata: PriceMetadata;
294
+ /** Three-letter currency code (lowercase) when present, e.g. "inr" / "usd". */
295
+ currency?: string;
286
296
  }
287
297
  interface BillingProduct {
288
298
  id: string;
@@ -573,7 +583,7 @@ type IconProps = Omit<SVGProps<SVGSVGElement>, 'color' | 'strokeWidth' | 'width'
573
583
  };
574
584
  declare const SiconKey: ({ color, strokeWidth, size, ...props }: IconProps) => react_jsx_runtime.JSX.Element;
575
585
 
576
- declare function BillingContent({ embedded }?: {
586
+ declare function BillingContent({ embedded, }?: {
577
587
  embedded?: boolean;
578
588
  }): react_jsx_runtime.JSX.Element;
579
589
 
package/dist/index.js CHANGED
@@ -4195,7 +4195,10 @@ function formatDate(iso) {
4195
4195
  return iso;
4196
4196
  }
4197
4197
  }
4198
- function formatCurrency(cents) {
4198
+ function formatCurrency(cents, currency) {
4199
+ if (currency === "inr") {
4200
+ return `\u20B9${(cents / 100).toFixed(2)}`;
4201
+ }
4199
4202
  return `$${(cents / 100).toFixed(2)}`;
4200
4203
  }
4201
4204
  function formatCredits(n) {
@@ -4759,8 +4762,10 @@ function PlansView({
4759
4762
  downgradeScheduled,
4760
4763
  actionInFlight,
4761
4764
  onPlanAction,
4762
- onCancelDowngrade
4765
+ onCancelDowngrade,
4766
+ workspace
4763
4767
  }) {
4768
+ console.log("=============", products);
4764
4769
  const { toast } = useSettingsAdapters();
4765
4770
  const allPlans = (0, import_react8.useMemo)(() => {
4766
4771
  if (products.length > 0) {
@@ -4770,9 +4775,10 @@ function PlansView({
4770
4775
  productId: product.id,
4771
4776
  priceId: price.id,
4772
4777
  planName: price.metadata.name,
4773
- price: price.unit_amount === 0 ? "$0" : `${formatCurrency(price.unit_amount)} / month`,
4778
+ price: price.unit_amount === 0 ? price.currency === "inr" ? "\u20B90" : "$0" : `${formatCurrency(price.unit_amount, price.currency ?? "usd")} / month`,
4774
4779
  credits: `${Number(price.metadata.credits).toLocaleString()} credits`,
4775
4780
  amount: price.unit_amount,
4781
+ currency: price.currency?.toLowerCase(),
4776
4782
  isCurrent: price.id === currentPlanId
4777
4783
  }))
4778
4784
  );
@@ -4783,18 +4789,12 @@ function PlansView({
4783
4789
  () => [...allPlans].sort((a, b) => a.amount - b.amount),
4784
4790
  [allPlans]
4785
4791
  );
4792
+ console.log("All plans:::::::::::::::", workspace, sortedPlans);
4786
4793
  const isNotFreePlan = (planName) => {
4787
- if (planName) {
4788
- return [
4789
- "Free (DR Test)",
4790
- "Custom",
4791
- "custom",
4792
- "custom_shopify",
4793
- "custom shopify",
4794
- "Custom_Shopify"
4795
- ].includes(planName);
4794
+ if (planName.toLowerCase() === "free") {
4795
+ return false;
4796
4796
  }
4797
- return false;
4797
+ return true;
4798
4798
  };
4799
4799
  const freePlan = sortedPlans.find(
4800
4800
  (plan) => plan.amount === 0 && !isNotFreePlan(plan.planName)
@@ -5096,9 +5096,12 @@ function PlansView({
5096
5096
  {
5097
5097
  open: showUpgradeConfirmModal,
5098
5098
  currentPlanName: currentPlan?.planName || "Free",
5099
- currentPlanAmount: currentPlan ? currentPlan.amount === 0 ? "$0" : formatCurrency(currentPlan.amount) : "$0",
5099
+ currentPlanAmount: currentPlan ? currentPlan.amount === 0 ? "$0" : formatCurrency(currentPlan.amount, currentPlan.currency || "") : "$0",
5100
5100
  nextPlanName: selectedMiddlePlan?.planName || "Pro",
5101
- nextPlanAmount: selectedMiddlePlan ? selectedMiddlePlan.amount === 0 ? "$0" : formatCurrency(selectedMiddlePlan.amount) : "$0",
5101
+ nextPlanAmount: selectedMiddlePlan ? selectedMiddlePlan.amount === 0 ? "$0" : formatCurrency(
5102
+ selectedMiddlePlan.amount,
5103
+ selectedMiddlePlan.currency || ""
5104
+ ) : "$0",
5102
5105
  nextBillingDateLabel: formatNextBillingDateLabel(currentPlanEndDate),
5103
5106
  loading: isMiddleActionLoading,
5104
5107
  onClose: () => setShowUpgradeConfirmModal(false),
@@ -5472,7 +5475,9 @@ function UsageLogsTable({
5472
5475
  }
5473
5476
  );
5474
5477
  }
5475
- function BillingContent({ embedded = false } = {}) {
5478
+ function BillingContent({
5479
+ embedded = false
5480
+ } = {}) {
5476
5481
  const { auth, workspace, api, toast, navigation } = useSettingsAdapters();
5477
5482
  const user = auth.user;
5478
5483
  const workspaceId = workspace.current?.id ?? null;
@@ -5527,6 +5532,7 @@ function BillingContent({ embedded = false } = {}) {
5527
5532
  const fetchBillingOverview = (0, import_react8.useCallback)(async () => {
5528
5533
  setLoadingBilling(true);
5529
5534
  try {
5535
+ console.log("calling Billing apis");
5530
5536
  const [subRes, creditsRes, productsRes, customRes] = await Promise.all([
5531
5537
  api.fetchCurrentPlan().catch(() => null),
5532
5538
  api.fetchAvailableCredits().catch(() => null),
@@ -5536,7 +5542,17 @@ function BillingContent({ embedded = false } = {}) {
5536
5542
  if (subRes?.data) setSubscription(subRes.data);
5537
5543
  if (creditsRes?.availableCredits != null)
5538
5544
  setAvailableCredits(creditsRes.availableCredits);
5539
- if (productsRes?.data) setProducts(productsRes.data);
5545
+ if (productsRes?.data) {
5546
+ const normalizedWorkspaceCountry = workspace.current?.country?.trim().toUpperCase();
5547
+ const targetCurrency = normalizedWorkspaceCountry === "IN" ? "inr" : "usd";
5548
+ const filteredProducts = productsRes.data.map((product) => ({
5549
+ ...product,
5550
+ prices: product.prices.filter(
5551
+ (price) => price.currency?.toLowerCase() === targetCurrency
5552
+ )
5553
+ })).filter((product) => product.prices.length > 0);
5554
+ setProducts(filteredProducts);
5555
+ }
5540
5556
  if (customRes?.data?.credits != null) {
5541
5557
  setCustomPlanCredits(customRes.data.credits);
5542
5558
  }
@@ -5544,7 +5560,7 @@ function BillingContent({ embedded = false } = {}) {
5544
5560
  } finally {
5545
5561
  setLoadingBilling(false);
5546
5562
  }
5547
- }, [api]);
5563
+ }, [api, workspace.current?.country]);
5548
5564
  const fetchUserMap = (0, import_react8.useCallback)(async () => {
5549
5565
  try {
5550
5566
  const members = await api.fetchMembers();
@@ -5705,9 +5721,14 @@ function BillingContent({ embedded = false } = {}) {
5705
5721
  if (loadingBilling) return;
5706
5722
  setShowInitialLoader(false);
5707
5723
  }, [showInitialLoader, initialFetchTriggered, loadingBilling]);
5708
- const planName = subscription?.plan?.name?.trim() || (subscription?.plan?.amount != null && subscription.plan.amount > 0 ? "Pro" : "Free");
5724
+ console.log("subscription", subscription);
5725
+ const isProPlan = products.find(
5726
+ (product) => product.id == subscription?.stripeProductId && product.name == "Paid"
5727
+ );
5728
+ console.log("isProPlan", isProPlan);
5729
+ const planName = isProPlan ? "Pro" : subscription?.plan?.name?.trim() || (subscription?.plan?.amount ? subscription.plan.amount > 0 ? "Pro" : "Free" : "-");
5709
5730
  const isFreePlan = subscription?.plan?.amount == null || subscription.plan.amount === 0;
5710
- const planAmount = subscription?.plan?.amount != null ? `$${(subscription.plan.amount / 100).toFixed(2)} / month` : "$0";
5731
+ const planAmount = subscription?.plan?.amount != null ? `${subscription?.plan?.currency == "inr" ? "\u20B9" : "$"}${(subscription.plan.amount / 100).toFixed(2)} / ${subscription?.plan?.interval}` : "$0";
5711
5732
  const usedCredits = availableCredits != null && totalCredits != null ? Math.max(0, totalCredits - availableCredits) : null;
5712
5733
  const progressPct = usedCredits != null && totalCredits != null && totalCredits > 0 ? Math.min(100, Math.round(usedCredits / totalCredits * 100)) : 0;
5713
5734
  const remainingPct = 100 - progressPct;
@@ -5905,7 +5926,8 @@ function BillingContent({ embedded = false } = {}) {
5905
5926
  downgradeScheduled: Boolean(subscription?.downgradeScheduled),
5906
5927
  actionInFlight: planActionInFlight,
5907
5928
  onPlanAction: handlePlanAction,
5908
- onCancelDowngrade: handleCancelDowngrade
5929
+ onCancelDowngrade: handleCancelDowngrade,
5930
+ workspace: workspace.current
5909
5931
  }
5910
5932
  );
5911
5933
  if (embedded) return plansView;
package/dist/index.mjs CHANGED
@@ -3387,7 +3387,13 @@ function TeamsContent({
3387
3387
  }
3388
3388
 
3389
3389
  // src/billing-content.tsx
3390
- import { useState as useState7, useEffect as useEffect4, useCallback as useCallback3, useMemo as useMemo2, useRef as useRef2 } from "react";
3390
+ import {
3391
+ useState as useState7,
3392
+ useEffect as useEffect4,
3393
+ useCallback as useCallback3,
3394
+ useMemo as useMemo2,
3395
+ useRef as useRef2
3396
+ } from "react";
3391
3397
  import {
3392
3398
  ScPlanDetailsCard,
3393
3399
  ScCreditsUsageCard,
@@ -3402,7 +3408,6 @@ import {
3402
3408
  SiconLeft as SiconLeft2,
3403
3409
  SiconDoubleArrow,
3404
3410
  SiconClose as SiconClose4,
3405
- SiconCredits,
3406
3411
  SiconArtifacts as SiconArtifacts3,
3407
3412
  SiconPhotogenix as SiconPhotogenix3,
3408
3413
  SiconCatalogix as SiconCatalogix2
@@ -4209,7 +4214,10 @@ function formatDate(iso) {
4209
4214
  return iso;
4210
4215
  }
4211
4216
  }
4212
- function formatCurrency(cents) {
4217
+ function formatCurrency(cents, currency) {
4218
+ if (currency === "inr") {
4219
+ return `\u20B9${(cents / 100).toFixed(2)}`;
4220
+ }
4213
4221
  return `$${(cents / 100).toFixed(2)}`;
4214
4222
  }
4215
4223
  function formatCredits(n) {
@@ -4773,8 +4781,10 @@ function PlansView({
4773
4781
  downgradeScheduled,
4774
4782
  actionInFlight,
4775
4783
  onPlanAction,
4776
- onCancelDowngrade
4784
+ onCancelDowngrade,
4785
+ workspace
4777
4786
  }) {
4787
+ console.log("=============", products);
4778
4788
  const { toast } = useSettingsAdapters();
4779
4789
  const allPlans = useMemo2(() => {
4780
4790
  if (products.length > 0) {
@@ -4784,9 +4794,10 @@ function PlansView({
4784
4794
  productId: product.id,
4785
4795
  priceId: price.id,
4786
4796
  planName: price.metadata.name,
4787
- price: price.unit_amount === 0 ? "$0" : `${formatCurrency(price.unit_amount)} / month`,
4797
+ price: price.unit_amount === 0 ? price.currency === "inr" ? "\u20B90" : "$0" : `${formatCurrency(price.unit_amount, price.currency ?? "usd")} / month`,
4788
4798
  credits: `${Number(price.metadata.credits).toLocaleString()} credits`,
4789
4799
  amount: price.unit_amount,
4800
+ currency: price.currency?.toLowerCase(),
4790
4801
  isCurrent: price.id === currentPlanId
4791
4802
  }))
4792
4803
  );
@@ -4797,18 +4808,12 @@ function PlansView({
4797
4808
  () => [...allPlans].sort((a, b) => a.amount - b.amount),
4798
4809
  [allPlans]
4799
4810
  );
4811
+ console.log("All plans:::::::::::::::", workspace, sortedPlans);
4800
4812
  const isNotFreePlan = (planName) => {
4801
- if (planName) {
4802
- return [
4803
- "Free (DR Test)",
4804
- "Custom",
4805
- "custom",
4806
- "custom_shopify",
4807
- "custom shopify",
4808
- "Custom_Shopify"
4809
- ].includes(planName);
4813
+ if (planName.toLowerCase() === "free") {
4814
+ return false;
4810
4815
  }
4811
- return false;
4816
+ return true;
4812
4817
  };
4813
4818
  const freePlan = sortedPlans.find(
4814
4819
  (plan) => plan.amount === 0 && !isNotFreePlan(plan.planName)
@@ -5110,9 +5115,12 @@ function PlansView({
5110
5115
  {
5111
5116
  open: showUpgradeConfirmModal,
5112
5117
  currentPlanName: currentPlan?.planName || "Free",
5113
- currentPlanAmount: currentPlan ? currentPlan.amount === 0 ? "$0" : formatCurrency(currentPlan.amount) : "$0",
5118
+ currentPlanAmount: currentPlan ? currentPlan.amount === 0 ? "$0" : formatCurrency(currentPlan.amount, currentPlan.currency || "") : "$0",
5114
5119
  nextPlanName: selectedMiddlePlan?.planName || "Pro",
5115
- nextPlanAmount: selectedMiddlePlan ? selectedMiddlePlan.amount === 0 ? "$0" : formatCurrency(selectedMiddlePlan.amount) : "$0",
5120
+ nextPlanAmount: selectedMiddlePlan ? selectedMiddlePlan.amount === 0 ? "$0" : formatCurrency(
5121
+ selectedMiddlePlan.amount,
5122
+ selectedMiddlePlan.currency || ""
5123
+ ) : "$0",
5116
5124
  nextBillingDateLabel: formatNextBillingDateLabel(currentPlanEndDate),
5117
5125
  loading: isMiddleActionLoading,
5118
5126
  onClose: () => setShowUpgradeConfirmModal(false),
@@ -5486,7 +5494,9 @@ function UsageLogsTable({
5486
5494
  }
5487
5495
  );
5488
5496
  }
5489
- function BillingContent({ embedded = false } = {}) {
5497
+ function BillingContent({
5498
+ embedded = false
5499
+ } = {}) {
5490
5500
  const { auth, workspace, api, toast, navigation } = useSettingsAdapters();
5491
5501
  const user = auth.user;
5492
5502
  const workspaceId = workspace.current?.id ?? null;
@@ -5541,6 +5551,7 @@ function BillingContent({ embedded = false } = {}) {
5541
5551
  const fetchBillingOverview = useCallback3(async () => {
5542
5552
  setLoadingBilling(true);
5543
5553
  try {
5554
+ console.log("calling Billing apis");
5544
5555
  const [subRes, creditsRes, productsRes, customRes] = await Promise.all([
5545
5556
  api.fetchCurrentPlan().catch(() => null),
5546
5557
  api.fetchAvailableCredits().catch(() => null),
@@ -5550,7 +5561,17 @@ function BillingContent({ embedded = false } = {}) {
5550
5561
  if (subRes?.data) setSubscription(subRes.data);
5551
5562
  if (creditsRes?.availableCredits != null)
5552
5563
  setAvailableCredits(creditsRes.availableCredits);
5553
- if (productsRes?.data) setProducts(productsRes.data);
5564
+ if (productsRes?.data) {
5565
+ const normalizedWorkspaceCountry = workspace.current?.country?.trim().toUpperCase();
5566
+ const targetCurrency = normalizedWorkspaceCountry === "IN" ? "inr" : "usd";
5567
+ const filteredProducts = productsRes.data.map((product) => ({
5568
+ ...product,
5569
+ prices: product.prices.filter(
5570
+ (price) => price.currency?.toLowerCase() === targetCurrency
5571
+ )
5572
+ })).filter((product) => product.prices.length > 0);
5573
+ setProducts(filteredProducts);
5574
+ }
5554
5575
  if (customRes?.data?.credits != null) {
5555
5576
  setCustomPlanCredits(customRes.data.credits);
5556
5577
  }
@@ -5558,7 +5579,7 @@ function BillingContent({ embedded = false } = {}) {
5558
5579
  } finally {
5559
5580
  setLoadingBilling(false);
5560
5581
  }
5561
- }, [api]);
5582
+ }, [api, workspace.current?.country]);
5562
5583
  const fetchUserMap = useCallback3(async () => {
5563
5584
  try {
5564
5585
  const members = await api.fetchMembers();
@@ -5719,9 +5740,14 @@ function BillingContent({ embedded = false } = {}) {
5719
5740
  if (loadingBilling) return;
5720
5741
  setShowInitialLoader(false);
5721
5742
  }, [showInitialLoader, initialFetchTriggered, loadingBilling]);
5722
- const planName = subscription?.plan?.name?.trim() || (subscription?.plan?.amount != null && subscription.plan.amount > 0 ? "Pro" : "Free");
5743
+ console.log("subscription", subscription);
5744
+ const isProPlan = products.find(
5745
+ (product) => product.id == subscription?.stripeProductId && product.name == "Paid"
5746
+ );
5747
+ console.log("isProPlan", isProPlan);
5748
+ const planName = isProPlan ? "Pro" : subscription?.plan?.name?.trim() || (subscription?.plan?.amount ? subscription.plan.amount > 0 ? "Pro" : "Free" : "-");
5723
5749
  const isFreePlan = subscription?.plan?.amount == null || subscription.plan.amount === 0;
5724
- const planAmount = subscription?.plan?.amount != null ? `$${(subscription.plan.amount / 100).toFixed(2)} / month` : "$0";
5750
+ const planAmount = subscription?.plan?.amount != null ? `${subscription?.plan?.currency == "inr" ? "\u20B9" : "$"}${(subscription.plan.amount / 100).toFixed(2)} / ${subscription?.plan?.interval}` : "$0";
5725
5751
  const usedCredits = availableCredits != null && totalCredits != null ? Math.max(0, totalCredits - availableCredits) : null;
5726
5752
  const progressPct = usedCredits != null && totalCredits != null && totalCredits > 0 ? Math.min(100, Math.round(usedCredits / totalCredits * 100)) : 0;
5727
5753
  const remainingPct = 100 - progressPct;
@@ -5919,7 +5945,8 @@ function BillingContent({ embedded = false } = {}) {
5919
5945
  downgradeScheduled: Boolean(subscription?.downgradeScheduled),
5920
5946
  actionInFlight: planActionInFlight,
5921
5947
  onPlanAction: handlePlanAction,
5922
- onCancelDowngrade: handleCancelDowngrade
5948
+ onCancelDowngrade: handleCancelDowngrade,
5949
+ workspace: workspace.current
5923
5950
  }
5924
5951
  );
5925
5952
  if (embedded) return plansView;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamoid/settings",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -15,7 +15,9 @@
15
15
  "build": "npm run build:js && npm run build:dts",
16
16
  "build:js": "esbuild src/index.ts --bundle --loader:.module.css=local-css --external:react --external:react-dom --external:@streamoid/icons --external:@streamoid/ui --outfile=dist/index.js --format=cjs && esbuild src/index.ts --bundle --loader:.module.css=local-css --external:react --external:react-dom --external:@streamoid/icons --external:@streamoid/ui --outfile=dist/index.mjs --format=esm",
17
17
  "build:dts": "tsup src/index.ts --dts-only",
18
- "dev": "npm run build -- --watch",
18
+ "watch:js": "esbuild src/index.ts --bundle --loader:.module.css=local-css --external:react --external:react-dom --external:@streamoid/icons --external:@streamoid/ui --outfile=dist/index.js --format=cjs --watch=forever & esbuild src/index.ts --bundle --loader:.module.css=local-css --external:react --external:react-dom --external:@streamoid/icons --external:@streamoid/ui --outfile=dist/index.mjs --format=esm --watch=forever & wait",
19
+ "watch:dts": "tsup src/index.ts --dts-only --watch",
20
+ "dev": "sh -c 'npm run watch:js & npm run watch:dts & wait'",
19
21
  "lint": "eslint src/",
20
22
  "typecheck": "tsc --noEmit"
21
23
  },