@swarm.ing/pieui 2.0.14 → 2.0.15

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=step1-local.test.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step1-local.test.d.cts","sourceRoot":"","sources":["../../src/__tests__/step1-local.test.cjs"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=step2-remote.test.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step2-remote.test.d.cts","sourceRoot":"","sources":["../../src/__tests__/step2-remote.test.cjs"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=step3-contract.test.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step3-contract.test.d.cts","sourceRoot":"","sources":["../../src/__tests__/step3-contract.test.cjs"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=step4-ci.test.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step4-ci.test.d.cts","sourceRoot":"","sources":["../../src/__tests__/step4-ci.test.cjs"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=step5-regres.test.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step5-regres.test.d.cts","sourceRoot":"","sources":["../../src/__tests__/step5-regres.test.cjs"],"names":[],"mappings":""}
package/dist/cli.js CHANGED
@@ -193049,7 +193049,7 @@ var parseArgs = (argv) => {
193049
193049
  if (command === "remove" && argv[1]) {
193050
193050
  removeComponentName = argv[1];
193051
193051
  }
193052
- if ((command === "create-pie-app" || command === "create") && argv[1]) {
193052
+ if ((command === "create-pie-app" || command === "create-pieui" || command === "create") && argv[1]) {
193053
193053
  createAppName = argv[1];
193054
193054
  }
193055
193055
  if (command === "list") {
@@ -193140,6 +193140,7 @@ var printUsage = () => {
193140
193140
  console.log("Commands:");
193141
193141
  console.log(" create <AppName> Create a Next.js app and run pieui init inside it");
193142
193142
  console.log(" create-pie-app <AppName> Create a blank Next.js web template for PieUI (bun create next-app under the hood)");
193143
+ console.log(" create-pieui <AppName> Alias for create-pie-app");
193143
193144
  console.log(" login Sign in to PieUI and save credentials to .pie/config.json");
193144
193145
  console.log(" init Initialize piecomponents directory with registry.ts");
193145
193146
  console.log(" card add [type] <ComponentName> [--io] [--ajax] Create a new component in piecomponents directory");
@@ -193193,6 +193194,7 @@ var printUsage = () => {
193193
193194
  console.log(" pieui init");
193194
193195
  console.log(" pieui create my-pie-app");
193195
193196
  console.log(" pieui create-pie-app my-pie-app");
193197
+ console.log(" pieui create-pieui my-pie-app");
193196
193198
  console.log(" pieui init --out-dir packages/app");
193197
193199
  console.log(" pieui card add MyCustomCard # Creates complex-container by default");
193198
193200
  console.log(" pieui card add simple MySimpleCard # Creates simple component");
@@ -198520,6 +198522,254 @@ var createCommand = (appName) => {
198520
198522
  runBunCommand(bunBin, ["run", "dev"], appDir);
198521
198523
  };
198522
198524
 
198525
+ // src/code/commands/createPieApp.ts
198526
+ var import_fs13 = __toESM(require("fs"));
198527
+ var import_path15 = __toESM(require("path"));
198528
+ var import_child_process2 = require("child_process");
198529
+ var DEFAULT_TEMPLATE_SPEC2 = "next-app@latest";
198530
+ var SHARED_TEMPLATE_DIR_ENV = "PIEUI_SHARED_TEMPLATE_DIR";
198531
+ var AI_EXCHANGE_BOT_DIR_ENV = "PIEUI_AI_EXCHANGE_BOT_DIR";
198532
+ var DEFAULT_SHARED_SIMPLE_TSX = `"use client";
198533
+
198534
+ import { useEffect, useState } from "react";
198535
+ import { useRouter } from "next/navigation";
198536
+ import { PieTelegramRoot, PieQueryOptions } from "@swarm.ing/pieui";
198537
+
198538
+ import { usePathname, useSearchParams } from "next/navigation";
198539
+ import LoadingScreen from "@/components/LoadingScreen";
198540
+ import { ErrorToast } from "@/components/ErrorToast";
198541
+ import piecache from "@/app/piecache.json";
198542
+ import "@/piecomponents/registry";
198543
+
198544
+ const pieQueryOptions: PieQueryOptions = {
198545
+ staleTime: 0, // always stale - refetch on every mount
198546
+ gcTime: 60 * 1000, // keep cache for 1 minute
198547
+ refetchOnMount: "always" as const,
198548
+ refetchOnWindowFocus: "always" as const,
198549
+ refetchOnReconnect: "always" as const,
198550
+ };
198551
+
198552
+ export default function PiePage({
198553
+ fallback,
198554
+ }: { fallback?: React.ReactNode } = {}) {
198555
+ const router = useRouter();
198556
+ const pathname = usePathname();
198557
+ const searchParams = useSearchParams();
198558
+ const search = searchParams.toString();
198559
+ const [showVpnError, setShowVpnError] = useState(false);
198560
+
198561
+ const handleError = () => {
198562
+ if (showVpnError) {
198563
+ }
198564
+ setShowVpnError(true);
198565
+ setTimeout(() => setShowVpnError(false), 2000);
198566
+ };
198567
+
198568
+ useEffect(() => {
198569
+ const backButton = window.Telegram?.WebApp?.BackButton;
198570
+ if (!backButton) return;
198571
+
198572
+ const slashCount = (pathname.match(/\\//g) || []).length;
198573
+ const historyState = window.history.state as { idx?: number } | null;
198574
+ const hasHistory =
198575
+ typeof historyState?.idx === "number"
198576
+ ? historyState.idx > 0
198577
+ : window.history.length > 1;
198578
+ const showBack = slashCount > 1 && hasHistory;
198579
+
198580
+ const handleBack = () => router.back();
198581
+
198582
+ if (showBack) {
198583
+ backButton.onClick(handleBack);
198584
+ backButton.show();
198585
+ } else {
198586
+ backButton.offClick(handleBack);
198587
+ backButton.hide();
198588
+ }
198589
+
198590
+ return () => {
198591
+ backButton.offClick(handleBack);
198592
+ backButton.hide();
198593
+ };
198594
+ }, [router, pathname]);
198595
+
198596
+ useEffect(() => {
198597
+ const webApp = window.Telegram?.WebApp;
198598
+ webApp?.expand();
198599
+ webApp?.disableVerticalSwipes();
198600
+ }, []);
198601
+
198602
+ const Root = PieTelegramRoot;
198603
+
198604
+ return (
198605
+ <>
198606
+ <ErrorToast
198607
+ visible={showVpnError}
198608
+ message="Connection issues. Please check your VPN."
198609
+ />
198610
+ <Root
198611
+ location={{
198612
+ pathname: pathname,
198613
+ search: search,
198614
+ }}
198615
+ config={{
198616
+ apiServer: process.env.PIE_API_SERVER!,
198617
+ centrifugeServer: process.env.PIE_CENTRIFUGE_SERVER!,
198618
+ enableRenderingLog: true,
198619
+ pageProcessor: "telegram",
198620
+ }}
198621
+ onNavigate={(url) => router.push(url)}
198622
+ fallback={<LoadingScreen />}
198623
+ piecache={piecache as any}
198624
+ queryOptions={pieQueryOptions}
198625
+ onError={handleError}
198626
+ />
198627
+ </>
198628
+ );
198629
+ }
198630
+ `;
198631
+ var BACKEND_LINK_COMMENT = "// TODO(pie-backend): Link generated Python Unicorn backend routes here once backend template sync is enabled.";
198632
+ var updateNextScriptsToBun = (packageJsonPath) => {
198633
+ if (!import_fs13.default.existsSync(packageJsonPath))
198634
+ return;
198635
+ try {
198636
+ const pkg = JSON.parse(import_fs13.default.readFileSync(packageJsonPath, "utf8"));
198637
+ if (!pkg.scripts)
198638
+ return;
198639
+ const updates = {
198640
+ dev: "bun --bun next dev",
198641
+ build: "bun --bun next build",
198642
+ start: "bun --bun next start"
198643
+ };
198644
+ for (const [scriptName, scriptValue] of Object.entries(updates)) {
198645
+ if (pkg.scripts[scriptName]) {
198646
+ pkg.scripts[scriptName] = scriptValue;
198647
+ }
198648
+ }
198649
+ import_fs13.default.writeFileSync(packageJsonPath, `${JSON.stringify(pkg, null, 2)}
198650
+ `, "utf8");
198651
+ } catch {
198652
+ }
198653
+ };
198654
+ var appendBackendLinkComment = (pagePath) => {
198655
+ if (!import_fs13.default.existsSync(pagePath))
198656
+ return;
198657
+ const page = import_fs13.default.readFileSync(pagePath, "utf8");
198658
+ if (page.includes(BACKEND_LINK_COMMENT))
198659
+ return;
198660
+ import_fs13.default.writeFileSync(pagePath, `${page.trimEnd()}
198661
+
198662
+ ${BACKEND_LINK_COMMENT}
198663
+ `, "utf8");
198664
+ };
198665
+ var uniq = (items) => [...new Set(items)];
198666
+ var isDirectory = (target) => {
198667
+ try {
198668
+ return import_fs13.default.statSync(target).isDirectory();
198669
+ } catch {
198670
+ return false;
198671
+ }
198672
+ };
198673
+ var resolveSharedTemplateDir = () => {
198674
+ const explicitSharedDir = process.env[SHARED_TEMPLATE_DIR_ENV];
198675
+ if (explicitSharedDir) {
198676
+ const normalized = import_path15.default.resolve(explicitSharedDir);
198677
+ if (!isDirectory(normalized)) {
198678
+ throw new Error(`${SHARED_TEMPLATE_DIR_ENV} points to a missing directory: ${normalized}`);
198679
+ }
198680
+ return normalized;
198681
+ }
198682
+ const cwd = process.cwd();
198683
+ const baseDirs = [cwd, import_path15.default.resolve(cwd, ".."), import_path15.default.resolve(cwd, "../..")];
198684
+ const explicitRepoDir = process.env[AI_EXCHANGE_BOT_DIR_ENV];
198685
+ if (explicitRepoDir) {
198686
+ baseDirs.unshift(import_path15.default.resolve(explicitRepoDir));
198687
+ }
198688
+ const repoNames = ["ai-exchange-bot", "ai-exchange-web", "a-exchange-web"];
198689
+ const repoCandidates = uniq([
198690
+ ...baseDirs,
198691
+ ...baseDirs.flatMap((base) => repoNames.map((name) => import_path15.default.join(base, name)))
198692
+ ]).filter((candidate) => isDirectory(candidate));
198693
+ const sharedSubpaths = [
198694
+ "_shared",
198695
+ "src/_shared",
198696
+ "app/_shared",
198697
+ "pages/_shared",
198698
+ "pages/components/_shared",
198699
+ "apps/web/_shared",
198700
+ "apps/web/src/_shared"
198701
+ ];
198702
+ for (const repoDir of repoCandidates) {
198703
+ for (const subpath of sharedSubpaths) {
198704
+ const candidate = import_path15.default.join(repoDir, subpath);
198705
+ if (isDirectory(candidate)) {
198706
+ return candidate;
198707
+ }
198708
+ }
198709
+ }
198710
+ return null;
198711
+ };
198712
+ var copySharedTemplate = (sharedTemplateDir, appDir) => {
198713
+ const destination = import_path15.default.join(appDir, "_shared");
198714
+ import_fs13.default.cpSync(sharedTemplateDir, destination, { recursive: true, force: true });
198715
+ };
198716
+ var scaffoldFallbackSharedTemplate = (appDir) => {
198717
+ const destination = import_path15.default.join(appDir, "_shared");
198718
+ import_fs13.default.mkdirSync(destination, { recursive: true });
198719
+ import_fs13.default.writeFileSync(import_path15.default.join(destination, "simple.tsx"), DEFAULT_SHARED_SIMPLE_TSX, "utf8");
198720
+ };
198721
+ var createPieAppCommand = (appName) => {
198722
+ const trimmedAppName = appName.trim();
198723
+ if (!trimmedAppName) {
198724
+ console.error("[pieui] Error: App name is required for create-pie-app command");
198725
+ process.exit(1);
198726
+ }
198727
+ const appDir = import_path15.default.resolve(process.cwd(), trimmedAppName);
198728
+ if (import_fs13.default.existsSync(appDir)) {
198729
+ console.error(`[pieui] Error: Target directory already exists: ${trimmedAppName}`);
198730
+ process.exit(1);
198731
+ }
198732
+ const bunBin = process.env.PIEUI_CREATE_BUN_BIN || "bun";
198733
+ const templateSpec = process.env.PIEUI_CREATE_NEXT_APP_SPEC || DEFAULT_TEMPLATE_SPEC2;
198734
+ console.log(`[pieui] Creating blank PieUI web template in "${trimmedAppName}"...`);
198735
+ const args = ["create", templateSpec, trimmedAppName, "--yes"];
198736
+ const result = import_child_process2.spawnSync(bunBin, args, {
198737
+ cwd: process.cwd(),
198738
+ stdio: "inherit",
198739
+ env: process.env
198740
+ });
198741
+ if (result.error) {
198742
+ throw result.error;
198743
+ }
198744
+ if (result.status !== 0) {
198745
+ throw new Error(`create-pie-app failed (exit code ${result.status ?? "unknown"})`);
198746
+ }
198747
+ const sharedTemplateDir = resolveSharedTemplateDir();
198748
+ if (sharedTemplateDir) {
198749
+ copySharedTemplate(sharedTemplateDir, appDir);
198750
+ } else {
198751
+ scaffoldFallbackSharedTemplate(appDir);
198752
+ console.warn([
198753
+ `[pieui] Warning: Could not locate a _shared template directory automatically.`,
198754
+ `Generated fallback "${trimmedAppName}/_shared/simple.tsx".`,
198755
+ `Set ${SHARED_TEMPLATE_DIR_ENV}=/absolute/path/to/_shared`,
198756
+ `or ${AI_EXCHANGE_BOT_DIR_ENV}=/absolute/path/to/ai-exchange-bot to copy a project-standard folder.`
198757
+ ].join(" "));
198758
+ }
198759
+ updateNextScriptsToBun(import_path15.default.join(appDir, "package.json"));
198760
+ appendBackendLinkComment(import_path15.default.join(appDir, "app", "page.tsx"));
198761
+ console.log("[pieui] Template created successfully.");
198762
+ if (sharedTemplateDir) {
198763
+ console.log(`[pieui] Copied _shared from: ${sharedTemplateDir}`);
198764
+ } else {
198765
+ console.log(`[pieui] Using fallback _shared scaffold. Update it with your project standards.`);
198766
+ }
198767
+ console.log(`[pieui] Next steps:`);
198768
+ console.log(` 1. cd ${trimmedAppName}`);
198769
+ console.log(` 2. bun install`);
198770
+ console.log(` 3. bun run dev`);
198771
+ };
198772
+
198523
198773
  // src/code/commands/login.ts
198524
198774
  var import_node_child_process = require("node:child_process");
198525
198775
  var import_node_crypto = require("node:crypto");
@@ -198527,6 +198777,8 @@ var import_node_fs = __toESM(require("node:fs"));
198527
198777
  var import_node_path2 = __toESM(require("node:path"));
198528
198778
  var CONNECT_BASE = "https://pieui.swarm.ing/connect";
198529
198779
  var CREDENTIALS_API = "https://api-pieui.swarm.ing/api/external/credentials";
198780
+ var CONNECT_BASE_ENV = "PIEUI_LOGIN_CONNECT_BASE";
198781
+ var CREDENTIALS_API_ENV = "PIEUI_LOGIN_CREDENTIALS_API";
198530
198782
  var CODE_LENGTH = 32;
198531
198783
  var generateCode = (length = CODE_LENGTH) => {
198532
198784
  const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@@ -198600,8 +198852,10 @@ async function loginCommand(options = {}) {
198600
198852
  fetchImpl = fetch,
198601
198853
  sleepImpl = defaultSleep
198602
198854
  } = options;
198855
+ const connectBase = process.env[CONNECT_BASE_ENV] || CONNECT_BASE;
198856
+ const credentialsApi = process.env[CREDENTIALS_API_ENV] || CREDENTIALS_API;
198603
198857
  const code = generateCode(CODE_LENGTH);
198604
- const connectUrl = `${CONNECT_BASE}?${new URLSearchParams({ code }).toString()}`;
198858
+ const connectUrl = `${connectBase}?${new URLSearchParams({ code }).toString()}`;
198605
198859
  console.log(`Open link in browser:
198606
198860
  `);
198607
198861
  console.log(connectUrl);
@@ -198611,7 +198865,7 @@ async function loginCommand(options = {}) {
198611
198865
  }
198612
198866
  const pieDir = import_node_path2.default.join(cwd, ".pie");
198613
198867
  const configPath = import_node_path2.default.join(pieDir, "config.json");
198614
- const url = `${CREDENTIALS_API}?${new URLSearchParams({ code }).toString()}`;
198868
+ const url = `${credentialsApi}?${new URLSearchParams({ code }).toString()}`;
198615
198869
  let first = true;
198616
198870
  while (true) {
198617
198871
  if (!first) {
@@ -198685,6 +198939,15 @@ var main = async () => {
198685
198939
  }
198686
198940
  createCommand(createAppName);
198687
198941
  return;
198942
+ case "create-pie-app":
198943
+ case "create-pieui":
198944
+ if (!createAppName) {
198945
+ console.error("[pieui] Error: App name is required for create-pie-app command");
198946
+ printUsage();
198947
+ process.exit(1);
198948
+ }
198949
+ createPieAppCommand(createAppName);
198950
+ return;
198688
198951
  case "card":
198689
198952
  if (cardAction !== "add") {
198690
198953
  console.error("[pieui] Error: Supported card subcommands: add");
@@ -1 +1 @@
1
- {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/code/args.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKR,UAAU,EACb,MAAM,SAAS,CAAA;AAEhB,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,EAAE,KAAG,UA0I1C,CAAA;AAED,eAAO,MAAM,UAAU,YA0JtB,CAAA"}
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/code/args.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKR,UAAU,EACb,MAAM,SAAS,CAAA;AAEhB,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,EAAE,KAAG,UA+I1C,CAAA;AAED,eAAO,MAAM,UAAU,YA8JtB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"createPieApp.d.ts","sourceRoot":"","sources":["../../../src/code/commands/createPieApp.ts"],"names":[],"mappings":"AA4HA,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,SAqDlD,CAAA"}
1
+ {"version":3,"file":"createPieApp.d.ts","sourceRoot":"","sources":["../../../src/code/commands/createPieApp.ts"],"names":[],"mappings":"AAmOA,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,SAuElD,CAAA"}
@@ -1,5 +1,7 @@
1
1
  export declare const CONNECT_BASE = "https://pieui.swarm.ing/connect";
2
2
  export declare const CREDENTIALS_API = "https://api-pieui.swarm.ing/api/external/credentials";
3
+ export declare const CONNECT_BASE_ENV = "PIEUI_LOGIN_CONNECT_BASE";
4
+ export declare const CREDENTIALS_API_ENV = "PIEUI_LOGIN_CREDENTIALS_API";
3
5
  export type LoginCommandOptions = {
4
6
  cwd?: string;
5
7
  pollIntervalMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/code/commands/login.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,YAAY,oCAAoC,CAAA;AAC7D,eAAO,MAAM,eAAe,yDAC8B,CAAA;AA2E1D,MAAM,MAAM,mBAAmB,GAAG;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5C,CAAA;AAED,wBAAsB,YAAY,CAC9B,OAAO,GAAE,mBAAwB,GAClC,OAAO,CAAC,IAAI,CAAC,CAiFf"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/code/commands/login.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,YAAY,oCAAoC,CAAA;AAC7D,eAAO,MAAM,eAAe,yDAC8B,CAAA;AAC1D,eAAO,MAAM,gBAAgB,6BAA6B,CAAA;AAC1D,eAAO,MAAM,mBAAmB,gCAAgC,CAAA;AA2EhE,MAAM,MAAM,mBAAmB,GAAG;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5C,CAAA;AAED,wBAAsB,YAAY,CAC9B,OAAO,GAAE,mBAAwB,GAClC,OAAO,CAAC,IAAI,CAAC,CAmFf"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarm.ing/pieui",
3
- "version": "2.0.14",
3
+ "version": "2.0.15",
4
4
  "description": "A React component library featuring PieCard component",
5
5
  "repository": {
6
6
  "type": "git",
@@ -61,7 +61,13 @@
61
61
  "publish:npm": "npm publish",
62
62
  "publish:beta": "npm publish --tag beta",
63
63
  "unrelease": "git tag -d v$(node -p \"require('./package.json').version\") && git push origin :refs/tags/v$(node -p \"require('./package.json').version\")",
64
- "version:current": "node -p \"require('./package.json').version\""
64
+ "version:current": "node -p \"require('./package.json').version\"",
65
+ "test:step1": "bun test src/__tests__/step1-local.test.cjs",
66
+ "test:step2": "bun test src/__tests__/step2-remote.test.cjs",
67
+ "test:step3": "bun test src/__tests__/step3-contract.test.cjs",
68
+ "test:step4": "bun test src/__tests__/step4-ci.test.cjs",
69
+ "test:step5": "bun test src/__tests__/step5-regres.test.cjs",
70
+ "test:steps": "bun test src/__tests__/step1-local.test.cjs src/__tests__/step2-remote.test.cjs src/__tests__/step3-contract.test.cjs src/__tests__/step4-ci.test.cjs src/__tests__/step5-regres.test.cjs"
65
71
  },
66
72
  "keywords": [
67
73
  "react",