History

feat(settings): add open source license notices

Julius Marminge committed 2 months ago

Commit 3ffdc2a1 parent 31c1c5953 files · +2895 −14Browse files

Unified · Split

53 files
  1. .gitignore +1 −0
  2. apps/mobile/licenses/badgin.txt +23 −0
  3. apps/mobile/licenses/boolbase.txt +13 −0
  4. apps/mobile/licenses/bplist-parser.txt +22 −0
  5. apps/mobile/licenses/expo-devcert.txt +21 −0
  6. apps/mobile/licenses/expo-xcpretty.txt +29 −0
  7. apps/mobile/licenses/fb-dotslash.txt +21 −0
  8. apps/mobile/licenses/ghostty-kit.txt +32 −0
  9. apps/mobile/licenses/jimp-compact.txt +21 −0
  10. apps/mobile/licenses/meslo-lgs-nf.txt +13 −0
  11. apps/mobile/licenses/metro.txt +21 −0
  12. apps/mobile/licenses/react-native-ai.txt +22 −0
  13. apps/mobile/licenses/react-native-nitro-modules.txt +21 −0
  14. apps/mobile/licenses/react-remove-scroll-bar.txt +21 −0
  15. apps/mobile/licenses/standard-navigation.txt +21 −0
  16. apps/mobile/licenses/stream-buffers.txt +24 −0
  17. apps/mobile/licenses/structured-headers.txt +21 −0
  18. apps/mobile/metro.config.js +61 −5
  19. apps/mobile/src/Stack.tsx +18 −0
  20. apps/mobile/src/features/settings/SettingsOpenSourceLicensesRouteScreen.tsx +212 −0
  21. apps/mobile/src/features/settings/SettingsRouteScreen.tsx +5 −0
  22. apps/mobile/src/features/settings/components/settings-sheet-targets.ts +1 −0
  23. apps/mobile/src/features/settings/mobileThirdPartyLicenses.ts +4 −0
  24. apps/mobile/src/types/mobile-third-party-licenses.d.ts +4 −0
  25. apps/web/THIRD_PARTY_NOTICES.md +20 −7
  26. apps/web/licenses/electron-internal-extract-zip.txt +25 −0
  27. apps/web/licenses/glob-to-regexp.txt +24 −0
  28. apps/web/licenses/isarray.txt +21 −0
  29. apps/web/licenses/keyv.txt +22 −0
  30. apps/web/licenses/kubernetes-types.txt +176 −0
  31. apps/web/licenses/lazy-val.txt +21 −0
  32. apps/web/licenses/lru-map.txt +19 −0
  33. apps/web/licenses/msgpackr-extract-linux-x64.txt +21 −0
  34. apps/web/licenses/pierre-theming.txt +189 −0
  35. apps/web/licenses/react-grab-cli.txt +21 −0
  36. apps/web/licenses/type-fest.txt +9 −0
  37. apps/web/src/components/settings/OpenSourceLicenses.tsx +205 −0
  38. apps/web/src/components/settings/SettingsBreadcrumb.tsx +1 −0
  39. apps/web/src/components/settings/SettingsPanels.tsx +13 −0
  40. apps/web/src/components/settings/SettingsSidebarNav.tsx +8 −1
  41. apps/web/src/components/settings/settingsSearch.ts +5 −0
  42. apps/web/src/routeTree.gen.ts +22 −0
  43. apps/web/src/routes/settings.open-source-licenses.tsx +7 −0
  44. apps/web/tsconfig.json +2 −1
  45. apps/web/vite.config.ts +10 −0
  46. docs/internals/open-source-licenses.md +84 −0
  47. docs/user/open-source-licenses.md +12 −0
  48. packages/shared/package.json +4 −0
  49. packages/shared/src/thirdPartyLicenses.test.ts +63 −0
  50. packages/shared/src/thirdPartyLicenses.ts +103 −0
  51. scripts/lib/third-party-licenses.test.ts +245 −0
  52. scripts/lib/third-party-licenses.ts +710 −0
  53. third-party-licenses.config.json +181 −0
.gitignore+1−0
@@ -30,6 +30,7 @@ dist-electron/ · 29 unmodified lines
3030 .electron-runtime/
3131 .showcase/
3232 apps/mobile/.showcase/
33+apps/mobile/.generated/
3334 artifacts/app-store/screenshots/
3435 .github/pr-assets/
3536 native/**/target/
apps/mobile/licenses/badgin.txt+23−0
@@ -0,0 +1,23 @@
1+badgin is a refactored fork of Tinycon.
2+
3+MIT License
4+
5+Copyright (c) Julian Hundeloh and Tinycon contributors
6+
7+Permission is hereby granted, free of charge, to any person obtaining a copy
8+of this software and associated documentation files (the "Software"), to deal
9+in the Software without restriction, including without limitation the rights
10+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+copies of the Software, and to permit persons to whom the Software is
12+furnished to do so, subject to the following conditions:
13+
14+The above copyright notice and this permission notice shall be included in all
15+copies or substantial portions of the Software.
16+
17+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+SOFTWARE.
apps/mobile/licenses/boolbase.txt+13−0
@@ -0,0 +1,13 @@
1+Copyright (c) 2014-2015, Felix Boehm <me@feedic.com>
2+
3+Permission to use, copy, modify, and/or distribute this software for any
4+purpose with or without fee is hereby granted, provided that the above
5+copyright notice and this permission notice appear in all copies.
6+
7+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
apps/mobile/licenses/bplist-parser.txt+22−0
@@ -0,0 +1,22 @@
1+The MIT License
2+
3+Copyright (c) 2012 Near Infinity Corporation
4+
5+Permission is hereby granted, free of charge, to any person obtaining
6+a copy of this software and associated documentation files (the
7+"Software"), to deal in the Software without restriction, including
8+without limitation the rights to use, copy, modify, merge, publish,
9+distribute, sublicense, and/or sell copies of the Software, and to
10+permit persons to whom the Software is furnished to do so, subject to
11+the following conditions:
12+
13+The above copyright notice and this permission notice shall be
14+included in all copies or substantial portions of the Software.
15+
16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
apps/mobile/licenses/expo-devcert.txt+21−0
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) Dave Wasmer
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/mobile/licenses/expo-xcpretty.txt+29−0
@@ -0,0 +1,29 @@
1+BSD 3-Clause License
2+
3+Copyright (c) 2016-present, 650 Industries, Inc. (Expo)
4+All rights reserved.
5+
6+Redistribution and use in source and binary forms, with or without
7+modification, are permitted provided that the following conditions are met:
8+
9+1. Redistributions of source code must retain the above copyright notice, this
10+ list of conditions and the following disclaimer.
11+
12+2. Redistributions in binary form must reproduce the above copyright notice,
13+ this list of conditions and the following disclaimer in the documentation
14+ and/or other materials provided with the distribution.
15+
16+3. Neither the name of the copyright holder nor the names of its
17+ contributors may be used to endorse or promote products derived from
18+ this software without specific prior written permission.
19+
20+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
apps/mobile/licenses/fb-dotslash.txt+21−0
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) Meta Platforms, Inc. and affiliates.
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/mobile/licenses/ghostty-kit.txt+32−0
@@ -0,0 +1,32 @@
1+GhosttyKit
2+
3+The iOS terminal renderer vendors GhosttyKit.xcframework, a libghostty build produced from T3's
4+iOS 16 support fork. That fork was created from VVTerm's custom-I/O Ghostty fork.
5+
6+Upstream project: https://github.com/ghostty-org/ghostty
7+Custom-I/O base fork: https://github.com/wiedymi/ghostty/tree/custom-io
8+Vendored source fork: https://github.com/Yash-Singh1/ghostty/tree/custom-io
9+Vendored revision: d36c3b8dffd0d756dd5e5f4933962f774a0e6753
10+Reference integration: https://github.com/vivy-company/vvterm
11+
12+MIT License
13+
14+Copyright (c) 2024 Mitchell Hashimoto, Ghostty contributors
15+
16+Permission is hereby granted, free of charge, to any person obtaining a copy
17+of this software and associated documentation files (the "Software"), to deal
18+in the Software without restriction, including without limitation the rights
19+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20+copies of the Software, and to permit persons to whom the Software is
21+furnished to do so, subject to the following conditions:
22+
23+The above copyright notice and this permission notice shall be included in all
24+copies or substantial portions of the Software.
25+
26+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32+SOFTWARE.
apps/mobile/licenses/jimp-compact.txt+21−0
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) 2018 Oliver Moran
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/mobile/licenses/meslo-lgs-nf.txt+13−0
@@ -0,0 +1,13 @@
1+Copyright 2009, 2010, 2013 André Berg
2+
3+Licensed under the Apache License, Version 2.0 (the "License");
4+you may not use this file except in compliance with the License.
5+You may obtain a copy of the License at
6+
7+http://www.apache.org/licenses/LICENSE-2.0
8+
9+Unless required by applicable law or agreed to in writing, software
10+distributed under the License is distributed on an "AS IS" BASIS,
11+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+See the License for the specific language governing permissions and
13+limitations under the License.
apps/mobile/licenses/metro.txt+21−0
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) Meta Platforms, Inc. and affiliates.
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/mobile/licenses/react-native-ai.txt+22−0
@@ -0,0 +1,22 @@
1+MIT License
2+
3+Copyright (c) 2024-2025 Szymon Rybczak
4+Copyright (c) 2025-present Callstack
5+
6+Permission is hereby granted, free of charge, to any person obtaining a copy
7+of this software and associated documentation files (the "Software"), to deal
8+in the Software without restriction, including without limitation the rights
9+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+copies of the Software, and to permit persons to whom the Software is
11+furnished to do so, subject to the following conditions:
12+
13+The above copyright notice and this permission notice shall be included in all
14+copies or substantial portions of the Software.
15+
16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+SOFTWARE.
apps/mobile/licenses/react-native-nitro-modules.txt+21−0
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) 2024 Marc Rousavy
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/mobile/licenses/react-remove-scroll-bar.txt+21−0
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) Anton Korzunov <thekashey@gmail.com>
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/mobile/licenses/standard-navigation.txt+21−0
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) 2026 React Navigation Contributors
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/mobile/licenses/stream-buffers.txt+24−0
@@ -0,0 +1,24 @@
1+This is free and unencumbered software released into the public domain.
2+
3+Anyone is free to copy, modify, publish, use, compile, sell, or
4+distribute this software, either in source code form or as a compiled
5+binary, for any purpose, commercial or non-commercial, and by any
6+means.
7+
8+In jurisdictions that recognize copyright laws, the author or authors
9+of this software dedicate any and all copyright interest in the
10+software to the public domain. We make this dedication for the benefit
11+of the public at large and to the detriment of our heirs and
12+successors. We intend this dedication to be an overt act of
13+relinquishment in perpetuity of all present and future rights to this
14+software under copyright law.
15+
16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+OTHER DEALINGS IN THE SOFTWARE.
23+
24+For more information, please refer to <http://unlicense.org/>
apps/mobile/licenses/structured-headers.txt+21−0
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) 2018 Evert Pot
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/mobile/metro.config.js+61−5
@@ -1,5 +1,7 @@
11 const fs = require("node:fs");
22 const path = require("node:path");
3+const { pathToFileURL } = require("node:url");
4+const TypeScript = require("typescript");
35 const { getDefaultConfig } = require("expo/metro-config");
46 const { withUniwindConfig } = require("uniwind/metro");
57 const extraThemes = require("./generated-uniwind-theme-names.json");
@@ -7,6 +9,13 @@ const extraThemes = require("./generated-uniwind-theme-names.json"); · 1 unmodified line
79 /** @type {import("expo/metro-config").MetroConfig} */
810 const config = getDefaultConfig(__dirname);
911 const workspaceRoot = path.resolve(__dirname, "../..");
12+const generatedLicenseModuleRoot = path.join(__dirname, ".generated", "third-party-licenses");
13+const licenseGeneratorSource = path.join(
14+ workspaceRoot,
15+ "scripts",
16+ "lib",
17+ "third-party-licenses.ts",
18+);
1019 const escapedWorkspaceRoot = workspaceRoot.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1120 const mobileShikiRoot = path.dirname(require.resolve("shiki/package.json", { paths: [__dirname] }));
1221 const resolveShikiDependencyRoot = (packageName) => {
@@ -38,6 +47,7 @@ config.resolver = { · 25 unmodified lines
3847 extraNodeModules: {
3948 // oxlint-disable-next-line unicorn/no-useless-fallback-in-spread
4049 ...(config.resolver?.extraNodeModules ?? {}),
50+ "@t3tools/mobile-third-party-licenses": generatedLicenseModuleRoot,
4151 shiki: mobileShikiRoot,
4252 "@shikijs/core": resolveShikiDependencyRoot("@shikijs/core"),
4353 "@shikijs/engine-javascript": resolveShikiDependencyRoot("@shikijs/engine-javascript"),
@@ -49,8 +59,54 @@ config.resolver = { · 5 unmodified lines
4959 },
5060 };
5161
52−module.exports = withUniwindConfig(config, {
53− cssEntryFile: "./global.css",
54− extraThemes,
55− polyfills: { rem: 14 },
56−});
62+async function writeFileIfChanged(filePath, contents) {
63+ try {
64+ if ((await fs.promises.readFile(filePath, "utf8")) === contents) return;
65+ } catch (error) {
66+ if (error?.code !== "ENOENT") throw error;
67+ }
68+ await fs.promises.writeFile(filePath, contents, "utf8");
69+}
70+
71+async function generateMobileThirdPartyLicenses() {
72+ await fs.promises.mkdir(generatedLicenseModuleRoot, { recursive: true });
73+ const generatorSource = await fs.promises.readFile(licenseGeneratorSource, "utf8");
74+ const compiledGenerator = TypeScript.transpileModule(generatorSource, {
75+ compilerOptions: {
76+ module: TypeScript.ModuleKind.ESNext,
77+ target: TypeScript.ScriptTarget.ES2022,
78+ verbatimModuleSyntax: true,
79+ },
80+ fileName: licenseGeneratorSource,
81+ }).outputText;
82+ const compiledGeneratorPath = path.join(generatedLicenseModuleRoot, "generator.mjs");
83+ await writeFileIfChanged(compiledGeneratorPath, compiledGenerator);
84+
85+ const generatorVersion = (await fs.promises.stat(compiledGeneratorPath)).mtimeMs;
86+ const { generateThirdPartyLicenseManifest } = await import(
87+ `${pathToFileURL(compiledGeneratorPath).href}?version=${String(generatorVersion)}`
88+ );
89+ const manifest = await generateThirdPartyLicenseManifest({
90+ configFile: path.join(workspaceRoot, "third-party-licenses.config.json"),
91+ packageManifests: [{ bundle: "mobile", path: path.join(__dirname, "package.json") }],
92+ });
93+
94+ await Promise.all([
95+ writeFileIfChanged(
96+ path.join(generatedLicenseModuleRoot, "index.js"),
97+ `module.exports = ${JSON.stringify(manifest)};\n`,
98+ ),
99+ writeFileIfChanged(
100+ path.join(generatedLicenseModuleRoot, "package.json"),
101+ '{"main":"index.js"}\n',
102+ ),
103+ ]);
104+}
105+
106+module.exports = generateMobileThirdPartyLicenses().then(() =>
107+ withUniwindConfig(config, {
108+ cssEntryFile: "./global.css",
109+ extraThemes,
110+ polyfills: { rem: 14 },
111+ }),
112+);
apps/mobile/src/Stack.tsx+18−0
@@ -55,6 +55,10 @@ import { SettingsClientStorageRouteScreen } from "./features/settings/SettingsCl · 54 unmodified lines
5555 import { SettingsAuthRouteScreen } from "./features/settings/SettingsAuthRouteScreen";
5656 import { SettingsEnvironmentsRouteScreen } from "./features/settings/SettingsEnvironmentsRouteScreen";
5757 import { SettingsLegalRouteScreen } from "./features/settings/SettingsLegalRouteScreen";
58+import {
59+ SettingsOpenSourceLicenseRouteScreen,
60+ SettingsOpenSourceLicensesRouteScreen,
61+} from "./features/settings/SettingsOpenSourceLicensesRouteScreen";
5862 import { SettingsProjectGroupingRouteScreen } from "./features/settings/SettingsProjectGroupingRouteScreen";
5963 import { UsageRouteScreen } from "./features/usage/UsageRouteScreen";
6064 import { SettingsRouteScreen } from "./features/settings/SettingsRouteScreen";
@@ -191,6 +195,20 @@ const SettingsContentStack = createNativeStackNavigator({ · 130 unmodified lines
191195 title: "Client Storage",
192196 },
193197 }),
198+ SettingsOpenSourceLicenses: createNativeStackScreen({
199+ screen: SettingsOpenSourceLicensesRouteScreen,
200+ linking: "open-source-licenses",
201+ options: {
202+ title: "Open source licenses",
203+ },
204+ }),
205+ SettingsOpenSourceLicense: createNativeStackScreen({
206+ screen: SettingsOpenSourceLicenseRouteScreen,
207+ linking: "open-source-licenses/:entryKey",
208+ options: {
209+ title: "License notice",
210+ },
211+ }),
194212 SettingsUsage: createNativeStackScreen({
195213 screen: UsageRouteScreen,
196214 linking: "usage",
apps/mobile/src/features/settings/SettingsOpenSourceLicensesRouteScreen.tsx+212−0
@@ -0,0 +1,212 @@
1+import { LegendList } from "@legendapp/list/react-native";
2+import { type StaticScreenProps, useNavigation } from "@react-navigation/native";
3+import {
4+ filterThirdPartyLicenseEntries,
5+ findThirdPartyLicenseEntry,
6+ formatLicenseBundles,
7+ thirdPartyLicenseEntryKey,
8+ type ThirdPartyLicenseEntry,
9+} from "@t3tools/shared/thirdPartyLicenses";
10+import { useCallback, useMemo, useState } from "react";
11+import { Linking, Platform, Pressable, ScrollView, View } from "react-native";
12+import { useSafeAreaInsets } from "react-native-safe-area-context";
13+
14+import { AndroidScreenHeader } from "../../components/AndroidScreenHeader";
15+import { SymbolView } from "../../components/AppSymbol";
16+import { AppText as Text, AppTextInput as TextInput } from "../../components/AppText";
17+import { NativeStackScreenOptions } from "../../native/StackHeader";
18+import { MOBILE_THIRD_PARTY_LICENSES } from "./mobileThirdPartyLicenses";
19+
20+function LicenseRow(props: {
21+ readonly entry: ThirdPartyLicenseEntry;
22+ readonly onPress: () => void;
23+}) {
24+ return (
25+ <Pressable
26+ accessibilityHint="Opens the complete license notice"
27+ accessibilityLabel={`${props.entry.name}, ${props.entry.license}`}
28+ accessibilityRole="button"
29+ onPress={props.onPress}
30+ className="border-b border-border bg-card px-5 py-4 active:bg-card-alt"
31+ >
32+ <View className="flex-row items-start gap-3">
33+ <View className="min-w-0 flex-1 gap-1">
34+ <Text className="text-base font-t3-medium text-foreground" numberOfLines={2}>
35+ {props.entry.name}
36+ </Text>
37+ <Text className="text-sm text-foreground-muted" numberOfLines={2}>
38+ {props.entry.version ? `${props.entry.version} · ` : ""}
39+ {props.entry.license}
40+ </Text>
41+ </View>
42+ <SymbolView
43+ name="chevron.right"
44+ size={16}
45+ tintColorClassName={"accent-chevron"}
46+ type="monochrome"
47+ weight="semibold"
48+ />
49+ </View>
50+ </Pressable>
51+ );
52+}
53+
54+export function SettingsOpenSourceLicensesRouteScreen() {
55+ const navigation = useNavigation();
56+ const insets = useSafeAreaInsets();
57+ const [query, setQuery] = useState("");
58+ const entries = MOBILE_THIRD_PARTY_LICENSES.entries;
59+ const filteredEntries = useMemo(
60+ () => filterThirdPartyLicenseEntries(entries, query),
61+ [entries, query],
62+ );
63+ const renderItem = useCallback(
64+ ({ item }: { readonly item: ThirdPartyLicenseEntry }) => (
65+ <LicenseRow
66+ entry={item}
67+ onPress={() =>
68+ navigation.navigate("SettingsSheet", {
69+ screen: "SettingsContent",
70+ params: {
71+ screen: "SettingsOpenSourceLicense",
72+ params: { entryKey: thirdPartyLicenseEntryKey(item) },
73+ },
74+ })
75+ }
76+ />
77+ ),
78+ [navigation],
79+ );
80+
81+ return (
82+ <View collapsable={false} className="flex-1 bg-sheet">
83+ {Platform.OS === "android" ? (
84+ <>
85+ <NativeStackScreenOptions options={{ headerShown: false }} />
86+ <AndroidScreenHeader title="Open source licenses" onBack={() => navigation.goBack()} />
87+ </>
88+ ) : null}
89+ <LegendList
90+ className="flex-1"
91+ contentContainerStyle={{ paddingBottom: Math.max(insets.bottom, 18) + 18 }}
92+ contentInsetAdjustmentBehavior="automatic"
93+ data={filteredEntries}
94+ estimatedItemSize={78}
95+ keyboardDismissMode="on-drag"
96+ keyboardShouldPersistTaps="handled"
97+ keyExtractor={thirdPartyLicenseEntryKey}
98+ ListEmptyComponent={
99+ <View className="items-center px-6 py-12">
100+ <Text className="text-center text-base text-foreground-muted">
101+ No licenses match that search.
102+ </Text>
103+ </View>
104+ }
105+ ListHeaderComponent={
106+ <View className="gap-4 px-5 pt-4 pb-5">
107+ <Text className="text-base leading-normal text-foreground-muted">
108+ Third-party notices for dependencies and assets included in T3 Code Mobile.
109+ </Text>
110+ <TextInput
111+ accessibilityLabel="Search open-source licenses"
112+ autoCapitalize="none"
113+ autoCorrect={false}
114+ clearButtonMode="while-editing"
115+ onChangeText={setQuery}
116+ placeholder="Search packages"
117+ returnKeyType="search"
118+ value={query}
119+ />
120+ <Text className="tabular-nums text-sm text-foreground-muted">
121+ {filteredEntries.length === entries.length
122+ ? `${String(entries.length)} notices`
123+ : `${String(filteredEntries.length)} of ${String(entries.length)} notices`}
124+ </Text>
125+ </View>
126+ }
127+ renderItem={renderItem}
128+ showsVerticalScrollIndicator={false}
129+ />
130+ </View>
131+ );
132+}
133+
134+type LicenseDetailProps = StaticScreenProps<{ readonly entryKey: string }>;
135+
136+export function SettingsOpenSourceLicenseRouteScreen({ route }: LicenseDetailProps) {
137+ const navigation = useNavigation();
138+ const insets = useSafeAreaInsets();
139+ const entry = findThirdPartyLicenseEntry(
140+ MOBILE_THIRD_PARTY_LICENSES.entries,
141+ route.params.entryKey,
142+ );
143+ const sourceUrl = entry?.sourceUrl?.match(/^https?:\/\//) ? entry.sourceUrl : null;
144+
145+ if (!entry) {
146+ return (
147+ <View collapsable={false} className="flex-1 bg-sheet">
148+ {Platform.OS === "android" ? (
149+ <>
150+ <NativeStackScreenOptions options={{ headerShown: false }} />
151+ <AndroidScreenHeader title="License notice" onBack={() => navigation.goBack()} />
152+ </>
153+ ) : null}
154+ <View className="flex-1 items-center justify-center px-6">
155+ <Text className="text-center text-base text-foreground-muted">
156+ This license notice is unavailable.
157+ </Text>
158+ </View>
159+ </View>
160+ );
161+ }
162+
163+ return (
164+ <View collapsable={false} className="flex-1 bg-sheet">
165+ {Platform.OS === "android" ? (
166+ <>
167+ <NativeStackScreenOptions options={{ headerShown: false }} />
168+ <AndroidScreenHeader title="License notice" onBack={() => navigation.goBack()} />
169+ </>
170+ ) : null}
171+ <ScrollView
172+ className="flex-1"
173+ contentInsetAdjustmentBehavior="automatic"
174+ contentContainerClassName="gap-5 px-5 pt-4"
175+ contentContainerStyle={{ paddingBottom: Math.max(insets.bottom, 18) + 18 }}
176+ showsVerticalScrollIndicator={false}
177+ >
178+ <View className="gap-2 px-1">
179+ <Text className="text-2xl font-t3-bold text-foreground">{entry.name}</Text>
180+ <Text className="text-base leading-normal text-foreground-muted">
181+ {[entry.version, entry.license, formatLicenseBundles(entry.bundles)]
182+ .filter((value): value is string => Boolean(value))
183+ .join(" · ")}
184+ </Text>
185+ {sourceUrl ? (
186+ <Pressable
187+ accessibilityHint="Opens the project website"
188+ accessibilityRole="link"
189+ onPress={() => void Linking.openURL(sourceUrl)}
190+ className="min-h-12 flex-row items-center gap-2 self-start py-2 active:opacity-60"
191+ >
192+ <Text className="font-t3-medium text-primary">Project source</Text>
193+ <SymbolView
194+ name="arrow.up.right"
195+ size={16}
196+ tintColorClassName={"accent-primary"}
197+ type="monochrome"
198+ weight="semibold"
199+ />
200+ </Pressable>
201+ ) : null}
202+ </View>
203+
204+ <View className="overflow-hidden rounded-[24px] border-continuous bg-card p-4">
205+ <Text selectable className="font-mono text-base leading-normal text-foreground">
206+ {entry.noticeText}
207+ </Text>
208+ </View>
209+ </ScrollView>
210+ </View>
211+ );
212+}
apps/mobile/src/features/settings/SettingsRouteScreen.tsx+5−0
@@ -670,6 +670,11 @@ function AppSettingsSection() { · 669 unmodified lines
670670 return (
671671 <SettingsSection title="App">
672672 <SettingsRow icon="internaldrive" label="Client Storage" target="SettingsClientStorage" />
673+ <SettingsRow
674+ icon="doc.on.doc"
675+ label="Open source licenses"
676+ target="SettingsOpenSourceLicenses"
677+ />
673678 <SettingsRow icon="doc.text" label="Legal" fullScreenTarget="SettingsLegal" />
674679 {updateCheckAvailable ? (
675680 <Pressable
apps/mobile/src/features/settings/components/settings-sheet-targets.ts+1−0
@@ -4,6 +4,7 @@ export type SettingsSheetTarget = · 3 unmodified lines
44 | "SettingsAppearance"
55 | "SettingsProjectGrouping"
66 | "SettingsClientStorage"
7+ | "SettingsOpenSourceLicenses"
78 | "SettingsUsage";
89
910 export type SettingsLegalDocumentTarget = "SettingsLegal";
apps/mobile/src/features/settings/mobileThirdPartyLicenses.ts+4−0
@@ -0,0 +1,4 @@
1+import generatedManifest from "@t3tools/mobile-third-party-licenses";
2+import { decodeThirdPartyLicenseManifest } from "@t3tools/shared/thirdPartyLicenses";
3+
4+export const MOBILE_THIRD_PARTY_LICENSES = decodeThirdPartyLicenseManifest(generatedManifest);
apps/mobile/src/types/mobile-third-party-licenses.d.ts+4−0
@@ -0,0 +1,4 @@
1+declare module "@t3tools/mobile-third-party-licenses" {
2+ const manifest: unknown;
3+ export default manifest;
4+}
apps/web/THIRD_PARTY_NOTICES.md+20−7
@@ -1,11 +1,24 @@
1−# Third-Party Notices
1+vscode-icons
22
3−## vscode-icons
4−
5−The custom file icon symbols in `src/pierre-icons.ts` are adapted from the
6−[`vscode-icons`](https://github.com/vscode-icons/vscode-icons) project.
3+The custom file icon symbols bundled with T3 Code are adapted from the
4+vscode-icons project: https://github.com/vscode-icons/vscode-icons
75
86 Copyright (c) 2016 Roberto Huertas
97
10−Licensed under the MIT License. The full license text is available in the
11−upstream repository: <https://github.com/vscode-icons/vscode-icons/blob/master/LICENSE>.
8+Permission is hereby granted, free of charge, to any person obtaining a copy
9+of this software and associated documentation files (the "Software"), to deal
10+in the Software without restriction, including without limitation the rights
11+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+copies of the Software, and to permit persons to whom the Software is
13+furnished to do so, subject to the following conditions:
14+
15+The above copyright notice and this permission notice shall be included in all
16+copies or substantial portions of the Software.
17+
18+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+SOFTWARE.
apps/web/licenses/electron-internal-extract-zip.txt+25−0
@@ -0,0 +1,25 @@
1+BSD 2-Clause License
2+
3+Copyright (c) 2026 Samuel Attard and Electron contributors
4+All rights reserved.
5+
6+Redistribution and use in source and binary forms, with or without
7+modification, are permitted provided that the following conditions are met:
8+
9+1. Redistributions of source code must retain the above copyright notice,
10+ this list of conditions and the following disclaimer.
11+
12+2. Redistributions in binary form must reproduce the above copyright notice,
13+ this list of conditions and the following disclaimer in the documentation
14+ and/or other materials provided with the distribution.
15+
16+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
apps/web/licenses/glob-to-regexp.txt+24−0
@@ -0,0 +1,24 @@
1+Copyright (c) 2013, Nick Fitzgerald
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without modification,
6+are permitted provided that the following conditions are met:
7+
8+* Redistributions of source code must retain the above copyright notice, this
9+ list of conditions and the following disclaimer.
10+
11+* Redistributions in binary form must reproduce the above copyright notice, this
12+ list of conditions and the following disclaimer in the documentation and/or
13+ other materials provided with the distribution.
14+
15+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
19+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
apps/web/licenses/isarray.txt+21−0
@@ -0,0 +1,21 @@
1+(MIT)
2+
3+Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy of
6+this software and associated documentation files (the "Software"), to deal in
7+the Software without restriction, including without limitation the rights to
8+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9+of the Software, and to permit persons to whom the Software is furnished to do
10+so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/web/licenses/keyv.txt+22−0
@@ -0,0 +1,22 @@
1+MIT License
2+
3+Copyright (c) 2017-2021 Luke Childs
4+Copyright (c) 2021-2022 Jared Wray
5+
6+Permission is hereby granted, free of charge, to any person obtaining a copy
7+of this software and associated documentation files (the "Software"), to deal
8+in the Software without restriction, including without limitation the rights
9+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+copies of the Software, and to permit persons to whom the Software is
11+furnished to do so, subject to the following conditions:
12+
13+The above copyright notice and this permission notice shall be included in all
14+copies or substantial portions of the Software.
15+
16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+SOFTWARE.
apps/web/licenses/kubernetes-types.txt+176−0
@@ -0,0 +1,176 @@
1+Apache License
2+ Version 2.0, January 2004
3+ http://www.apache.org/licenses/
4+
5+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6+
7+ 1. Definitions.
8+
9+ "License" shall mean the terms and conditions for use, reproduction,
10+ and distribution as defined by Sections 1 through 9 of this document.
11+
12+ "Licensor" shall mean the copyright owner or entity authorized by
13+ the copyright owner that is granting the License.
14+
15+ "Legal Entity" shall mean the union of the acting entity and all
16+ other entities that control, are controlled by, or are under common
17+ control with that entity. For the purposes of this definition,
18+ "control" means (i) the power, direct or indirect, to cause the
19+ direction or management of such entity, whether by contract or
20+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21+ outstanding shares, or (iii) beneficial ownership of such entity.
22+
23+ "You" (or "Your") shall mean an individual or Legal Entity
24+ exercising permissions granted by this License.
25+
26+ "Source" form shall mean the preferred form for making modifications,
27+ including but not limited to software source code, documentation
28+ source, and configuration files.
29+
30+ "Object" form shall mean any form resulting from mechanical
31+ transformation or translation of a Source form, including but
32+ not limited to compiled object code, generated documentation,
33+ and conversions to other media types.
34+
35+ "Work" shall mean the work of authorship, whether in Source or
36+ Object form, made available under the License, as indicated by a
37+ copyright notice that is included in or attached to the work
38+ (an example is provided in the Appendix below).
39+
40+ "Derivative Works" shall mean any work, whether in Source or Object
41+ form, that is based on (or derived from) the Work and for which the
42+ editorial revisions, annotations, elaborations, or other modifications
43+ represent, as a whole, an original work of authorship. For the purposes
44+ of this License, Derivative Works shall not include works that remain
45+ separable from, or merely link (or bind by name) to the interfaces of,
46+ the Work and Derivative Works thereof.
47+
48+ "Contribution" shall mean any work of authorship, including
49+ the original version of the Work and any modifications or additions
50+ to that Work or Derivative Works thereof, that is intentionally
51+ submitted to Licensor for inclusion in the Work by the copyright owner
52+ or by an individual or Legal Entity authorized to submit on behalf of
53+ the copyright owner. For the purposes of this definition, "submitted"
54+ means any form of electronic, verbal, or written communication sent
55+ to the Licensor or its representatives, including but not limited to
56+ communication on electronic mailing lists, source code control systems,
57+ and issue tracking systems that are managed by, or on behalf of, the
58+ Licensor for the purpose of discussing and improving the Work, but
59+ excluding communication that is conspicuously marked or otherwise
60+ designated in writing by the copyright owner as "Not a Contribution."
61+
62+ "Contributor" shall mean Licensor and any individual or Legal Entity
63+ on behalf of whom a Contribution has been received by Licensor and
64+ subsequently incorporated within the Work.
65+
66+ 2. Grant of Copyright License. Subject to the terms and conditions of
67+ this License, each Contributor hereby grants to You a perpetual,
68+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69+ copyright license to reproduce, prepare Derivative Works of,
70+ publicly display, publicly perform, sublicense, and distribute the
71+ Work and such Derivative Works in Source or Object form.
72+
73+ 3. Grant of Patent License. Subject to the terms and conditions of
74+ this License, each Contributor hereby grants to You a perpetual,
75+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76+ (except as stated in this section) patent license to make, have made,
77+ use, offer to sell, sell, import, and otherwise transfer the Work,
78+ where such license applies only to those patent claims licensable
79+ by such Contributor that are necessarily infringed by their
80+ Contribution(s) alone or by combination of their Contribution(s)
81+ with the Work to which such Contribution(s) was submitted. If You
82+ institute patent litigation against any entity (including a
83+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84+ or a Contribution incorporated within the Work constitutes direct
85+ or contributory patent infringement, then any patent licenses
86+ granted to You under this License for that Work shall terminate
87+ as of the date such litigation is filed.
88+
89+ 4. Redistribution. You may reproduce and distribute copies of the
90+ Work or Derivative Works thereof in any medium, with or without
91+ modifications, and in Source or Object form, provided that You
92+ meet the following conditions:
93+
94+ (a) You must give any other recipients of the Work or
95+ Derivative Works a copy of this License; and
96+
97+ (b) You must cause any modified files to carry prominent notices
98+ stating that You changed the files; and
99+
100+ (c) You must retain, in the Source form of any Derivative Works
101+ that You distribute, all copyright, patent, trademark, and
102+ attribution notices from the Source form of the Work,
103+ excluding those notices that do not pertain to any part of
104+ the Derivative Works; and
105+
106+ (d) If the Work includes a "NOTICE" text file as part of its
107+ distribution, then any Derivative Works that You distribute must
108+ include a readable copy of the attribution notices contained
109+ within such NOTICE file, excluding those notices that do not
110+ pertain to any part of the Derivative Works, in at least one
111+ of the following places: within a NOTICE text file distributed
112+ as part of the Derivative Works; within the Source form or
113+ documentation, if provided along with the Derivative Works; or,
114+ within a display generated by the Derivative Works, if and
115+ wherever such third-party notices normally appear. The contents
116+ of the NOTICE file are for informational purposes only and
117+ do not modify the License. You may add Your own attribution
118+ notices within Derivative Works that You distribute, alongside
119+ or as an addendum to the NOTICE text from the Work, provided
120+ that such additional attribution notices cannot be construed
121+ as modifying the License.
122+
123+ You may add Your own copyright statement to Your modifications and
124+ may provide additional or different license terms and conditions
125+ for use, reproduction, or distribution of Your modifications, or
126+ for any such Derivative Works as a whole, provided Your use,
127+ reproduction, and distribution of the Work otherwise complies with
128+ the conditions stated in this License.
129+
130+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131+ any Contribution intentionally submitted for inclusion in the Work
132+ by You to the Licensor shall be under the terms and conditions of
133+ this License, without any additional terms or conditions.
134+ Notwithstanding the above, nothing herein shall supersede or modify
135+ the terms of any separate license agreement you may have executed
136+ with Licensor regarding such Contributions.
137+
138+ 6. Trademarks. This License does not grant permission to use the trade
139+ names, trademarks, service marks, or product names of the Licensor,
140+ except as required for reasonable and customary use in describing the
141+ origin of the Work and reproducing the content of the NOTICE file.
142+
143+ 7. Disclaimer of Warranty. Unless required by applicable law or
144+ agreed to in writing, Licensor provides the Work (and each
145+ Contributor provides its Contributions) on an "AS IS" BASIS,
146+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147+ implied, including, without limitation, any warranties or conditions
148+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149+ PARTICULAR PURPOSE. You are solely responsible for determining the
150+ appropriateness of using or redistributing the Work and assume any
151+ risks associated with Your exercise of permissions under this License.
152+
153+ 8. Limitation of Liability. In no event and under no legal theory,
154+ whether in tort (including negligence), contract, or otherwise,
155+ unless required by applicable law (such as deliberate and grossly
156+ negligent acts) or agreed to in writing, shall any Contributor be
157+ liable to You for damages, including any direct, indirect, special,
158+ incidental, or consequential damages of any character arising as a
159+ result of this License or out of the use or inability to use the
160+ Work (including but not limited to damages for loss of goodwill,
161+ work stoppage, computer failure or malfunction, or any and all
162+ other commercial damages or losses), even if such Contributor
163+ has been advised of the possibility of such damages.
164+
165+ 9. Accepting Warranty or Additional Liability. While redistributing
166+ the Work or Derivative Works thereof, You may choose to offer,
167+ and charge a fee for, acceptance of support, warranty, indemnity,
168+ or other liability obligations and/or rights consistent with this
169+ License. However, in accepting such obligations, You may act only
170+ on Your own behalf and on Your sole responsibility, not on behalf
171+ of any other Contributor, and only if You agree to indemnify,
172+ defend, and hold each Contributor harmless for any liability
173+ incurred by, or claims asserted against, such Contributor by reason
174+ of your accepting any such warranty or additional liability.
175+
176+ END OF TERMS AND CONDITIONS
apps/web/licenses/lazy-val.txt+21−0
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) Vladimir Krivosheev
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/web/licenses/lru-map.txt+19−0
@@ -0,0 +1,19 @@
1+Copyright (c) 2010-2016 Rasmus Andersson <https://rsms.me/>
2+
3+Permission is hereby granted, free of charge, to any person obtaining a copy
4+of this software and associated documentation files (the "Software"), to deal
5+in the Software without restriction, including without limitation the rights
6+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+copies of the Software, and to permit persons to whom the Software is
8+furnished to do so, subject to the following conditions:
9+
10+The above copyright notice and this permission notice shall be included in
11+all copies or substantial portions of the Software.
12+
13+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+THE SOFTWARE.
apps/web/licenses/msgpackr-extract-linux-x64.txt+21−0
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) 2020 Kris Zyp
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/web/licenses/pierre-theming.txt+189−0
@@ -0,0 +1,189 @@
1+ Apache License
2+ Version 2.0, January 2004
3+ http://www.apache.org/licenses/
4+
5+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6+
7+1. Definitions.
8+
9+ "License" shall mean the terms and conditions for use, reproduction, and
10+ distribution as defined by Sections 1 through 9 of this document.
11+
12+ "Licensor" shall mean the copyright owner or entity authorized by the
13+ copyright owner that is granting the License.
14+
15+ "Legal Entity" shall mean the union of the acting entity and all other
16+ entities that control, are controlled by, or are under common control with
17+ that entity. For the purposes of this definition, "control" means (i) the
18+ power, direct or indirect, to cause the direction or management of such
19+ entity, whether by contract or otherwise, or (ii) ownership of fifty percent
20+ (50%) or more of the outstanding shares, or (iii) beneficial ownership of
21+ such entity.
22+
23+ "You" (or "Your") shall mean an individual or Legal Entity exercising
24+ permissions granted by this License.
25+
26+ "Source" form shall mean the preferred form for making modifications,
27+ including but not limited to software source code, documentation source, and
28+ configuration files.
29+
30+ "Object" form shall mean any form resulting from mechanical transformation
31+ or translation of a Source form, including but not limited to compiled
32+ object code, generated documentation, and conversions to other media types.
33+
34+ "Work" shall mean the work of authorship, whether in Source or Object form,
35+ made available under the License, as indicated by a copyright notice that is
36+ included in or attached to the work (an example is provided in the Appendix
37+ below).
38+
39+ "Derivative Works" shall mean any work, whether in Source or Object form,
40+ that is based on (or derived from) the Work and for which the editorial
41+ revisions, annotations, elaborations, or other modifications represent, as a
42+ whole, an original work of authorship. For the purposes of this License,
43+ Derivative Works shall not include works that remain separable from, or
44+ merely link (or bind by name) to the interfaces of, the Work and Derivative
45+ Works thereof.
46+
47+ "Contribution" shall mean any work of authorship, including the original
48+ version of the Work and any modifications or additions to that Work or
49+ Derivative Works thereof, that is intentionally submitted to Licensor for
50+ inclusion in the Work by the copyright owner or by an individual or Legal
51+ Entity authorized to submit on behalf of the copyright owner. For the
52+ purposes of this definition, "submitted" means any form of electronic,
53+ verbal, or written communication sent to the Licensor or its
54+ representatives, including but not limited to communication on electronic
55+ mailing lists, source code control systems, and issue tracking systems that
56+ are managed by, or on behalf of, the Licensor for the purpose of discussing
57+ and improving the Work, but excluding communication that is conspicuously
58+ marked or otherwise designated in writing by the copyright owner as "Not a
59+ Contribution."
60+
61+ "Contributor" shall mean Licensor and any individual or Legal Entity on
62+ behalf of whom a Contribution has been received by Licensor and subsequently
63+ incorporated within the Work.
64+
65+2. Grant of Copyright License. Subject to the terms and conditions of this
66+ License, each Contributor hereby grants to You a perpetual, worldwide,
67+ non-exclusive, no-charge, royalty-free, irrevocable copyright license to
68+ reproduce, prepare Derivative Works of, publicly display, publicly perform,
69+ sublicense, and distribute the Work and such Derivative Works in Source or
70+ Object form.
71+
72+3. Grant of Patent License. Subject to the terms and conditions of this
73+ License, each Contributor hereby grants to You a perpetual, worldwide,
74+ non-exclusive, no-charge, royalty-free, irrevocable (except as stated in
75+ this section) patent license to make, have made, use, offer to sell, sell,
76+ import, and otherwise transfer the Work, where such license applies only to
77+ those patent claims licensable by such Contributor that are necessarily
78+ infringed by their Contribution(s) alone or by combination of their
79+ Contribution(s) with the Work to which such Contribution(s) was submitted.
80+ If You institute patent litigation against any entity (including a
81+ cross-claim or counterclaim in a lawsuit) alleging that the Work or a
82+ Contribution incorporated within the Work constitutes direct or contributory
83+ patent infringement, then any patent licenses granted to You under this
84+ License for that Work shall terminate as of the date such litigation is
85+ filed.
86+
87+4. Redistribution. You may reproduce and distribute copies of the Work or
88+ Derivative Works thereof in any medium, with or without modifications, and
89+ in Source or Object form, provided that You meet the following conditions:
90+
91+ (a) You must give any other recipients of the Work or Derivative Works a
92+ copy of this License; and
93+
94+ (b) You must cause any modified files to carry prominent notices stating
95+ that You changed the files; and
96+
97+ (c) You must retain, in the Source form of any Derivative Works that You
98+ distribute, all copyright, patent, trademark, and attribution notices from
99+ the Source form of the Work, excluding those notices that do not pertain to
100+ any part of the Derivative Works; and
101+
102+ (d) If the Work includes a "NOTICE" text file as part of its distribution,
103+ then any Derivative Works that You distribute must include a readable copy
104+ of the attribution notices contained within such NOTICE file, excluding
105+ those notices that do not pertain to any part of the Derivative Works, in at
106+ least one of the following places: within a NOTICE text file distributed as
107+ part of the Derivative Works; within the Source form or documentation, if
108+ provided along with the Derivative Works; or, within a display generated by
109+ the Derivative Works, if and wherever such third-party notices normally
110+ appear. The contents of the NOTICE file are for informational purposes only
111+ and do not modify the License. You may add Your own attribution notices
112+ within Derivative Works that You distribute, alongside or as an addendum to
113+ the NOTICE text from the Work, provided that such additional attribution
114+ notices cannot be construed as modifying the License.
115+
116+ You may add Your own copyright statement to Your modifications and may
117+ provide additional or different license terms and conditions for use,
118+ reproduction, or distribution of Your modifications, or for any such
119+ Derivative Works as a whole, provided Your use, reproduction, and
120+ distribution of the Work otherwise complies with the conditions stated in
121+ this License.
122+
123+5. Submission of Contributions. Unless You explicitly state otherwise, any
124+ Contribution intentionally submitted for inclusion in the Work by You to the
125+ Licensor shall be under the terms and conditions of this License, without
126+ any additional terms or conditions. Notwithstanding the above, nothing
127+ herein shall supersede or modify the terms of any separate license agreement
128+ you may have executed with Licensor regarding such Contributions.
129+
130+6. Trademarks. This License does not grant permission to use the trade names,
131+ trademarks, service marks, or product names of the Licensor, except as
132+ required for reasonable and customary use in describing the origin of the
133+ Work and reproducing the content of the NOTICE file.
134+
135+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
136+ writing, Licensor provides the Work (and each Contributor provides its
137+ Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
138+ KIND, either express or implied, including, without limitation, any
139+ warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or
140+ FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining
141+ the appropriateness of using or redistributing the Work and assume any risks
142+ associated with Your exercise of permissions under this License.
143+
144+8. Limitation of Liability. In no event and under no legal theory, whether in
145+ tort (including negligence), contract, or otherwise, unless required by
146+ applicable law (such as deliberate and grossly negligent acts) or agreed to
147+ in writing, shall any Contributor be liable to You for damages, including
148+ any direct, indirect, special, incidental, or consequential damages of any
149+ character arising as a result of this License or out of the use or inability
150+ to use the Work (including but not limited to damages for loss of goodwill,
151+ work stoppage, computer failure or malfunction, or any and all other
152+ commercial damages or losses), even if such Contributor has been advised of
153+ the possibility of such damages.
154+
155+9. Accepting Warranty or Additional Liability. While redistributing the Work or
156+ Derivative Works thereof, You may choose to offer, and charge a fee for,
157+ acceptance of support, warranty, indemnity, or other liability obligations
158+ and/or rights consistent with this License. However, in accepting such
159+ obligations, You may act only on Your own behalf and on Your sole
160+ responsibility, not on behalf of any other Contributor, and only if You
161+ agree to indemnify, defend, and hold each Contributor harmless for any
162+ liability incurred by, or claims asserted against, such Contributor by
163+ reason of your accepting any such warranty or additional liability.
164+
165+END OF TERMS AND CONDITIONS
166+
167+APPENDIX: How to apply the Apache License to your work.
168+
169+ To apply the Apache License to your work, attach the following
170+ boilerplate notice, with the fields enclosed by brackets "[]"
171+ replaced with your own identifying information. (Don't include
172+ the brackets!) The text should be enclosed in the appropriate
173+ comment syntax for the file format. We also recommend that a
174+ file or class name and description of purpose be included on the
175+ same "printed page" as the copyright notice for easier
176+ identification within third-party archives.
177+
178+Copyright 2025 Pierre Computer Company
179+
180+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
181+this file except in compliance with the License. You may obtain a copy of the
182+License at
183+
184+ http://www.apache.org/licenses/LICENSE-2.0
185+
186+Unless required by applicable law or agreed to in writing, software distributed
187+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
188+CONDITIONS OF ANY KIND, either express or implied. See the License for the
189+specific language governing permissions and limitations under the License.
apps/web/licenses/react-grab-cli.txt+21−0
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) 2025 Aiden Bai
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
apps/web/licenses/type-fest.txt+9−0
@@ -0,0 +1,9 @@
1+MIT License
2+
3+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6+
7+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8+
9+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
apps/web/src/components/settings/OpenSourceLicenses.tsx+205−0
@@ -0,0 +1,205 @@
1+import { ChevronRightIcon, ExternalLinkIcon, SearchIcon } from "lucide-react";
2+import { useCallback, useEffect, useMemo, useState } from "react";
3+
4+import {
5+ decodeThirdPartyLicenseManifest,
6+ filterThirdPartyLicenseEntries,
7+ formatLicenseBundles,
8+ type ThirdPartyLicenseEntry,
9+ type ThirdPartyLicenseManifest,
10+} from "@t3tools/shared/thirdPartyLicenses";
11+
12+import { cn } from "../../lib/utils";
13+import { Button } from "../ui/button";
14+import { Input } from "../ui/input";
15+import { SettingsPageContainer } from "./settingsLayout";
16+
17+type LicenseManifestState =
18+ | { readonly status: "loading" }
19+ | { readonly status: "error"; readonly message: string }
20+ | { readonly status: "ready"; readonly manifest: ThirdPartyLicenseManifest };
21+
22+async function loadLicenseManifest(signal: AbortSignal): Promise<ThirdPartyLicenseManifest> {
23+ const response = await fetch(
24+ `${import.meta.env.BASE_URL.replace(/\/$/, "")}/third-party-licenses.json`,
25+ { signal },
26+ );
27+ if (!response.ok) {
28+ throw new Error(`The license manifest request failed with status ${String(response.status)}.`);
29+ }
30+ return decodeThirdPartyLicenseManifest((await response.json()) as unknown);
31+}
32+
33+function LicenseNoticeRow({ entry }: { readonly entry: ThirdPartyLicenseEntry }) {
34+ const [open, setOpen] = useState(false);
35+ const panelId = `license-notice-${encodeURIComponent(`${entry.kind}-${entry.name}-${entry.version ?? "custom"}`)}`;
36+ const versionLabel = entry.version ? ` ${entry.version}` : "";
37+
38+ return (
39+ <article>
40+ <button
41+ type="button"
42+ aria-controls={panelId}
43+ aria-expanded={open}
44+ className="cursor-pointer flex min-h-14 w-full items-start gap-3 px-3 py-3 text-left hover:bg-foreground/4 sm:px-4"
45+ onClick={() => setOpen((value) => !value)}
46+ >
47+ <ChevronRightIcon
48+ aria-hidden
49+ className={cn(
50+ "mt-0.5 size-4 shrink-0 text-muted-foreground transition-transform duration-150",
51+ open && "rotate-90",
52+ )}
53+ />
54+ <span className="min-w-0 flex-1">
55+ <span className="flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-1">
56+ <span className="min-w-0 break-words font-medium text-foreground">{entry.name}</span>
57+ {entry.version ? (
58+ <code className="font-medium text-muted-foreground">{entry.version}</code>
59+ ) : null}
60+ </span>
61+ <span className="text-muted-foreground">
62+ {entry.license} · {formatLicenseBundles(entry.bundles)}
63+ </span>
64+ </span>
65+ </button>
66+ {open ? (
67+ <div
68+ id={panelId}
69+ className="border-t border-foreground/10 bg-foreground/2 px-3 py-4 sm:px-4"
70+ >
71+ <div className="flex flex-col gap-3">
72+ <div className="flex flex-wrap items-center justify-between gap-3">
73+ <h2 className="text-base font-medium text-foreground sm:text-sm">
74+ {entry.name}
75+ {versionLabel}
76+ </h2>
77+ {entry.sourceUrl ? (
78+ <a
79+ className="inline-flex min-h-8 items-center gap-1.5 rounded-md px-2 text-base font-medium text-muted-foreground hover:bg-foreground/5 hover:text-foreground sm:text-sm"
80+ href={entry.sourceUrl}
81+ rel="noreferrer noopener"
82+ target="_blank"
83+ >
84+ Project source
85+ <ExternalLinkIcon aria-hidden className="size-4 shrink-0" />
86+ </a>
87+ ) : null}
88+ </div>
89+ <pre className="max-w-full whitespace-pre-wrap break-words font-mono text-base/7 text-foreground/85 sm:text-sm/6">
90+ {entry.noticeText}
91+ </pre>
92+ </div>
93+ </div>
94+ ) : null}
95+ </article>
96+ );
97+}
98+
99+function LicenseManifestError({ message, onRetry }: { message: string; onRetry: () => void }) {
100+ return (
101+ <div className="flex flex-col items-start gap-3 border-y border-foreground/10 px-3 py-5 sm:px-4">
102+ <div className="flex flex-col gap-1">
103+ <h2 className="text-base font-medium text-foreground sm:text-sm">
104+ Open-source notices are unavailable
105+ </h2>
106+ <p className="max-w-[70ch] text-pretty text-base/7 text-muted-foreground sm:text-sm/6">
107+ {message}
108+ </p>
109+ </div>
110+ <Button type="button" size="xs" variant="outline" onClick={onRetry}>
111+ Try again
112+ </Button>
113+ </div>
114+ );
115+}
116+
117+export function OpenSourceLicensesPanel() {
118+ const [state, setState] = useState<LicenseManifestState>({ status: "loading" });
119+ const [query, setQuery] = useState("");
120+ const [requestVersion, setRequestVersion] = useState(0);
121+
122+ useEffect(() => {
123+ const controller = new AbortController();
124+ setState({ status: "loading" });
125+ void loadLicenseManifest(controller.signal).then(
126+ (manifest) => setState({ status: "ready", manifest }),
127+ (error: unknown) => {
128+ if (controller.signal.aborted) return;
129+ setState({
130+ status: "error",
131+ message: error instanceof Error ? error.message : "The license manifest could not load.",
132+ });
133+ },
134+ );
135+ return () => controller.abort();
136+ }, [requestVersion]);
137+
138+ const entries = state.status === "ready" ? state.manifest.entries : [];
139+ const filteredEntries = useMemo(
140+ () => filterThirdPartyLicenseEntries(entries, query),
141+ [entries, query],
142+ );
143+ const retry = useCallback(() => setRequestVersion((value) => value + 1), []);
144+
145+ return (
146+ <SettingsPageContainer width="wide" className="gap-6">
147+ <header className="flex flex-col gap-2 px-3 sm:px-4">
148+ <h1 className="text-balance text-2xl font-semibold tracking-tight text-foreground">
149+ Open source licenses
150+ </h1>
151+ <p className="max-w-[70ch] text-pretty text-base/7 text-muted-foreground sm:text-sm/6">
152+ Third-party notices for dependencies and assets included in T3 Code.
153+ </p>
154+ </header>
155+
156+ {state.status === "ready" ? (
157+ <div className="flex flex-col gap-3">
158+ <div className="flex flex-col gap-2 px-3 sm:flex-row sm:items-center sm:justify-between sm:px-4">
159+ <label className="relative min-w-0 flex-1 sm:max-w-sm">
160+ <SearchIcon
161+ aria-hidden
162+ className="pointer-events-none absolute top-1/2 left-2.5 z-1 size-4 shrink-0 -translate-y-1/2 text-muted-foreground"
163+ />
164+ <Input
165+ nativeInput
166+ type="search"
167+ value={query}
168+ onChange={(event) => setQuery(event.currentTarget.value)}
169+ placeholder="Search packages"
170+ aria-label="Search open-source licenses"
171+ className="[&_[data-slot=input]]:pl-8"
172+ />
173+ </label>
174+ <p className="text-base text-muted-foreground tabular-nums sm:text-sm">
175+ {filteredEntries.length === entries.length
176+ ? `${String(entries.length)} notices`
177+ : `${String(filteredEntries.length)} of ${String(entries.length)} notices`}
178+ </p>
179+ </div>
180+
181+ <div className="overflow-hidden rounded-xl border border-foreground/10 divide-y divide-foreground/10 text-base sm:text-sm">
182+ {filteredEntries.length > 0 ? (
183+ filteredEntries.map((entry) => (
184+ <LicenseNoticeRow
185+ key={`${entry.kind}:${entry.name}:${entry.version ?? "custom"}`}
186+ entry={entry}
187+ />
188+ ))
189+ ) : (
190+ <p className="px-3 py-8 text-center text-base/7 text-muted-foreground sm:px-4 sm:text-sm/6">
191+ No licenses match that search.
192+ </p>
193+ )}
194+ </div>
195+ </div>
196+ ) : state.status === "error" ? (
197+ <LicenseManifestError message={state.message} onRetry={retry} />
198+ ) : (
199+ <p className="border-y border-foreground/10 px-3 py-5 text-base/7 text-muted-foreground sm:px-4 sm:text-sm/6">
200+ Loading open-source notices…
201+ </p>
202+ )}
203+ </SettingsPageContainer>
204+ );
205+}
apps/web/src/components/settings/SettingsBreadcrumb.tsx+1−0
@@ -8,6 +8,7 @@ import { SETTINGS_SECTION_LABELS } from "./settingsSearch"; · 7 unmodified lines
88 const SETTINGS_BREADCRUMB_LABELS: Readonly<Record<string, string>> = {
99 ...SETTINGS_SECTION_LABELS,
1010 "/settings/diagnostics": "Diagnostics",
11+ "/settings/open-source-licenses": "Open source licenses",
1112 };
1213
1314 function settingsBreadcrumbLabel(pathname: string): string | null {
apps/web/src/components/settings/SettingsPanels.tsx+13−0
@@ -2523,6 +2523,19 @@ export function GeneralSettingsPanel() { · 2522 unmodified lines
25232523 </Button>
25242524 }
25252525 />
2526+ <SettingsRow
2527+ {...searchableSetting("open-source-licenses")}
2528+ description="Third-party notices for dependencies and assets included in T3 Code."
2529+ control={
2530+ <Button
2531+ render={<Link to="/settings/open-source-licenses" />}
2532+ size="xs"
2533+ variant="outline"
2534+ >
2535+ View licenses
2536+ </Button>
2537+ }
2538+ />
25262539 </SettingsSection>
25272540
25282541 <LegacyFeaturesSection />
apps/web/src/components/settings/SettingsSidebarNav.tsx+8−1
@@ -267,7 +267,14 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) { · 266 unmodified lines
267267 ))
268268 : SETTINGS_NAV_ITEMS.map((item) => {
269269 const Icon = item.icon;
270− const isActive = pathname === item.to || pathname.startsWith(`${item.to}/`);
270+ const isGeneralDetailPage =
271+ item.to === "/settings/general" &&
272+ (pathname === "/settings/diagnostics" ||
273+ pathname === "/settings/open-source-licenses");
274+ const isActive =
275+ isGeneralDetailPage ||
276+ pathname === item.to ||
277+ pathname.startsWith(`${item.to}/`);
271278 return (
272279 <SidebarMenuItem key={item.to}>
273280 <SidebarMenuButton
apps/web/src/components/settings/settingsSearch.ts+5−0
@@ -188,6 +188,11 @@ export const SETTINGS_SEARCH_ITEMS = [ · 187 unmodified lines
188188 title: "Diagnostics",
189189 to: "/settings/general",
190190 },
191+ {
192+ id: "open-source-licenses",
193+ title: "Open source licenses",
194+ to: "/settings/general",
195+ },
191196 {
192197 id: "legacy-plan-mode",
193198 title: "Plan mode (legacy)",
apps/web/src/routeTree.gen.ts+22−0
@@ -17,6 +17,7 @@ import { Route as ChatRouteImport } from './routes/_chat' · 16 unmodified lines
1717 import { Route as ChatIndexRouteImport } from './routes/_chat.index'
1818 import { Route as SettingsSourceControlRouteImport } from './routes/settings.source-control'
1919 import { Route as SettingsProvidersRouteImport } from './routes/settings.providers'
20+import { Route as SettingsOpenSourceLicensesRouteImport } from './routes/settings.open-source-licenses'
2021 import { Route as SettingsKeybindingsRouteImport } from './routes/settings.keybindings'
2122 import { Route as SettingsIntegrationsRouteImport } from './routes/settings.integrations'
2223 import { Route as SettingsGeneralRouteImport } from './routes/settings.general'
@@ -69,6 +70,12 @@ const SettingsProvidersRoute = SettingsProvidersRouteImport.update({ · 46 unmodified lines
6970 path: '/providers',
7071 getParentRoute: () => SettingsRoute,
7172 } as any)
73+const SettingsOpenSourceLicensesRoute =
74+ SettingsOpenSourceLicensesRouteImport.update({
75+ id: '/open-source-licenses',
76+ path: '/open-source-licenses',
77+ getParentRoute: () => SettingsRoute,
78+ } as any)
7279 const SettingsKeybindingsRoute = SettingsKeybindingsRouteImport.update({
7380 id: '/keybindings',
7481 path: '/keybindings',
@@ -147,6 +154,7 @@ export interface FileRoutesByFullPath { · 72 unmodified lines
147154 '/settings/general': typeof SettingsGeneralRoute
148155 '/settings/integrations': typeof SettingsIntegrationsRoute
149156 '/settings/keybindings': typeof SettingsKeybindingsRoute
157+ '/settings/open-source-licenses': typeof SettingsOpenSourceLicensesRoute
150158 '/settings/providers': typeof SettingsProvidersRoute
151159 '/settings/source-control': typeof SettingsSourceControlRoute
152160 '/$environmentId/$threadId': typeof ChatEnvironmentIdThreadIdRoute
@@ -167,6 +175,7 @@ export interface FileRoutesByTo { · 14 unmodified lines
167175 '/settings/general': typeof SettingsGeneralRoute
168176 '/settings/integrations': typeof SettingsIntegrationsRoute
169177 '/settings/keybindings': typeof SettingsKeybindingsRoute
178+ '/settings/open-source-licenses': typeof SettingsOpenSourceLicensesRoute
170179 '/settings/providers': typeof SettingsProvidersRoute
171180 '/settings/source-control': typeof SettingsSourceControlRoute
172181 '/': typeof ChatIndexRoute
@@ -190,6 +199,7 @@ export interface FileRoutesById { · 17 unmodified lines
190199 '/settings/general': typeof SettingsGeneralRoute
191200 '/settings/integrations': typeof SettingsIntegrationsRoute
192201 '/settings/keybindings': typeof SettingsKeybindingsRoute
202+ '/settings/open-source-licenses': typeof SettingsOpenSourceLicensesRoute
193203 '/settings/providers': typeof SettingsProvidersRoute
194204 '/settings/source-control': typeof SettingsSourceControlRoute
195205 '/_chat/': typeof ChatIndexRoute
@@ -214,6 +224,7 @@ export interface FileRouteTypes { · 18 unmodified lines
214224 | '/settings/general'
215225 | '/settings/integrations'
216226 | '/settings/keybindings'
227+ | '/settings/open-source-licenses'
217228 | '/settings/providers'
218229 | '/settings/source-control'
219230 | '/$environmentId/$threadId'
@@ -234,6 +245,7 @@ export interface FileRouteTypes { · 14 unmodified lines
234245 | '/settings/general'
235246 | '/settings/integrations'
236247 | '/settings/keybindings'
248+ | '/settings/open-source-licenses'
237249 | '/settings/providers'
238250 | '/settings/source-control'
239251 | '/'
@@ -256,6 +268,7 @@ export interface FileRouteTypes { · 16 unmodified lines
256268 | '/settings/general'
257269 | '/settings/integrations'
258270 | '/settings/keybindings'
271+ | '/settings/open-source-licenses'
259272 | '/settings/providers'
260273 | '/settings/source-control'
261274 | '/_chat/'
@@ -331,6 +344,13 @@ declare module '@tanstack/react-router' { · 69 unmodified lines
331344 preLoaderRoute: typeof SettingsProvidersRouteImport
332345 parentRoute: typeof SettingsRoute
333346 }
347+ '/settings/open-source-licenses': {
348+ id: '/settings/open-source-licenses'
349+ path: '/open-source-licenses'
350+ fullPath: '/settings/open-source-licenses'
351+ preLoaderRoute: typeof SettingsOpenSourceLicensesRouteImport
352+ parentRoute: typeof SettingsRoute
353+ }
334354 '/settings/keybindings': {
335355 id: '/settings/keybindings'
336356 path: '/keybindings'
@@ -442,6 +462,7 @@ interface SettingsRouteChildren { · 105 unmodified lines
442462 SettingsGeneralRoute: typeof SettingsGeneralRoute
443463 SettingsIntegrationsRoute: typeof SettingsIntegrationsRoute
444464 SettingsKeybindingsRoute: typeof SettingsKeybindingsRoute
465+ SettingsOpenSourceLicensesRoute: typeof SettingsOpenSourceLicensesRoute
445466 SettingsProvidersRoute: typeof SettingsProvidersRoute
446467 SettingsSourceControlRoute: typeof SettingsSourceControlRoute
447468 }
@@ -454,6 +475,7 @@ const SettingsRouteChildren: SettingsRouteChildren = { · 6 unmodified lines
454475 SettingsGeneralRoute: SettingsGeneralRoute,
455476 SettingsIntegrationsRoute: SettingsIntegrationsRoute,
456477 SettingsKeybindingsRoute: SettingsKeybindingsRoute,
478+ SettingsOpenSourceLicensesRoute: SettingsOpenSourceLicensesRoute,
457479 SettingsProvidersRoute: SettingsProvidersRoute,
458480 SettingsSourceControlRoute: SettingsSourceControlRoute,
459481 }
apps/web/src/routes/settings.open-source-licenses.tsx+7−0
@@ -0,0 +1,7 @@
1+import { createFileRoute } from "@tanstack/react-router";
2+
3+import { OpenSourceLicensesPanel } from "../components/settings/OpenSourceLicenses";
4+
5+export const Route = createFileRoute("/settings/open-source-licenses")({
6+ component: OpenSourceLicensesPanel,
7+});
apps/web/tsconfig.json+2−1
@@ -31,6 +31,7 @@ · 30 unmodified lines
3131 "vercel.ts",
3232 "test",
3333 "scripts/warm-dep-cache.ts",
34− "../../scripts/lib/public-config.ts"
34+ "../../scripts/lib/public-config.ts",
35+ "../../scripts/lib/third-party-licenses.ts"
3536 ]
3637 }
apps/web/vite.config.ts+10−0
@@ -13,6 +13,7 @@ import pkg from "./package.json" with { type: "json" }; · 12 unmodified lines
1313 import { DEV_PROXIED_PATH_PREFIXES } from "@t3tools/shared/devProxy";
1414
1515 import { loadRepoEnv } from "../../scripts/lib/public-config";
16+import { thirdPartyLicensesPlugin } from "../../scripts/lib/third-party-licenses";
1617
1718 const repoEnv = loadRepoEnv();
1819 Object.assign(process.env, repoEnv);
@@ -156,6 +157,15 @@ export default defineConfig(() => { · 137 unmodified lines
156157 assetsInclude: ["**/*.wasm"],
157158 plugins: [
158159 devCompressionPlugin(),
160+ thirdPartyLicensesPlugin({
161+ bundleName: "web",
162+ configFile: new URL("../../third-party-licenses.config.json", import.meta.url),
163+ packageManifests: [
164+ { bundle: "web", path: new URL("./package.json", import.meta.url) },
165+ { bundle: "server", path: new URL("../server/package.json", import.meta.url) },
166+ { bundle: "desktop", path: new URL("../desktop/package.json", import.meta.url) },
167+ ],
168+ }),
159169 tanstackRouter(),
160170 react(),
161171 babel({
docs/internals/open-source-licenses.md+84−0
@@ -0,0 +1,84 @@
1+# Open source license notices
2+
3+License notices are generated independently for the client that ships them:
4+
5+- The web build emits `third-party-licenses.json` beside `index.html`. The Settings page loads that
6+ static file, so the same artifact works in hosted web, the client bundled with `npx t3`, and
7+ desktop.
8+- The mobile Metro config generates an ignored virtual module before each development, native, or
9+ over-the-air JavaScript bundle. Mobile reads the manifest directly from that module and does not
10+ need a network request.
11+
12+Neither path depends on the connected environment or an RPC.
13+
14+## What the build collects
15+
16+The generator follows installed production and optional dependencies, including dependencies of
17+workspace packages, and omits first-party `@t3tools/*` packages. The web manifest starts from the
18+web, server, and desktop package manifests. The mobile manifest starts from the mobile package
19+manifest. During the web bundle, the generator also checks emitted module ids to catch a bundled
20+npm import missing from a package manifest.
21+
22+The mobile manifest deliberately follows the complete production dependency closure declared by
23+Expo and React Native. That is conservative and can include build tooling that is not present in
24+the final JavaScript bundle, but it avoids dropping a notice when platform bundling changes.
25+
26+The build fails when a collected package has no distributable license identifier or contains no
27+license or notice file. This turns missing attribution into a build error instead of an empty row
28+in a release.
29+
30+## Custom notices and package overrides
31+
32+The repository-level `third-party-licenses.config.json` holds manually maintained exceptions for
33+all clients. Add an entry to `customNotices` for adapted icons, fonts, media, native modules, or
34+another asset that did not come from an npm package:
35+
36+```json
37+{
38+ "name": "asset-name",
39+ "license": "CC-BY-4.0",
40+ "noticeFile": "licenses/asset-name.txt",
41+ "sourceUrl": "https://example.com/source",
42+ "bundles": ["assets", "web"]
43+}
44+```
45+
46+Paths in `noticeFile` are relative to the config file. The notice file should contain the complete
47+copyright, attribution, and license text required for redistribution. `bundles` controls which
48+generated manifests include the entry; omit it only when the notice belongs in every client.
49+
50+Use `packageOverrides` only when an installed npm archive omits its notice or has incorrect
51+metadata:
52+
53+```json
54+{
55+ "name": "package-name",
56+ "version": "1.2.3",
57+ "noticeFile": "licenses/package-name-1.2.3.txt",
58+ "license": "MIT",
59+ "sourceUrl": "https://example.com/package-name"
60+}
61+```
62+
63+`version`, `license`, and `sourceUrl` are optional. Omitting `version` applies the override to every
64+installed version of that package. An override can use `repositoryUrl` instead of `name` when
65+several packages from one monorepo share the same notice:
66+
67+```json
68+{
69+ "repositoryUrl": "https://github.com/example/project",
70+ "noticeFile": "licenses/project.txt"
71+}
72+```
73+
74+The generator also reuses an installed sibling package's notice when both packages declare the
75+same normalized repository and license. A name-and-version override always wins over these
76+repository fallbacks.
77+
78+The `@react-grab/cli` override uses the root React Grab repository's MIT license because the CLI's
79+npm archive omits both its license field and license file. Keep the override until the published
80+CLI package carries that metadata itself.
81+
82+Generated mobile files live under `apps/mobile/.generated/` and are ignored. Do not commit or edit
83+them; updating dependencies, configuration, or a notice file is enough for the next Metro startup
84+to refresh the manifest.
docs/user/open-source-licenses.md+12−0
@@ -0,0 +1,12 @@
1+# Open source licenses
2+
3+T3 Code includes third-party software and adapted assets. To read their license and attribution
4+notices:
5+
6+- On web and desktop, open **Settings → General**, find **Open source licenses** under **About**,
7+ and select **View licenses**.
8+- On mobile, open **Settings → App → Open source licenses**.
9+
10+The page lists the package version, license identifier, and the parts of T3 Code that may include
11+it. Select a row to read the complete notice text. Use the search field to find a package, version,
12+license, or app component.
packages/shared/package.json+4−0
@@ -231,6 +231,10 @@ · 230 unmodified lines
231231 "types": "./src/usageFormat.ts",
232232 "import": "./src/usageFormat.ts"
233233 },
234+ "./thirdPartyLicenses": {
235+ "types": "./src/thirdPartyLicenses.ts",
236+ "import": "./src/thirdPartyLicenses.ts"
237+ },
234238 "./claudeCompaction": {
235239 "types": "./src/claudeCompaction.ts",
236240 "import": "./src/claudeCompaction.ts"
packages/shared/src/thirdPartyLicenses.test.ts+63−0
@@ -0,0 +1,63 @@
1+import { describe, expect, it } from "vite-plus/test";
2+
3+import {
4+ decodeThirdPartyLicenseManifest,
5+ filterThirdPartyLicenseEntries,
6+ findThirdPartyLicenseEntry,
7+ formatLicenseBundles,
8+ thirdPartyLicenseEntryKey,
9+ type ThirdPartyLicenseEntry,
10+} from "./thirdPartyLicenses.js";
11+
12+const ENTRIES: ReadonlyArray<ThirdPartyLicenseEntry> = [
13+ {
14+ bundles: ["web"],
15+ kind: "package",
16+ license: "MIT",
17+ name: "react",
18+ noticeText: "React license",
19+ sourceUrl: "https://react.dev",
20+ version: "19.2.6",
21+ },
22+ {
23+ bundles: ["assets", "mobile"],
24+ kind: "custom",
25+ license: "CC-BY-4.0",
26+ name: "sample-icons",
27+ noticeText: "Icon notice",
28+ sourceUrl: null,
29+ version: null,
30+ },
31+];
32+
33+describe("third-party license manifests", () => {
34+ it("decodes the generated manifest shape", () => {
35+ expect(decodeThirdPartyLicenseManifest({ schemaVersion: 1, entries: ENTRIES })).toEqual({
36+ schemaVersion: 1,
37+ entries: ENTRIES,
38+ });
39+ });
40+
41+ it("rejects unsupported manifest versions", () => {
42+ expect(() => decodeThirdPartyLicenseManifest({ schemaVersion: 2, entries: [] })).toThrow(
43+ "unsupported format",
44+ );
45+ });
46+
47+ it("filters by package, license, version, and bundle", () => {
48+ expect(filterThirdPartyLicenseEntries(ENTRIES, "react 19.2")).toEqual([ENTRIES[0]]);
49+ expect(filterThirdPartyLicenseEntries(ENTRIES, "cc-by mobile")).toEqual([ENTRIES[1]]);
50+ expect(filterThirdPartyLicenseEntries(ENTRIES, "apache")).toEqual([]);
51+ });
52+
53+ it("formats platform bundle names for display", () => {
54+ expect(formatLicenseBundles(["android", "assets", "ios", "mobile", "plugin"])).toBe(
55+ "Android, Assets, iOS, Mobile, plugin",
56+ );
57+ });
58+
59+ it("finds an entry by its stable navigation key", () => {
60+ const entry = ENTRIES[0]!;
61+ expect(findThirdPartyLicenseEntry(ENTRIES, thirdPartyLicenseEntryKey(entry))).toBe(entry);
62+ });
63+});
packages/shared/src/thirdPartyLicenses.ts+103−0
@@ -0,0 +1,103 @@
1+export interface ThirdPartyLicenseEntry {
2+ readonly bundles: ReadonlyArray<string>;
3+ readonly kind: "custom" | "package";
4+ readonly license: string;
5+ readonly name: string;
6+ readonly noticeText: string;
7+ readonly sourceUrl: string | null;
8+ readonly version: string | null;
9+}
10+
11+export interface ThirdPartyLicenseManifest {
12+ readonly schemaVersion: 1;
13+ readonly entries: ReadonlyArray<ThirdPartyLicenseEntry>;
14+}
15+
16+function isRecord(value: unknown): value is Record<string, unknown> {
17+ return typeof value === "object" && value !== null && !Array.isArray(value);
18+}
19+
20+function isStringArray(value: unknown): value is ReadonlyArray<string> {
21+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
22+}
23+
24+function decodeEntry(value: unknown, index: number): ThirdPartyLicenseEntry {
25+ if (!isRecord(value)) {
26+ throw new Error(`License entry ${String(index + 1)} is not an object.`);
27+ }
28+ if (
29+ !isStringArray(value.bundles) ||
30+ value.bundles.length === 0 ||
31+ (value.kind !== "custom" && value.kind !== "package") ||
32+ typeof value.license !== "string" ||
33+ typeof value.name !== "string" ||
34+ typeof value.noticeText !== "string" ||
35+ (value.sourceUrl !== null && typeof value.sourceUrl !== "string") ||
36+ (value.version !== null && typeof value.version !== "string")
37+ ) {
38+ throw new Error(`License entry ${String(index + 1)} has an invalid shape.`);
39+ }
40+ return {
41+ bundles: value.bundles,
42+ kind: value.kind,
43+ license: value.license,
44+ name: value.name,
45+ noticeText: value.noticeText,
46+ sourceUrl: value.sourceUrl,
47+ version: value.version,
48+ };
49+}
50+
51+export function decodeThirdPartyLicenseManifest(value: unknown): ThirdPartyLicenseManifest {
52+ if (!isRecord(value) || value.schemaVersion !== 1 || !Array.isArray(value.entries)) {
53+ throw new Error("The open-source license manifest has an unsupported format.");
54+ }
55+ return {
56+ schemaVersion: 1,
57+ entries: value.entries.map(decodeEntry),
58+ };
59+}
60+
61+export function filterThirdPartyLicenseEntries(
62+ entries: ReadonlyArray<ThirdPartyLicenseEntry>,
63+ query: string,
64+): ReadonlyArray<ThirdPartyLicenseEntry> {
65+ const terms = query
66+ .trim()
67+ .toLocaleLowerCase()
68+ .split(/\s+/)
69+ .filter((term) => term.length > 0);
70+ if (terms.length === 0) return entries;
71+ return entries.filter((entry) => {
72+ const searchable = [entry.name, entry.version, entry.license, ...entry.bundles]
73+ .filter((value): value is string => value !== null)
74+ .join(" ")
75+ .toLocaleLowerCase();
76+ return terms.every((term) => searchable.includes(term));
77+ });
78+}
79+
80+const BUNDLE_LABELS: Readonly<Record<string, string>> = {
81+ android: "Android",
82+ assets: "Assets",
83+ desktop: "Desktop",
84+ ios: "iOS",
85+ mobile: "Mobile",
86+ server: "Server",
87+ web: "Web",
88+};
89+
90+export function formatLicenseBundles(bundles: ReadonlyArray<string>): string {
91+ return bundles.map((bundle) => BUNDLE_LABELS[bundle] ?? bundle).join(", ");
92+}
93+
94+export function thirdPartyLicenseEntryKey(entry: ThirdPartyLicenseEntry): string {
95+ return `${entry.kind}:${entry.name}:${entry.version ?? "custom"}`;
96+}
97+
98+export function findThirdPartyLicenseEntry(
99+ entries: ReadonlyArray<ThirdPartyLicenseEntry>,
100+ key: string,
101+): ThirdPartyLicenseEntry | undefined {
102+ return entries.find((entry) => thirdPartyLicenseEntryKey(entry) === key);
103+}
scripts/lib/third-party-licenses.test.ts+245−0
@@ -0,0 +1,245 @@
1+// @effect-diagnostics nodeBuiltinImport:off - Tests exercise the Node filesystem build boundary.
2+
3+import * as NodeFSP from "node:fs/promises";
4+import * as NodeOS from "node:os";
5+import * as NodePath from "node:path";
6+
7+import { afterEach, describe, expect, it } from "vite-plus/test";
8+
9+import { generateThirdPartyLicenseManifest } from "./third-party-licenses.js";
10+
11+const tempDirectories: string[] = [];
12+
13+async function writeJson(path: string, value: unknown): Promise<void> {
14+ await NodeFSP.mkdir(NodePath.dirname(path), { recursive: true });
15+ await NodeFSP.writeFile(path, `${JSON.stringify(value)}\n`, "utf8");
16+}
17+
18+async function createFixture(): Promise<{
19+ readonly appManifest: string;
20+ readonly configFile: string;
21+ readonly dependencyRoot: string;
22+ readonly root: string;
23+}> {
24+ const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3code-licenses-"));
25+ tempDirectories.push(root);
26+ const appManifest = NodePath.join(root, "package.json");
27+ const dependencyRoot = NodePath.join(root, "node_modules", "demo-dependency");
28+ const configFile = NodePath.join(root, "third-party-licenses.config.json");
29+
30+ await writeJson(appManifest, {
31+ name: "fixture-app",
32+ dependencies: { "demo-dependency": "1.2.3" },
33+ });
34+ await writeJson(NodePath.join(dependencyRoot, "package.json"), {
35+ name: "demo-dependency",
36+ version: "1.2.3",
37+ license: "MIT",
38+ main: "index.js",
39+ repository: "example/demo-dependency",
40+ });
41+ await NodeFSP.writeFile(NodePath.join(dependencyRoot, "index.js"), "export {};\n", "utf8");
42+ await NodeFSP.writeFile(
43+ NodePath.join(dependencyRoot, "LICENSE"),
44+ "Demo MIT license text\n",
45+ "utf8",
46+ );
47+ await NodeFSP.writeFile(NodePath.join(root, "asset-notice.txt"), "Asset notice text\n", "utf8");
48+ await writeJson(configFile, {
49+ customNotices: [
50+ {
51+ name: "demo-asset",
52+ license: "CC-BY-4.0",
53+ noticeFile: "asset-notice.txt",
54+ bundles: ["assets", "web"],
55+ },
56+ ],
57+ packageOverrides: [],
58+ });
59+ return { appManifest, configFile, dependencyRoot, root };
60+}
61+
62+afterEach(async () => {
63+ await Promise.all(
64+ tempDirectories
65+ .splice(0)
66+ .map((directory) => NodeFSP.rm(directory, { force: true, recursive: true })),
67+ );
68+});
69+
70+describe("third-party license generation", () => {
71+ it("collects production packages and custom asset notices", async () => {
72+ const fixture = await createFixture();
73+ const manifest = await generateThirdPartyLicenseManifest({
74+ configFile: fixture.configFile,
75+ packageManifests: [{ bundle: "web", path: fixture.appManifest }],
76+ });
77+
78+ expect(manifest).toEqual({
79+ schemaVersion: 1,
80+ entries: [
81+ {
82+ bundles: ["assets", "web"],
83+ kind: "custom",
84+ license: "CC-BY-4.0",
85+ name: "demo-asset",
86+ noticeText: "Asset notice text",
87+ sourceUrl: null,
88+ version: null,
89+ },
90+ {
91+ bundles: ["web"],
92+ kind: "package",
93+ license: "MIT",
94+ name: "demo-dependency",
95+ noticeText: "Demo MIT license text",
96+ sourceUrl: "https://github.com/example/demo-dependency",
97+ version: "1.2.3",
98+ },
99+ ],
100+ });
101+ });
102+
103+ it("fails when a production package has no distributable notice text", async () => {
104+ const fixture = await createFixture();
105+ await NodeFSP.rm(NodePath.join(fixture.dependencyRoot, "LICENSE"));
106+
107+ await expect(
108+ generateThirdPartyLicenseManifest({
109+ configFile: fixture.configFile,
110+ packageManifests: [{ bundle: "web", path: fixture.appManifest }],
111+ }),
112+ ).rejects.toThrow("does not include a license or notice file");
113+ });
114+
115+ it("collects notices nested inside a published package", async () => {
116+ const fixture = await createFixture();
117+ await NodeFSP.rm(NodePath.join(fixture.dependencyRoot, "LICENSE"));
118+ await NodeFSP.mkdir(NodePath.join(fixture.dependencyRoot, "dist", "third-party"), {
119+ recursive: true,
120+ });
121+ await NodeFSP.writeFile(
122+ NodePath.join(fixture.dependencyRoot, "dist", "third-party", "NOTICE.txt"),
123+ "Nested notice\n",
124+ "utf8",
125+ );
126+
127+ const manifest = await generateThirdPartyLicenseManifest({
128+ configFile: fixture.configFile,
129+ packageManifests: [{ bundle: "web", path: fixture.appManifest }],
130+ });
131+
132+ expect(manifest.entries.find((entry) => entry.name === "demo-dependency")?.noticeText).toBe(
133+ "Nested notice",
134+ );
135+ });
136+
137+ it("uses package overrides for notices published outside the npm archive", async () => {
138+ const fixture = await createFixture();
139+ await NodeFSP.rm(NodePath.join(fixture.dependencyRoot, "LICENSE"));
140+ await NodeFSP.writeFile(NodePath.join(fixture.root, "override.txt"), "Override text\n", "utf8");
141+ await writeJson(fixture.configFile, {
142+ customNotices: [],
143+ packageOverrides: [
144+ {
145+ name: "demo-dependency",
146+ noticeFile: "override.txt",
147+ },
148+ ],
149+ });
150+
151+ const manifest = await generateThirdPartyLicenseManifest({
152+ configFile: fixture.configFile,
153+ packageManifests: [{ bundle: "web", path: fixture.appManifest }],
154+ });
155+
156+ expect(manifest.entries[0]?.noticeText).toBe("Override text");
157+ });
158+
159+ it("applies repository overrides across monorepo packages", async () => {
160+ const fixture = await createFixture();
161+ await NodeFSP.rm(NodePath.join(fixture.dependencyRoot, "LICENSE"));
162+ await NodeFSP.writeFile(
163+ NodePath.join(fixture.root, "override.txt"),
164+ "Repository text\n",
165+ "utf8",
166+ );
167+ await writeJson(fixture.configFile, {
168+ customNotices: [],
169+ packageOverrides: [
170+ {
171+ repositoryUrl: "https://github.com/example/demo-dependency",
172+ noticeFile: "override.txt",
173+ },
174+ ],
175+ });
176+
177+ const manifest = await generateThirdPartyLicenseManifest({
178+ configFile: fixture.configFile,
179+ packageManifests: [{ bundle: "web", path: fixture.appManifest }],
180+ });
181+
182+ expect(manifest.entries[0]?.noticeText).toBe("Repository text");
183+ });
184+
185+ it("reuses a repository license for packages from the same monorepo", async () => {
186+ const fixture = await createFixture();
187+ const siblingRoot = NodePath.join(fixture.root, "node_modules", "demo-sibling");
188+ await writeJson(fixture.appManifest, {
189+ name: "fixture-app",
190+ dependencies: { "demo-dependency": "1.2.3", "demo-sibling": "2.0.0" },
191+ });
192+ await writeJson(NodePath.join(siblingRoot, "package.json"), {
193+ name: "demo-sibling",
194+ version: "2.0.0",
195+ license: "MIT",
196+ main: "index.js",
197+ repository: "example/demo-dependency",
198+ });
199+ await NodeFSP.writeFile(NodePath.join(siblingRoot, "index.js"), "export {};\n", "utf8");
200+
201+ const manifest = await generateThirdPartyLicenseManifest({
202+ configFile: fixture.configFile,
203+ packageManifests: [{ bundle: "web", path: fixture.appManifest }],
204+ });
205+
206+ expect(manifest.entries.find((entry) => entry.name === "demo-sibling")?.noticeText).toBe(
207+ "Demo MIT license text",
208+ );
209+ });
210+
211+ it("omits custom notices for other bundles", async () => {
212+ const fixture = await createFixture();
213+ await writeJson(fixture.configFile, {
214+ customNotices: [
215+ {
216+ name: "web-only-asset",
217+ license: "MIT",
218+ noticeFile: "asset-notice.txt",
219+ bundles: ["assets", "web"],
220+ },
221+ ],
222+ packageOverrides: [],
223+ });
224+
225+ const manifest = await generateThirdPartyLicenseManifest({
226+ configFile: fixture.configFile,
227+ packageManifests: [{ bundle: "mobile", path: fixture.appManifest }],
228+ });
229+
230+ expect(manifest.entries.some((entry) => entry.name === "web-only-asset")).toBe(false);
231+ });
232+
233+ it("fails when a custom notice file is empty", async () => {
234+ const fixture = await createFixture();
235+ await NodeFSP.writeFile(NodePath.join(fixture.root, "asset-notice.txt"), "\n", "utf8");
236+
237+ await expect(
238+ generateThirdPartyLicenseManifest({
239+ configFile: fixture.configFile,
240+ packageManifests: [{ bundle: "web", path: fixture.appManifest }],
241+ }),
242+ ).rejects.toThrow('Custom third-party notice "demo-asset" is empty');
243+ });
244+});
245+// @effect-diagnostics nodeBuiltinImport:off - Tests exercise the Node filesystem build boundary.
scripts/lib/third-party-licenses.ts+710−0
@@ -0,0 +1,710 @@
1+// @effect-diagnostics nodeBuiltinImport:off - Vite's build plugin runs before an Effect runtime exists.
2+
3+import * as NodeFSP from "node:fs/promises";
4+import * as NodePath from "node:path";
5+import * as NodeURL from "node:url";
6+import * as NodeModule from "node:module";
7+
8+import type { Plugin } from "vite-plus";
9+
10+export const THIRD_PARTY_LICENSES_FILE_NAME = "third-party-licenses.json";
11+
12+export interface ThirdPartyLicenseEntry {
13+ readonly bundles: ReadonlyArray<string>;
14+ readonly kind: "custom" | "package";
15+ readonly license: string;
16+ readonly name: string;
17+ readonly noticeText: string;
18+ readonly sourceUrl: string | null;
19+ readonly version: string | null;
20+}
21+
22+export interface ThirdPartyLicenseManifest {
23+ readonly schemaVersion: 1;
24+ readonly entries: ReadonlyArray<ThirdPartyLicenseEntry>;
25+}
26+
27+export interface ThirdPartyLicensePackageManifest {
28+ readonly bundle: string;
29+ readonly path: string | URL;
30+}
31+
32+export interface ThirdPartyLicensesPluginOptions {
33+ readonly configFile?: string | URL;
34+ readonly packageManifests: ReadonlyArray<ThirdPartyLicensePackageManifest>;
35+ readonly bundleName: string;
36+}
37+
38+interface PackageJson {
39+ readonly dependencies?: Readonly<Record<string, string>>;
40+ readonly homepage?: unknown;
41+ readonly license?: unknown;
42+ readonly licenses?: unknown;
43+ readonly name?: unknown;
44+ readonly optionalDependencies?: Readonly<Record<string, string>>;
45+ readonly repository?: unknown;
46+ readonly version?: unknown;
47+}
48+
49+interface CustomNoticeConfigEntry {
50+ readonly bundles?: ReadonlyArray<string>;
51+ readonly license: string;
52+ readonly name: string;
53+ readonly noticeFile: string;
54+ readonly sourceUrl?: string;
55+ readonly version?: string;
56+}
57+
58+interface PackageNoticeOverrideConfigEntry {
59+ readonly license?: string;
60+ readonly name?: string;
61+ readonly noticeFile?: string;
62+ readonly repositoryUrl?: string;
63+ readonly sourceUrl?: string;
64+ readonly version?: string;
65+}
66+
67+interface ThirdPartyLicensesConfig {
68+ readonly customNotices: ReadonlyArray<CustomNoticeConfigEntry>;
69+ readonly packageOverrides: ReadonlyArray<PackageNoticeOverrideConfigEntry>;
70+}
71+
72+interface CollectedPackage {
73+ readonly bundles: Set<string>;
74+ readonly packageJson: PackageJson;
75+ readonly packageRoot: string;
76+}
77+
78+interface PackageCollection {
79+ readonly byIdentity: Map<string, CollectedPackage>;
80+}
81+
82+const EMPTY_CONFIG: ThirdPartyLicensesConfig = {
83+ customNotices: [],
84+ packageOverrides: [],
85+};
86+
87+const NOTICE_FILE_PATTERN = /^(?:licen[cs]e|copying|notice)(?:[._-].*)?$/i;
88+const FIRST_PARTY_PACKAGE_PREFIX = "@t3tools/";
89+
90+function asPath(value: string | URL): string {
91+ return value instanceof URL ? NodeURL.fileURLToPath(value) : NodePath.resolve(value);
92+}
93+
94+function isRecord(value: unknown): value is Record<string, unknown> {
95+ return typeof value === "object" && value !== null && !Array.isArray(value);
96+}
97+
98+function readRequiredString(value: Record<string, unknown>, key: string, context: string): string {
99+ const field = value[key];
100+ if (typeof field !== "string" || field.trim().length === 0) {
101+ throw new Error(`${context} must define a non-empty "${key}" string.`);
102+ }
103+ return field.trim();
104+}
105+
106+function readOptionalString(
107+ value: Record<string, unknown>,
108+ key: string,
109+ context: string,
110+): string | undefined {
111+ const field = value[key];
112+ if (field === undefined) return undefined;
113+ if (typeof field !== "string" || field.trim().length === 0) {
114+ throw new Error(`${context} must define "${key}" as a non-empty string when present.`);
115+ }
116+ return field.trim();
117+}
118+
119+function readOptionalStringArray(
120+ value: Record<string, unknown>,
121+ key: string,
122+ context: string,
123+): ReadonlyArray<string> | undefined {
124+ const field = value[key];
125+ if (field === undefined) return undefined;
126+ if (
127+ !Array.isArray(field) ||
128+ field.length === 0 ||
129+ field.some((entry) => typeof entry !== "string" || entry.trim().length === 0)
130+ ) {
131+ throw new Error(`${context} must define "${key}" as a non-empty string array.`);
132+ }
133+ return field.map((entry) => (entry as string).trim());
134+}
135+
136+function decodeCustomNotices(value: unknown): ReadonlyArray<CustomNoticeConfigEntry> {
137+ if (value === undefined) return [];
138+ if (!Array.isArray(value)) {
139+ throw new Error('Third-party license config field "customNotices" must be an array.');
140+ }
141+ return value.map((entry, index) => {
142+ const context = `Third-party custom notice at index ${String(index)}`;
143+ if (!isRecord(entry)) throw new Error(`${context} must be an object.`);
144+ const version = readOptionalString(entry, "version", context);
145+ const sourceUrl = readOptionalString(entry, "sourceUrl", context);
146+ const bundles = readOptionalStringArray(entry, "bundles", context);
147+ return {
148+ name: readRequiredString(entry, "name", context),
149+ license: readRequiredString(entry, "license", context),
150+ noticeFile: readRequiredString(entry, "noticeFile", context),
151+ ...(version !== undefined ? { version } : {}),
152+ ...(sourceUrl !== undefined ? { sourceUrl } : {}),
153+ ...(bundles !== undefined ? { bundles } : {}),
154+ };
155+ });
156+}
157+
158+function decodePackageOverrides(value: unknown): ReadonlyArray<PackageNoticeOverrideConfigEntry> {
159+ if (value === undefined) return [];
160+ if (!Array.isArray(value)) {
161+ throw new Error('Third-party license config field "packageOverrides" must be an array.');
162+ }
163+ return value.map((entry, index) => {
164+ const context = `Third-party package override at index ${String(index)}`;
165+ if (!isRecord(entry)) throw new Error(`${context} must be an object.`);
166+ const name = readOptionalString(entry, "name", context);
167+ const repositoryUrl = readOptionalString(entry, "repositoryUrl", context);
168+ if ((name === undefined) === (repositoryUrl === undefined)) {
169+ throw new Error(`${context} must define exactly one of "name" or "repositoryUrl".`);
170+ }
171+ const version = readOptionalString(entry, "version", context);
172+ const license = readOptionalString(entry, "license", context);
173+ const noticeFile = readOptionalString(entry, "noticeFile", context);
174+ const sourceUrl = readOptionalString(entry, "sourceUrl", context);
175+ return {
176+ ...(name !== undefined ? { name } : {}),
177+ ...(repositoryUrl !== undefined ? { repositoryUrl } : {}),
178+ ...(version !== undefined ? { version } : {}),
179+ ...(license !== undefined ? { license } : {}),
180+ ...(noticeFile !== undefined ? { noticeFile } : {}),
181+ ...(sourceUrl !== undefined ? { sourceUrl } : {}),
182+ };
183+ });
184+}
185+
186+async function readConfig(configFile: string | URL | undefined): Promise<{
187+ readonly config: ThirdPartyLicensesConfig;
188+ readonly directory: string;
189+}> {
190+ if (configFile === undefined) {
191+ return { config: EMPTY_CONFIG, directory: NodePath.resolve(".") };
192+ }
193+ const configPath = asPath(configFile);
194+ const source = await NodeFSP.readFile(configPath, "utf8");
195+ const decoded = JSON.parse(source) as unknown;
196+ if (!isRecord(decoded)) throw new Error("Third-party license config must contain an object.");
197+ return {
198+ config: {
199+ customNotices: decodeCustomNotices(decoded.customNotices),
200+ packageOverrides: decodePackageOverrides(decoded.packageOverrides),
201+ },
202+ directory: NodePath.dirname(configPath),
203+ };
204+}
205+
206+async function readPackageJson(packageJsonPath: string): Promise<PackageJson> {
207+ const source = await NodeFSP.readFile(packageJsonPath, "utf8");
208+ const value = JSON.parse(source) as unknown;
209+ if (!isRecord(value)) throw new Error(`Package manifest is not an object: ${packageJsonPath}`);
210+ return value as PackageJson;
211+}
212+
213+function packageIdentity(packageJson: PackageJson, packageRoot: string): string {
214+ const name =
215+ typeof packageJson.name === "string" ? packageJson.name : NodePath.basename(packageRoot);
216+ const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
217+ return `${name}@${version}`;
218+}
219+
220+async function findPackageRoot(
221+ resolvedPath: string,
222+ expectedName?: string,
223+): Promise<{ readonly packageJson: PackageJson; readonly packageRoot: string } | null> {
224+ let current = NodePath.dirname(resolvedPath);
225+ const root = NodePath.parse(current).root;
226+
227+ while (current !== root) {
228+ const packageJsonPath = NodePath.join(current, "package.json");
229+ try {
230+ const packageJson = await readPackageJson(packageJsonPath);
231+ const matchesExpectedPackage =
232+ expectedName !== undefined
233+ ? packageJson.name === expectedName
234+ : typeof packageJson.name === "string" && typeof packageJson.version === "string";
235+ if (matchesExpectedPackage) {
236+ return { packageJson, packageRoot: await NodeFSP.realpath(current) };
237+ }
238+ } catch (error) {
239+ const code = isRecord(error) && typeof error.code === "string" ? error.code : null;
240+ if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
241+ }
242+ current = NodePath.dirname(current);
243+ }
244+ return null;
245+}
246+
247+async function resolveDependencyPackage(
248+ dependencyName: string,
249+ fromPackageJsonPath: string,
250+): Promise<{ readonly packageJson: PackageJson; readonly packageRoot: string } | null> {
251+ const requireFromPackage = NodeModule.createRequire(fromPackageJsonPath);
252+ const candidates = [`${dependencyName}/package.json`, dependencyName];
253+ for (const candidate of candidates) {
254+ try {
255+ const resolved = requireFromPackage.resolve(candidate);
256+ const found = await findPackageRoot(resolved, dependencyName);
257+ if (found) return found;
258+ } catch (error) {
259+ const code = isRecord(error) && typeof error.code === "string" ? error.code : null;
260+ if (code !== "MODULE_NOT_FOUND" && code !== "ERR_PACKAGE_PATH_NOT_EXPORTED") throw error;
261+ }
262+ }
263+ return null;
264+}
265+
266+function dependencyNames(packageJson: PackageJson): ReadonlyArray<string> {
267+ return [
268+ ...new Set([
269+ ...Object.keys(packageJson.dependencies ?? {}),
270+ ...Object.keys(packageJson.optionalDependencies ?? {}),
271+ ]),
272+ ].sort((left, right) => left.localeCompare(right));
273+}
274+
275+async function collectProductionDependencyPackages(
276+ packageManifests: ReadonlyArray<ThirdPartyLicensePackageManifest>,
277+): Promise<PackageCollection> {
278+ const collection: PackageCollection = {
279+ byIdentity: new Map(),
280+ };
281+ const visited = new Set<string>();
282+
283+ const visitManifest = async (packageJsonPath: string, bundle: string): Promise<void> => {
284+ const packageJson = await readPackageJson(packageJsonPath);
285+ for (const dependencyName of dependencyNames(packageJson)) {
286+ const resolved = await resolveDependencyPackage(dependencyName, packageJsonPath);
287+ if (!resolved) continue;
288+ const visitKey = `${bundle}:${resolved.packageRoot}`;
289+ if (visited.has(visitKey)) continue;
290+ visited.add(visitKey);
291+
292+ const dependencyPackageJsonPath = NodePath.join(resolved.packageRoot, "package.json");
293+ const name =
294+ typeof resolved.packageJson.name === "string" ? resolved.packageJson.name : dependencyName;
295+ if (!name.startsWith(FIRST_PARTY_PACKAGE_PREFIX)) {
296+ const identity = packageIdentity(resolved.packageJson, resolved.packageRoot);
297+ const existing = collection.byIdentity.get(identity);
298+ if (existing) {
299+ existing.bundles.add(bundle);
300+ } else {
301+ collection.byIdentity.set(identity, {
302+ bundles: new Set([bundle]),
303+ packageJson: resolved.packageJson,
304+ packageRoot: resolved.packageRoot,
305+ });
306+ }
307+ }
308+
309+ await visitManifest(dependencyPackageJsonPath, bundle);
310+ }
311+ };
312+
313+ for (const manifest of packageManifests) {
314+ await visitManifest(asPath(manifest.path), manifest.bundle);
315+ }
316+ return collection;
317+}
318+
319+function moduleFilePath(moduleId: string): string | null {
320+ if (moduleId.startsWith("\0") || moduleId.includes("\0")) return null;
321+ const withoutQuery = moduleId.split(/[?#]/, 1)[0] ?? moduleId;
322+ const filePath = withoutQuery.startsWith("file:")
323+ ? NodeURL.fileURLToPath(withoutQuery)
324+ : withoutQuery.replace(/^\/@fs\//, "/");
325+ const normalized = filePath.replaceAll("\\", "/");
326+ return normalized.includes("/node_modules/") ? filePath : null;
327+}
328+
329+async function addBundledModulePackages(
330+ collection: PackageCollection,
331+ moduleIds: ReadonlyArray<string>,
332+ bundle: string,
333+): Promise<void> {
334+ for (const moduleId of moduleIds) {
335+ const filePath = moduleFilePath(moduleId);
336+ if (!filePath) continue;
337+ let found: Awaited<ReturnType<typeof findPackageRoot>>;
338+ try {
339+ found = await findPackageRoot(await NodeFSP.realpath(filePath));
340+ } catch (error) {
341+ const code = isRecord(error) && typeof error.code === "string" ? error.code : null;
342+ if (code === "ENOENT" || code === "ENOTDIR") continue;
343+ throw error;
344+ }
345+ if (!found || typeof found.packageJson.name !== "string") continue;
346+ if (found.packageJson.name.startsWith(FIRST_PARTY_PACKAGE_PREFIX)) continue;
347+ const identity = packageIdentity(found.packageJson, found.packageRoot);
348+ const existing = collection.byIdentity.get(identity);
349+ if (existing) {
350+ existing.bundles.add(bundle);
351+ } else {
352+ collection.byIdentity.set(identity, {
353+ bundles: new Set([bundle]),
354+ packageJson: found.packageJson,
355+ packageRoot: found.packageRoot,
356+ });
357+ }
358+ }
359+}
360+
361+function normalizeLicense(packageJson: PackageJson): string | null {
362+ if (typeof packageJson.license === "string" && packageJson.license.trim().length > 0) {
363+ return packageJson.license.trim();
364+ }
365+ if (isRecord(packageJson.license) && typeof packageJson.license.type === "string") {
366+ return packageJson.license.type.trim() || null;
367+ }
368+ if (Array.isArray(packageJson.licenses)) {
369+ const licenses = packageJson.licenses
370+ .map((entry) => {
371+ if (typeof entry === "string") return entry.trim();
372+ if (isRecord(entry) && typeof entry.type === "string") return entry.type.trim();
373+ return "";
374+ })
375+ .filter((entry) => entry.length > 0);
376+ if (licenses.length > 0) return licenses.join(" OR ");
377+ }
378+ return null;
379+}
380+
381+function normalizeRepositoryUrl(value: unknown): string | null {
382+ const raw =
383+ typeof value === "string"
384+ ? value
385+ : isRecord(value) && typeof value.url === "string"
386+ ? value.url
387+ : null;
388+ if (!raw) return null;
389+ const trimmed = raw.trim();
390+ if (trimmed.startsWith("github:")) return `https://github.com/${trimmed.slice(7)}`;
391+ const normalized = trimmed
392+ .replace(/^git\+ssh:\/\/git@github\.com\//, "https://github.com/")
393+ .replace(/^git\+/, "")
394+ .replace(/^git@github\.com:/, "https://github.com/")
395+ .replace(/^ssh:\/\/(?:git@)?github\.com\//, "https://github.com/")
396+ .replace(/^git:\/\/github\.com\//, "https://github.com/")
397+ .replace(/\.git$/, "");
398+ if (/^[\w.-]+\/[\w.-]+$/.test(normalized)) return `https://github.com/${normalized}`;
399+ return normalized;
400+}
401+
402+function packageSourceUrl(packageJson: PackageJson): string | null {
403+ if (typeof packageJson.homepage === "string" && packageJson.homepage.trim().length > 0) {
404+ return packageJson.homepage.trim();
405+ }
406+ return normalizeRepositoryUrl(packageJson.repository);
407+}
408+
409+async function readPackageNoticeText(packageRoot: string): Promise<string | null> {
410+ const noticeFiles: string[] = [];
411+ const rootEntries = await NodeFSP.readdir(packageRoot, { withFileTypes: true });
412+ noticeFiles.push(
413+ ...rootEntries
414+ .filter((entry) => entry.isFile() && NOTICE_FILE_PATTERN.test(entry.name))
415+ .map((entry) => entry.name),
416+ );
417+
418+ const collectNestedNoticeFiles = async (directory: string, depth: number): Promise<void> => {
419+ const directoryEntries = await NodeFSP.readdir(NodePath.join(packageRoot, directory), {
420+ withFileTypes: true,
421+ });
422+ await Promise.all(
423+ directoryEntries.map(async (entry) => {
424+ const relativePath = NodePath.join(directory, entry.name);
425+ if (entry.isFile() && NOTICE_FILE_PATTERN.test(entry.name)) {
426+ noticeFiles.push(relativePath);
427+ return;
428+ }
429+ if (
430+ depth > 0 &&
431+ entry.isDirectory() &&
432+ entry.name !== "node_modules" &&
433+ entry.name !== ".git"
434+ ) {
435+ await collectNestedNoticeFiles(relativePath, depth - 1);
436+ }
437+ }),
438+ );
439+ };
440+ if (noticeFiles.length === 0) {
441+ await Promise.all(
442+ rootEntries
443+ .filter(
444+ (entry) => entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git",
445+ )
446+ .map((entry) => collectNestedNoticeFiles(entry.name, 2)),
447+ );
448+ }
449+ noticeFiles.sort((left, right) => left.localeCompare(right));
450+ if (noticeFiles.length === 0) return null;
451+
452+ const sections: string[] = [];
453+ for (const fileName of noticeFiles) {
454+ const contents = (await NodeFSP.readFile(NodePath.join(packageRoot, fileName), "utf8")).trim();
455+ if (contents.length === 0) continue;
456+ sections.push(noticeFiles.length === 1 ? contents : `${fileName}\n\n${contents}`);
457+ }
458+ return sections.length > 0 ? sections.join("\n\n---\n\n") : null;
459+}
460+
461+function repositoryNoticeKey(packageJson: PackageJson, license: string): string | null {
462+ const repositoryUrl = normalizeRepositoryUrl(packageJson.repository);
463+ return repositoryUrl
464+ ? `${repositoryUrl.toLocaleLowerCase()}\n${license.toLocaleLowerCase()}`
465+ : null;
466+}
467+
468+async function collectRepositoryNotices(
469+ collection: PackageCollection,
470+ packageNotices: Map<string, Promise<string | null>>,
471+): Promise<ReadonlyMap<string, string>> {
472+ const notices = new Map<string, string>();
473+ await Promise.all(
474+ [...collection.byIdentity.values()].map(async (collected) => {
475+ const license = normalizeLicense(collected.packageJson);
476+ if (!license) return;
477+ const key = repositoryNoticeKey(collected.packageJson, license);
478+ if (!key || notices.has(key)) return;
479+ const noticeText = await packageNoticeText(collected.packageRoot, packageNotices);
480+ if (noticeText) notices.set(key, noticeText);
481+ }),
482+ );
483+ return notices;
484+}
485+
486+function packageNoticeText(
487+ packageRoot: string,
488+ cache: Map<string, Promise<string | null>>,
489+): Promise<string | null> {
490+ const existing = cache.get(packageRoot);
491+ if (existing) return existing;
492+ const notice = readPackageNoticeText(packageRoot);
493+ cache.set(packageRoot, notice);
494+ return notice;
495+}
496+
497+function findPackageOverride(
498+ overrides: ReadonlyArray<PackageNoticeOverrideConfigEntry>,
499+ name: string,
500+ version: string,
501+ packageJson: PackageJson,
502+): PackageNoticeOverrideConfigEntry | undefined {
503+ const matchingVersion = (override: PackageNoticeOverrideConfigEntry) =>
504+ override.version === undefined || override.version === version;
505+ const repositoryUrl = normalizeRepositoryUrl(packageJson.repository)?.toLocaleLowerCase();
506+ return (
507+ overrides.find((override) => override.name === name && override.version === version) ??
508+ overrides.find((override) => override.name === name && override.version === undefined) ??
509+ overrides.find(
510+ (override) =>
511+ repositoryUrl !== undefined &&
512+ override.repositoryUrl !== undefined &&
513+ normalizeRepositoryUrl(override.repositoryUrl)?.toLocaleLowerCase() === repositoryUrl &&
514+ matchingVersion(override),
515+ )
516+ );
517+}
518+
519+async function packageEntry(
520+ collected: CollectedPackage,
521+ config: ThirdPartyLicensesConfig,
522+ configDirectory: string,
523+ packageNotices: Map<string, Promise<string | null>>,
524+ repositoryNotices: ReadonlyMap<string, string>,
525+): Promise<ThirdPartyLicenseEntry> {
526+ const name =
527+ typeof collected.packageJson.name === "string"
528+ ? collected.packageJson.name
529+ : NodePath.basename(collected.packageRoot);
530+ const version =
531+ typeof collected.packageJson.version === "string" ? collected.packageJson.version : "unknown";
532+ const override = findPackageOverride(
533+ config.packageOverrides,
534+ name,
535+ version,
536+ collected.packageJson,
537+ );
538+ const license = override?.license ?? normalizeLicense(collected.packageJson);
539+ if (!license || /^(?:unlicensed|proprietary)$/i.test(license)) {
540+ throw new Error(
541+ `${name}@${version} does not declare a distributable license. Add a package override in the third-party license config if the package publishes its notice elsewhere.`,
542+ );
543+ }
544+
545+ const repositoryKey = repositoryNoticeKey(collected.packageJson, license);
546+ const noticeText = override?.noticeFile
547+ ? (
548+ await NodeFSP.readFile(NodePath.resolve(configDirectory, override.noticeFile), "utf8")
549+ ).trim()
550+ : ((await packageNoticeText(collected.packageRoot, packageNotices)) ??
551+ (repositoryKey ? repositoryNotices.get(repositoryKey) : undefined));
552+ if (!noticeText) {
553+ throw new Error(
554+ `${name}@${version} does not include a license or notice file. Add a package override with "noticeFile" in the third-party license config.`,
555+ );
556+ }
557+
558+ return {
559+ bundles: [...collected.bundles].sort((left, right) => left.localeCompare(right)),
560+ kind: "package",
561+ license,
562+ name,
563+ noticeText,
564+ sourceUrl: override?.sourceUrl ?? packageSourceUrl(collected.packageJson),
565+ version,
566+ };
567+}
568+
569+async function customEntries(
570+ config: ThirdPartyLicensesConfig,
571+ configDirectory: string,
572+ includedBundles: ReadonlySet<string>,
573+): Promise<ReadonlyArray<ThirdPartyLicenseEntry>> {
574+ return Promise.all(
575+ config.customNotices
576+ .filter(
577+ (notice) =>
578+ notice.bundles === undefined ||
579+ notice.bundles.some((bundle) => includedBundles.has(bundle)),
580+ )
581+ .map(async (notice) => {
582+ const noticeText = (
583+ await NodeFSP.readFile(NodePath.resolve(configDirectory, notice.noticeFile), "utf8")
584+ ).trim();
585+ if (noticeText.length === 0) {
586+ throw new Error(`Custom third-party notice "${notice.name}" is empty.`);
587+ }
588+ return {
589+ bundles: [...(notice.bundles ?? ["assets"])].sort((left, right) =>
590+ left.localeCompare(right),
591+ ),
592+ kind: "custom" as const,
593+ license: notice.license,
594+ name: notice.name,
595+ noticeText,
596+ sourceUrl: notice.sourceUrl ?? null,
597+ version: notice.version ?? null,
598+ };
599+ }),
600+ );
601+}
602+
603+function entrySort(left: ThirdPartyLicenseEntry, right: ThirdPartyLicenseEntry): number {
604+ return (
605+ left.name.localeCompare(right.name) ||
606+ (left.version ?? "").localeCompare(right.version ?? "") ||
607+ left.kind.localeCompare(right.kind)
608+ );
609+}
610+
611+export async function generateThirdPartyLicenseManifest(input: {
612+ readonly configFile?: string | URL;
613+ readonly packageManifests: ReadonlyArray<ThirdPartyLicensePackageManifest>;
614+ readonly bundledModuleIds?: ReadonlyArray<string>;
615+ readonly bundleName?: string;
616+}): Promise<ThirdPartyLicenseManifest> {
617+ const [{ config, directory }, collection] = await Promise.all([
618+ readConfig(input.configFile),
619+ collectProductionDependencyPackages(input.packageManifests),
620+ ]);
621+ if (input.bundledModuleIds && input.bundleName) {
622+ await addBundledModulePackages(collection, input.bundledModuleIds, input.bundleName);
623+ }
624+
625+ const packageNotices = new Map<string, Promise<string | null>>();
626+ const repositoryNotices = await collectRepositoryNotices(collection, packageNotices);
627+
628+ const packageEntryResults = await Promise.allSettled(
629+ [...collection.byIdentity.values()].map((collected) =>
630+ packageEntry(collected, config, directory, packageNotices, repositoryNotices),
631+ ),
632+ );
633+ const failures = packageEntryResults.flatMap((result) =>
634+ result.status === "rejected"
635+ ? [result.reason instanceof Error ? result.reason.message : String(result.reason)]
636+ : [],
637+ );
638+ if (failures.length > 0) {
639+ throw new Error(
640+ `Third-party license generation found ${String(failures.length)} invalid package notice${failures.length === 1 ? "" : "s"}:\n${failures.map((failure) => `- ${failure}`).join("\n")}`,
641+ );
642+ }
643+ const packageEntries = packageEntryResults.flatMap((result) =>
644+ result.status === "fulfilled" ? [result.value] : [],
645+ );
646+ const includedBundles = new Set(input.packageManifests.map((manifest) => manifest.bundle));
647+ if (input.bundleName) includedBundles.add(input.bundleName);
648+ const manualEntries = await customEntries(config, directory, includedBundles);
649+ return {
650+ schemaVersion: 1,
651+ entries: [...packageEntries, ...manualEntries].sort(entrySort),
652+ };
653+}
654+
655+function moduleIdsFromBundle(bundle: unknown): ReadonlyArray<string> {
656+ const ids = new Set<string>();
657+ if (!isRecord(bundle)) return [];
658+ for (const output of Object.values(bundle)) {
659+ if (!isRecord(output) || output.type !== "chunk" || !isRecord(output.modules)) continue;
660+ for (const id of Object.keys(output.modules)) ids.add(id);
661+ }
662+ return [...ids];
663+}
664+
665+function serializeManifest(manifest: ThirdPartyLicenseManifest): string {
666+ return `${JSON.stringify(manifest)}\n`;
667+}
668+
669+export function thirdPartyLicensesPlugin(options: ThirdPartyLicensesPluginOptions): Plugin {
670+ return {
671+ name: "t3code:third-party-licenses",
672+ configureServer(server) {
673+ let manifestPromise: Promise<ThirdPartyLicenseManifest> | null = null;
674+ server.middlewares.use((request, response, next) => {
675+ if (request.url?.split("?", 1)[0] !== `/${THIRD_PARTY_LICENSES_FILE_NAME}`) {
676+ next();
677+ return;
678+ }
679+ manifestPromise ??= generateThirdPartyLicenseManifest({
680+ packageManifests: options.packageManifests,
681+ ...(options.configFile !== undefined ? { configFile: options.configFile } : {}),
682+ });
683+ void manifestPromise.then(
684+ (manifest) => {
685+ response.statusCode = 200;
686+ response.setHeader("Content-Type", "application/json; charset=utf-8");
687+ response.setHeader("Cache-Control", "no-store");
688+ response.end(serializeManifest(manifest));
689+ },
690+ (error: unknown) => {
691+ next(error instanceof Error ? error : new Error(String(error)));
692+ },
693+ );
694+ });
695+ },
696+ async generateBundle(_outputOptions, bundle) {
697+ const manifest = await generateThirdPartyLicenseManifest({
698+ packageManifests: options.packageManifests,
699+ bundledModuleIds: moduleIdsFromBundle(bundle),
700+ bundleName: options.bundleName,
701+ ...(options.configFile !== undefined ? { configFile: options.configFile } : {}),
702+ });
703+ this.emitFile({
704+ type: "asset",
705+ fileName: THIRD_PARTY_LICENSES_FILE_NAME,
706+ source: serializeManifest(manifest),
707+ });
708+ },
709+ };
710+}
third-party-licenses.config.json+181−0
@@ -0,0 +1,181 @@
1+{
2+ "customNotices": [
3+ {
4+ "bundles": ["assets", "mobile", "web"],
5+ "license": "MIT",
6+ "name": "vscode-icons",
7+ "noticeFile": "apps/web/THIRD_PARTY_NOTICES.md",
8+ "sourceUrl": "https://github.com/vscode-icons/vscode-icons"
9+ },
10+ {
11+ "bundles": ["android", "assets", "mobile", "web"],
12+ "license": "MIT",
13+ "name": "libghostty-vt",
14+ "noticeFile": "native/libghostty-vt/LICENSE",
15+ "sourceUrl": "https://github.com/ghostty-org/ghostty"
16+ },
17+ {
18+ "bundles": ["assets", "web"],
19+ "license": "MIT",
20+ "name": "Symbols Nerd Font Mono",
21+ "noticeFile": "apps/web/src/terminal/ghostty/fonts/LICENSE",
22+ "sourceUrl": "https://github.com/ryanoasis/nerd-fonts"
23+ },
24+ {
25+ "bundles": ["ios", "mobile"],
26+ "license": "MIT",
27+ "name": "GhosttyKit",
28+ "noticeFile": "apps/mobile/licenses/ghostty-kit.txt",
29+ "sourceUrl": "https://github.com/Yash-Singh1/ghostty/tree/custom-io"
30+ },
31+ {
32+ "bundles": ["android", "assets", "mobile"],
33+ "license": "Apache-2.0",
34+ "name": "MesloLGS NF",
35+ "noticeFile": "apps/mobile/licenses/meslo-lgs-nf.txt",
36+ "sourceUrl": "https://github.com/romkatv/powerlevel10k-media"
37+ },
38+ {
39+ "bundles": ["mobile"],
40+ "license": "MIT",
41+ "name": "react-native-uitextview",
42+ "noticeFile": "apps/mobile/modules/t3-markdown-text/LICENSE",
43+ "sourceUrl": "https://github.com/bluesky-social/react-native-uitextview",
44+ "version": "2.2.0"
45+ },
46+ {
47+ "bundles": ["mobile"],
48+ "license": "MIT",
49+ "name": "Expo text editor implementation",
50+ "noticeFile": "apps/mobile/modules/t3-composer-editor/LICENSE",
51+ "sourceUrl": "https://github.com/expo/expo"
52+ }
53+ ],
54+ "packageOverrides": [
55+ {
56+ "license": "MIT",
57+ "name": "@react-grab/cli",
58+ "noticeFile": "apps/web/licenses/react-grab-cli.txt",
59+ "sourceUrl": "https://github.com/aidenybai/react-grab/tree/main/packages/cli"
60+ },
61+ {
62+ "name": "glob-to-regexp",
63+ "noticeFile": "apps/web/licenses/glob-to-regexp.txt"
64+ },
65+ {
66+ "name": "kubernetes-types",
67+ "noticeFile": "apps/web/licenses/kubernetes-types.txt"
68+ },
69+ {
70+ "name": "@msgpackr-extract/msgpackr-extract-linux-x64",
71+ "noticeFile": "apps/web/licenses/msgpackr-extract-linux-x64.txt"
72+ },
73+ {
74+ "name": "isarray",
75+ "noticeFile": "apps/web/licenses/isarray.txt"
76+ },
77+ {
78+ "name": "keyv",
79+ "noticeFile": "apps/web/licenses/keyv.txt"
80+ },
81+ {
82+ "license": "MIT",
83+ "name": "type-fest",
84+ "noticeFile": "apps/web/licenses/type-fest.txt"
85+ },
86+ {
87+ "name": "lazy-val",
88+ "noticeFile": "apps/web/licenses/lazy-val.txt"
89+ },
90+ {
91+ "name": "@pierre/theming",
92+ "noticeFile": "apps/web/licenses/pierre-theming.txt"
93+ },
94+ {
95+ "name": "lru_map",
96+ "noticeFile": "apps/web/licenses/lru-map.txt"
97+ },
98+ {
99+ "name": "@electron-internal/extract-zip",
100+ "noticeFile": "apps/web/licenses/electron-internal-extract-zip.txt"
101+ },
102+ {
103+ "noticeFile": "apps/mobile/licenses/metro.txt",
104+ "repositoryUrl": "https://github.com/facebook/metro"
105+ },
106+ {
107+ "noticeFile": "apps/mobile/licenses/metro.txt",
108+ "repositoryUrl": "https://github.com/react/metro"
109+ },
110+ {
111+ "name": "@react-native-ai/apple",
112+ "noticeFile": "apps/mobile/licenses/react-native-ai.txt",
113+ "sourceUrl": "https://github.com/callstackincubator/ai"
114+ },
115+ {
116+ "name": "react-remove-scroll-bar",
117+ "noticeFile": "apps/mobile/licenses/react-remove-scroll-bar.txt"
118+ },
119+ {
120+ "name": "standard-navigation",
121+ "noticeFile": "apps/mobile/licenses/standard-navigation.txt"
122+ },
123+ {
124+ "name": "@expo/sdk-runtime-versions",
125+ "noticeFile": "apps/mobile/modules/t3-composer-editor/LICENSE"
126+ },
127+ {
128+ "name": "@expo/ws-tunnel",
129+ "noticeFile": "apps/mobile/modules/t3-composer-editor/LICENSE"
130+ },
131+ {
132+ "name": "stream-buffers",
133+ "noticeFile": "apps/mobile/licenses/stream-buffers.txt"
134+ },
135+ {
136+ "name": "bplist-parser",
137+ "noticeFile": "apps/mobile/licenses/bplist-parser.txt"
138+ },
139+ {
140+ "name": "@expo/devcert",
141+ "noticeFile": "apps/mobile/licenses/expo-devcert.txt"
142+ },
143+ {
144+ "name": "jimp-compact",
145+ "noticeFile": "apps/mobile/licenses/jimp-compact.txt"
146+ },
147+ {
148+ "name": "fb-watchman",
149+ "noticeFile": "apps/web/licenses/kubernetes-types.txt"
150+ },
151+ {
152+ "name": "bser",
153+ "noticeFile": "apps/web/licenses/kubernetes-types.txt"
154+ },
155+ {
156+ "name": "@expo/xcpretty",
157+ "noticeFile": "apps/mobile/licenses/expo-xcpretty.txt"
158+ },
159+ {
160+ "license": "MIT",
161+ "name": "fb-dotslash",
162+ "noticeFile": "apps/mobile/licenses/fb-dotslash.txt"
163+ },
164+ {
165+ "name": "structured-headers",
166+ "noticeFile": "apps/mobile/licenses/structured-headers.txt"
167+ },
168+ {
169+ "name": "badgin",
170+ "noticeFile": "apps/mobile/licenses/badgin.txt"
171+ },
172+ {
173+ "name": "react-native-nitro-modules",
174+ "noticeFile": "apps/mobile/licenses/react-native-nitro-modules.txt"
175+ },
176+ {
177+ "name": "boolbase",
178+ "noticeFile": "apps/mobile/licenses/boolbase.txt"
179+ }
180+ ]
181+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t