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
30.electron-runtime/30.electron-runtime/
31.showcase/31.showcase/
32apps/mobile/.showcase/32apps/mobile/.showcase/
33apps/mobile/.generated/
33artifacts/app-store/screenshots/34artifacts/app-store/screenshots/
34.github/pr-assets/35.github/pr-assets/
35native/**/target/36native/**/target/
apps/mobile/licenses/badgin.txt+23−0
@@ -0,0 +1,23 @@
1badgin is a refactored fork of Tinycon.
2
3MIT License
4
5Copyright (c) Julian Hundeloh and Tinycon contributors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
apps/mobile/licenses/boolbase.txt+13−0
@@ -0,0 +1,13 @@
1Copyright (c) 2014-2015, Felix Boehm <me@feedic.com>
2
3Permission to use, copy, modify, and/or distribute this software for any
4purpose with or without fee is hereby granted, provided that the above
5copyright notice and this permission notice appear in all copies.
6
7THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
apps/mobile/licenses/bplist-parser.txt+22−0
@@ -0,0 +1,22 @@
1The MIT License
2
3Copyright (c) 2012 Near Infinity Corporation
4
5Permission is hereby granted, free of charge, to any person obtaining
6a copy of this software and associated documentation files (the
7"Software"), to deal in the Software without restriction, including
8without limitation the rights to use, copy, modify, merge, publish,
9distribute, sublicense, and/or sell copies of the Software, and to
10permit persons to whom the Software is furnished to do so, subject to
11the following conditions:
12
13The above copyright notice and this permission notice shall be
14included in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
apps/mobile/licenses/expo-devcert.txt+21−0
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) Dave Wasmer
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
apps/mobile/licenses/expo-xcpretty.txt+29−0
@@ -0,0 +1,29 @@
1BSD 3-Clause License
2
3Copyright (c) 2016-present, 650 Industries, Inc. (Expo)
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
8
91. Redistributions of source code must retain the above copyright notice, this
10 list of conditions and the following disclaimer.
11
122. 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
163. 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
20THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
apps/mobile/licenses/fb-dotslash.txt+21−0
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) Meta Platforms, Inc. and affiliates.
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
apps/mobile/licenses/ghostty-kit.txt+32−0
@@ -0,0 +1,32 @@
1GhosttyKit
2
3The iOS terminal renderer vendors GhosttyKit.xcframework, a libghostty build produced from T3's
4iOS 16 support fork. That fork was created from VVTerm's custom-I/O Ghostty fork.
5
6Upstream project: https://github.com/ghostty-org/ghostty
7Custom-I/O base fork: https://github.com/wiedymi/ghostty/tree/custom-io
8Vendored source fork: https://github.com/Yash-Singh1/ghostty/tree/custom-io
9Vendored revision: d36c3b8dffd0d756dd5e5f4933962f774a0e6753
10Reference integration: https://github.com/vivy-company/vvterm
11
12MIT License
13
14Copyright (c) 2024 Mitchell Hashimoto, Ghostty contributors
15
16Permission is hereby granted, free of charge, to any person obtaining a copy
17of this software and associated documentation files (the "Software"), to deal
18in the Software without restriction, including without limitation the rights
19to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20copies of the Software, and to permit persons to whom the Software is
21furnished to do so, subject to the following conditions:
22
23The above copyright notice and this permission notice shall be included in all
24copies or substantial portions of the Software.
25
26THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32SOFTWARE.
apps/mobile/licenses/jimp-compact.txt+21−0
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) 2018 Oliver Moran
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
apps/mobile/licenses/meslo-lgs-nf.txt+13−0
@@ -0,0 +1,13 @@
1Copyright 2009, 2010, 2013 André Berg
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
apps/mobile/licenses/metro.txt+21−0
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) Meta Platforms, Inc. and affiliates.
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
apps/mobile/licenses/react-native-ai.txt+22−0
@@ -0,0 +1,22 @@
1MIT License
2
3Copyright (c) 2024-2025 Szymon Rybczak
4Copyright (c) 2025-present Callstack
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
apps/mobile/licenses/react-native-nitro-modules.txt+21−0
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) 2024 Marc Rousavy
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
apps/mobile/licenses/react-remove-scroll-bar.txt+21−0
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) Anton Korzunov <thekashey@gmail.com>
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
apps/mobile/licenses/standard-navigation.txt+21−0
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) 2026 React Navigation Contributors
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
apps/mobile/licenses/stream-buffers.txt+24−0
@@ -0,0 +1,24 @@
1This is free and unencumbered software released into the public domain.
2
3Anyone is free to copy, modify, publish, use, compile, sell, or
4distribute this software, either in source code form or as a compiled
5binary, for any purpose, commercial or non-commercial, and by any
6means.
7
8In jurisdictions that recognize copyright laws, the author or authors
9of this software dedicate any and all copyright interest in the
10software to the public domain. We make this dedication for the benefit
11of the public at large and to the detriment of our heirs and
12successors. We intend this dedication to be an overt act of
13relinquishment in perpetuity of all present and future rights to this
14software under copyright law.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22OTHER DEALINGS IN THE SOFTWARE.
23
24For more information, please refer to <http://unlicense.org/>
apps/mobile/licenses/structured-headers.txt+21−0
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) 2018 Evert Pot
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
apps/mobile/metro.config.js+61−5
@@ -1,5 +1,7 @@
1const fs = require("node:fs");1const fs = require("node:fs");
2const path = require("node:path");2const path = require("node:path");
3const { pathToFileURL } = require("node:url");
4const TypeScript = require("typescript");
3const { getDefaultConfig } = require("expo/metro-config");5const { getDefaultConfig } = require("expo/metro-config");
4const { withUniwindConfig } = require("uniwind/metro");6const { withUniwindConfig } = require("uniwind/metro");
5const extraThemes = require("./generated-uniwind-theme-names.json");7const extraThemes = require("./generated-uniwind-theme-names.json");
@@ -7,6 +9,13 @@ const extraThemes = require("./generated-uniwind-theme-names.json"); · 1 unmodified line
7/** @type {import("expo/metro-config").MetroConfig} */9/** @type {import("expo/metro-config").MetroConfig} */
8const config = getDefaultConfig(__dirname);10const config = getDefaultConfig(__dirname);
9const workspaceRoot = path.resolve(__dirname, "../..");11const workspaceRoot = path.resolve(__dirname, "../..");
12const generatedLicenseModuleRoot = path.join(__dirname, ".generated", "third-party-licenses");
13const licenseGeneratorSource = path.join(
14 workspaceRoot,
15 "scripts",
16 "lib",
17 "third-party-licenses.ts",
18);
10const escapedWorkspaceRoot = workspaceRoot.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");19const escapedWorkspaceRoot = workspaceRoot.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11const mobileShikiRoot = path.dirname(require.resolve("shiki/package.json", { paths: [__dirname] }));20const mobileShikiRoot = path.dirname(require.resolve("shiki/package.json", { paths: [__dirname] }));
12const resolveShikiDependencyRoot = (packageName) => {21const resolveShikiDependencyRoot = (packageName) => {
@@ -38,6 +47,7 @@ config.resolver = { · 25 unmodified lines
38 extraNodeModules: {47 extraNodeModules: {
39 // oxlint-disable-next-line unicorn/no-useless-fallback-in-spread48 // oxlint-disable-next-line unicorn/no-useless-fallback-in-spread
40 ...(config.resolver?.extraNodeModules ?? {}),49 ...(config.resolver?.extraNodeModules ?? {}),
50 "@t3tools/mobile-third-party-licenses": generatedLicenseModuleRoot,
41 shiki: mobileShikiRoot,51 shiki: mobileShikiRoot,
42 "@shikijs/core": resolveShikiDependencyRoot("@shikijs/core"),52 "@shikijs/core": resolveShikiDependencyRoot("@shikijs/core"),
43 "@shikijs/engine-javascript": resolveShikiDependencyRoot("@shikijs/engine-javascript"),53 "@shikijs/engine-javascript": resolveShikiDependencyRoot("@shikijs/engine-javascript"),
@@ -49,8 +59,54 @@ config.resolver = { · 5 unmodified lines
49 },59 },
50};60};
51 61
52module.exports = withUniwindConfig(config, {62async function writeFileIfChanged(filePath, contents) {
53 cssEntryFile: "./global.css",63 try {
54 extraThemes,64 if ((await fs.promises.readFile(filePath, "utf8")) === contents) return;
55 polyfills: { rem: 14 },65 } catch (error) {
56});66 if (error?.code !== "ENOENT") throw error;
67 }
68 await fs.promises.writeFile(filePath, contents, "utf8");
69}
70
71async 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
106module.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
55import { SettingsAuthRouteScreen } from "./features/settings/SettingsAuthRouteScreen";55import { SettingsAuthRouteScreen } from "./features/settings/SettingsAuthRouteScreen";
56import { SettingsEnvironmentsRouteScreen } from "./features/settings/SettingsEnvironmentsRouteScreen";56import { SettingsEnvironmentsRouteScreen } from "./features/settings/SettingsEnvironmentsRouteScreen";
57import { SettingsLegalRouteScreen } from "./features/settings/SettingsLegalRouteScreen";57import { SettingsLegalRouteScreen } from "./features/settings/SettingsLegalRouteScreen";
58import {
59 SettingsOpenSourceLicenseRouteScreen,
60 SettingsOpenSourceLicensesRouteScreen,
61} from "./features/settings/SettingsOpenSourceLicensesRouteScreen";
58import { SettingsProjectGroupingRouteScreen } from "./features/settings/SettingsProjectGroupingRouteScreen";62import { SettingsProjectGroupingRouteScreen } from "./features/settings/SettingsProjectGroupingRouteScreen";
59import { UsageRouteScreen } from "./features/usage/UsageRouteScreen";63import { UsageRouteScreen } from "./features/usage/UsageRouteScreen";
60import { SettingsRouteScreen } from "./features/settings/SettingsRouteScreen";64import { SettingsRouteScreen } from "./features/settings/SettingsRouteScreen";
@@ -191,6 +195,20 @@ const SettingsContentStack = createNativeStackNavigator({ · 130 unmodified lines
191 title: "Client Storage",195 title: "Client Storage",
192 },196 },
193 }),197 }),
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 }),
194 SettingsUsage: createNativeStackScreen({212 SettingsUsage: createNativeStackScreen({
195 screen: UsageRouteScreen,213 screen: UsageRouteScreen,
196 linking: "usage",214 linking: "usage",
apps/mobile/src/features/settings/SettingsOpenSourceLicensesRouteScreen.tsx+212−0
@@ -0,0 +1,212 @@
1import { LegendList } from "@legendapp/list/react-native";
2import { type StaticScreenProps, useNavigation } from "@react-navigation/native";
3import {
4 filterThirdPartyLicenseEntries,
5 findThirdPartyLicenseEntry,
6 formatLicenseBundles,
7 thirdPartyLicenseEntryKey,
8 type ThirdPartyLicenseEntry,
9} from "@t3tools/shared/thirdPartyLicenses";
10import { useCallback, useMemo, useState } from "react";
11import { Linking, Platform, Pressable, ScrollView, View } from "react-native";
12import { useSafeAreaInsets } from "react-native-safe-area-context";
13
14import { AndroidScreenHeader } from "../../components/AndroidScreenHeader";
15import { SymbolView } from "../../components/AppSymbol";
16import { AppText as Text, AppTextInput as TextInput } from "../../components/AppText";
17import { NativeStackScreenOptions } from "../../native/StackHeader";
18import { MOBILE_THIRD_PARTY_LICENSES } from "./mobileThirdPartyLicenses";
19
20function 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
54export 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
134type LicenseDetailProps = StaticScreenProps<{ readonly entryKey: string }>;
135
136export 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
670 return (670 return (
671 <SettingsSection title="App">671 <SettingsSection title="App">
672 <SettingsRow icon="internaldrive" label="Client Storage" target="SettingsClientStorage" />672 <SettingsRow icon="internaldrive" label="Client Storage" target="SettingsClientStorage" />
673 <SettingsRow
674 icon="doc.on.doc"
675 label="Open source licenses"
676 target="SettingsOpenSourceLicenses"
677 />
673 <SettingsRow icon="doc.text" label="Legal" fullScreenTarget="SettingsLegal" />678 <SettingsRow icon="doc.text" label="Legal" fullScreenTarget="SettingsLegal" />
674 {updateCheckAvailable ? (679 {updateCheckAvailable ? (
675 <Pressable680 <Pressable
apps/mobile/src/features/settings/components/settings-sheet-targets.ts+1−0
@@ -4,6 +4,7 @@ export type SettingsSheetTarget = · 3 unmodified lines
4 | "SettingsAppearance"4 | "SettingsAppearance"
5 | "SettingsProjectGrouping"5 | "SettingsProjectGrouping"
6 | "SettingsClientStorage"6 | "SettingsClientStorage"
7 | "SettingsOpenSourceLicenses"
7 | "SettingsUsage";8 | "SettingsUsage";
8 9
9export type SettingsLegalDocumentTarget = "SettingsLegal";10export type SettingsLegalDocumentTarget = "SettingsLegal";
apps/mobile/src/features/settings/mobileThirdPartyLicenses.ts+4−0
@@ -0,0 +1,4 @@
1import generatedManifest from "@t3tools/mobile-third-party-licenses";
2import { decodeThirdPartyLicenseManifest } from "@t3tools/shared/thirdPartyLicenses";
3
4export const MOBILE_THIRD_PARTY_LICENSES = decodeThirdPartyLicenseManifest(generatedManifest);
apps/mobile/src/types/mobile-third-party-licenses.d.ts+4−0
@@ -0,0 +1,4 @@
1declare 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 Notices1vscode-icons
2 2
3## vscode-icons3The custom file icon symbols bundled with T3 Code are adapted from the
4 4vscode-icons project: https://github.com/vscode-icons/vscode-icons
5The custom file icon symbols in `src/pierre-icons.ts` are adapted from the
6[`vscode-icons`](https://github.com/vscode-icons/vscode-icons) project.
7 5
8Copyright (c) 2016 Roberto Huertas6Copyright (c) 2016 Roberto Huertas
9 7
10Licensed under the MIT License. The full license text is available in the8Permission is hereby granted, free of charge, to any person obtaining a copy
11upstream repository: <https://github.com/vscode-icons/vscode-icons/blob/master/LICENSE>.9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14
15The above copyright notice and this permission notice shall be included in all
16copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24SOFTWARE.
apps/web/licenses/electron-internal-extract-zip.txt+25−0
@@ -0,0 +1,25 @@
1BSD 2-Clause License
2
3Copyright (c) 2026 Samuel Attard and Electron contributors
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
8
91. Redistributions of source code must retain the above copyright notice,
10 this list of conditions and the following disclaimer.
11
122. 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
apps/web/licenses/glob-to-regexp.txt+24−0
@@ -0,0 +1,24 @@
1Copyright (c) 2013, Nick Fitzgerald
2
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without modification,
6are 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
15THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
19ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22ANY 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
24SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
apps/web/licenses/isarray.txt+21−0
@@ -0,0 +1,21 @@
1(MIT)
2
3Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
4
5Permission is hereby granted, free of charge, to any person obtaining a copy of
6this software and associated documentation files (the "Software"), to deal in
7the Software without restriction, including without limitation the rights to
8use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9of the Software, and to permit persons to whom the Software is furnished to do
10so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
apps/web/licenses/keyv.txt+22−0
@@ -0,0 +1,22 @@
1MIT License
2
3Copyright (c) 2017-2021 Luke Childs
4Copyright (c) 2021-2022 Jared Wray
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
apps/web/licenses/kubernetes-types.txt+176−0
@@ -0,0 +1,176 @@
1Apache 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 @@
1MIT License
2
3Copyright (c) Vladimir Krivosheev
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
apps/web/licenses/lru-map.txt+19−0
@@ -0,0 +1,19 @@
1Copyright (c) 2010-2016 Rasmus Andersson <https://rsms.me/>
2
3Permission is hereby granted, free of charge, to any person obtaining a copy
4of this software and associated documentation files (the "Software"), to deal
5in the Software without restriction, including without limitation the rights
6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7copies of the Software, and to permit persons to whom the Software is
8furnished to do so, subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in
11all copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19THE SOFTWARE.
apps/web/licenses/msgpackr-extract-linux-x64.txt+21−0
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) 2020 Kris Zyp
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
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
5TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
71. 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
652. 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
723. 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
874. 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
1235. 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
1306. 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
1357. 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
1448. 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
1559. 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
165END OF TERMS AND CONDITIONS
166
167APPENDIX: 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
178Copyright 2025 Pierre Computer Company
179
180Licensed under the Apache License, Version 2.0 (the "License"); you may not use
181this file except in compliance with the License. You may obtain a copy of the
182License at
183
184 http://www.apache.org/licenses/LICENSE-2.0
185
186Unless required by applicable law or agreed to in writing, software distributed
187under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
188CONDITIONS OF ANY KIND, either express or implied. See the License for the
189specific language governing permissions and limitations under the License.
apps/web/licenses/react-grab-cli.txt+21−0
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) 2025 Aiden Bai
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
apps/web/licenses/type-fest.txt+9−0
@@ -0,0 +1,9 @@
1MIT License
2
3Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
4
5Permission 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
7The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
9THE 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 @@
1import { ChevronRightIcon, ExternalLinkIcon, SearchIcon } from "lucide-react";
2import { useCallback, useEffect, useMemo, useState } from "react";
3
4import {
5 decodeThirdPartyLicenseManifest,
6 filterThirdPartyLicenseEntries,
7 formatLicenseBundles,
8 type ThirdPartyLicenseEntry,
9 type ThirdPartyLicenseManifest,
10} from "@t3tools/shared/thirdPartyLicenses";
11
12import { cn } from "../../lib/utils";
13import { Button } from "../ui/button";
14import { Input } from "../ui/input";
15import { SettingsPageContainer } from "./settingsLayout";
16
17type LicenseManifestState =
18 | { readonly status: "loading" }
19 | { readonly status: "error"; readonly message: string }
20 | { readonly status: "ready"; readonly manifest: ThirdPartyLicenseManifest };
21
22async 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
33function 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
99function 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
117export 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
8const SETTINGS_BREADCRUMB_LABELS: Readonly<Record<string, string>> = {8const SETTINGS_BREADCRUMB_LABELS: Readonly<Record<string, string>> = {
9 ...SETTINGS_SECTION_LABELS,9 ...SETTINGS_SECTION_LABELS,
10 "/settings/diagnostics": "Diagnostics",10 "/settings/diagnostics": "Diagnostics",
11 "/settings/open-source-licenses": "Open source licenses",
11};12};
12 13
13function settingsBreadcrumbLabel(pathname: string): string | null {14function settingsBreadcrumbLabel(pathname: string): string | null {
apps/web/src/components/settings/SettingsPanels.tsx+13−0
@@ -2523,6 +2523,19 @@ export function GeneralSettingsPanel() { · 2522 unmodified lines
2523 </Button>2523 </Button>
2524 }2524 }
2525 />2525 />
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 />
2526 </SettingsSection>2539 </SettingsSection>
2527 2540
2528 <LegacyFeaturesSection />2541 <LegacyFeaturesSection />
apps/web/src/components/settings/SettingsSidebarNav.tsx+8−1
@@ -267,7 +267,14 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) { · 266 unmodified lines
267 ))267 ))
268 : SETTINGS_NAV_ITEMS.map((item) => {268 : SETTINGS_NAV_ITEMS.map((item) => {
269 const Icon = item.icon;269 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}/`);
271 return (278 return (
272 <SidebarMenuItem key={item.to}>279 <SidebarMenuItem key={item.to}>
273 <SidebarMenuButton280 <SidebarMenuButton
apps/web/src/components/settings/settingsSearch.ts+5−0
@@ -188,6 +188,11 @@ export const SETTINGS_SEARCH_ITEMS = [ · 187 unmodified lines
188 title: "Diagnostics",188 title: "Diagnostics",
189 to: "/settings/general",189 to: "/settings/general",
190 },190 },
191 {
192 id: "open-source-licenses",
193 title: "Open source licenses",
194 to: "/settings/general",
195 },
191 {196 {
192 id: "legacy-plan-mode",197 id: "legacy-plan-mode",
193 title: "Plan mode (legacy)",198 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
17import { Route as ChatIndexRouteImport } from './routes/_chat.index'17import { Route as ChatIndexRouteImport } from './routes/_chat.index'
18import { Route as SettingsSourceControlRouteImport } from './routes/settings.source-control'18import { Route as SettingsSourceControlRouteImport } from './routes/settings.source-control'
19import { Route as SettingsProvidersRouteImport } from './routes/settings.providers'19import { Route as SettingsProvidersRouteImport } from './routes/settings.providers'
20import { Route as SettingsOpenSourceLicensesRouteImport } from './routes/settings.open-source-licenses'
20import { Route as SettingsKeybindingsRouteImport } from './routes/settings.keybindings'21import { Route as SettingsKeybindingsRouteImport } from './routes/settings.keybindings'
21import { Route as SettingsIntegrationsRouteImport } from './routes/settings.integrations'22import { Route as SettingsIntegrationsRouteImport } from './routes/settings.integrations'
22import { Route as SettingsGeneralRouteImport } from './routes/settings.general'23import { Route as SettingsGeneralRouteImport } from './routes/settings.general'
@@ -69,6 +70,12 @@ const SettingsProvidersRoute = SettingsProvidersRouteImport.update({ · 46 unmodified lines
69 path: '/providers',70 path: '/providers',
70 getParentRoute: () => SettingsRoute,71 getParentRoute: () => SettingsRoute,
71} as any)72} as any)
73const SettingsOpenSourceLicensesRoute =
74 SettingsOpenSourceLicensesRouteImport.update({
75 id: '/open-source-licenses',
76 path: '/open-source-licenses',
77 getParentRoute: () => SettingsRoute,
78 } as any)
72const SettingsKeybindingsRoute = SettingsKeybindingsRouteImport.update({79const SettingsKeybindingsRoute = SettingsKeybindingsRouteImport.update({
73 id: '/keybindings',80 id: '/keybindings',
74 path: '/keybindings',81 path: '/keybindings',
@@ -147,6 +154,7 @@ export interface FileRoutesByFullPath { · 72 unmodified lines
147 '/settings/general': typeof SettingsGeneralRoute154 '/settings/general': typeof SettingsGeneralRoute
148 '/settings/integrations': typeof SettingsIntegrationsRoute155 '/settings/integrations': typeof SettingsIntegrationsRoute
149 '/settings/keybindings': typeof SettingsKeybindingsRoute156 '/settings/keybindings': typeof SettingsKeybindingsRoute
157 '/settings/open-source-licenses': typeof SettingsOpenSourceLicensesRoute
150 '/settings/providers': typeof SettingsProvidersRoute158 '/settings/providers': typeof SettingsProvidersRoute
151 '/settings/source-control': typeof SettingsSourceControlRoute159 '/settings/source-control': typeof SettingsSourceControlRoute
152 '/$environmentId/$threadId': typeof ChatEnvironmentIdThreadIdRoute160 '/$environmentId/$threadId': typeof ChatEnvironmentIdThreadIdRoute
@@ -167,6 +175,7 @@ export interface FileRoutesByTo { · 14 unmodified lines
167 '/settings/general': typeof SettingsGeneralRoute175 '/settings/general': typeof SettingsGeneralRoute
168 '/settings/integrations': typeof SettingsIntegrationsRoute176 '/settings/integrations': typeof SettingsIntegrationsRoute
169 '/settings/keybindings': typeof SettingsKeybindingsRoute177 '/settings/keybindings': typeof SettingsKeybindingsRoute
178 '/settings/open-source-licenses': typeof SettingsOpenSourceLicensesRoute
170 '/settings/providers': typeof SettingsProvidersRoute179 '/settings/providers': typeof SettingsProvidersRoute
171 '/settings/source-control': typeof SettingsSourceControlRoute180 '/settings/source-control': typeof SettingsSourceControlRoute
172 '/': typeof ChatIndexRoute181 '/': typeof ChatIndexRoute
@@ -190,6 +199,7 @@ export interface FileRoutesById { · 17 unmodified lines
190 '/settings/general': typeof SettingsGeneralRoute199 '/settings/general': typeof SettingsGeneralRoute
191 '/settings/integrations': typeof SettingsIntegrationsRoute200 '/settings/integrations': typeof SettingsIntegrationsRoute
192 '/settings/keybindings': typeof SettingsKeybindingsRoute201 '/settings/keybindings': typeof SettingsKeybindingsRoute
202 '/settings/open-source-licenses': typeof SettingsOpenSourceLicensesRoute
193 '/settings/providers': typeof SettingsProvidersRoute203 '/settings/providers': typeof SettingsProvidersRoute
194 '/settings/source-control': typeof SettingsSourceControlRoute204 '/settings/source-control': typeof SettingsSourceControlRoute
195 '/_chat/': typeof ChatIndexRoute205 '/_chat/': typeof ChatIndexRoute
@@ -214,6 +224,7 @@ export interface FileRouteTypes { · 18 unmodified lines
214 | '/settings/general'224 | '/settings/general'
215 | '/settings/integrations'225 | '/settings/integrations'
216 | '/settings/keybindings'226 | '/settings/keybindings'
227 | '/settings/open-source-licenses'
217 | '/settings/providers'228 | '/settings/providers'
218 | '/settings/source-control'229 | '/settings/source-control'
219 | '/$environmentId/$threadId'230 | '/$environmentId/$threadId'
@@ -234,6 +245,7 @@ export interface FileRouteTypes { · 14 unmodified lines
234 | '/settings/general'245 | '/settings/general'
235 | '/settings/integrations'246 | '/settings/integrations'
236 | '/settings/keybindings'247 | '/settings/keybindings'
248 | '/settings/open-source-licenses'
237 | '/settings/providers'249 | '/settings/providers'
238 | '/settings/source-control'250 | '/settings/source-control'
239 | '/'251 | '/'
@@ -256,6 +268,7 @@ export interface FileRouteTypes { · 16 unmodified lines
256 | '/settings/general'268 | '/settings/general'
257 | '/settings/integrations'269 | '/settings/integrations'
258 | '/settings/keybindings'270 | '/settings/keybindings'
271 | '/settings/open-source-licenses'
259 | '/settings/providers'272 | '/settings/providers'
260 | '/settings/source-control'273 | '/settings/source-control'
261 | '/_chat/'274 | '/_chat/'
@@ -331,6 +344,13 @@ declare module '@tanstack/react-router' { · 69 unmodified lines
331 preLoaderRoute: typeof SettingsProvidersRouteImport344 preLoaderRoute: typeof SettingsProvidersRouteImport
332 parentRoute: typeof SettingsRoute345 parentRoute: typeof SettingsRoute
333 }346 }
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 }
334 '/settings/keybindings': {354 '/settings/keybindings': {
335 id: '/settings/keybindings'355 id: '/settings/keybindings'
336 path: '/keybindings'356 path: '/keybindings'
@@ -442,6 +462,7 @@ interface SettingsRouteChildren { · 105 unmodified lines
442 SettingsGeneralRoute: typeof SettingsGeneralRoute462 SettingsGeneralRoute: typeof SettingsGeneralRoute
443 SettingsIntegrationsRoute: typeof SettingsIntegrationsRoute463 SettingsIntegrationsRoute: typeof SettingsIntegrationsRoute
444 SettingsKeybindingsRoute: typeof SettingsKeybindingsRoute464 SettingsKeybindingsRoute: typeof SettingsKeybindingsRoute
465 SettingsOpenSourceLicensesRoute: typeof SettingsOpenSourceLicensesRoute
445 SettingsProvidersRoute: typeof SettingsProvidersRoute466 SettingsProvidersRoute: typeof SettingsProvidersRoute
446 SettingsSourceControlRoute: typeof SettingsSourceControlRoute467 SettingsSourceControlRoute: typeof SettingsSourceControlRoute
447}468}
@@ -454,6 +475,7 @@ const SettingsRouteChildren: SettingsRouteChildren = { · 6 unmodified lines
454 SettingsGeneralRoute: SettingsGeneralRoute,475 SettingsGeneralRoute: SettingsGeneralRoute,
455 SettingsIntegrationsRoute: SettingsIntegrationsRoute,476 SettingsIntegrationsRoute: SettingsIntegrationsRoute,
456 SettingsKeybindingsRoute: SettingsKeybindingsRoute,477 SettingsKeybindingsRoute: SettingsKeybindingsRoute,
478 SettingsOpenSourceLicensesRoute: SettingsOpenSourceLicensesRoute,
457 SettingsProvidersRoute: SettingsProvidersRoute,479 SettingsProvidersRoute: SettingsProvidersRoute,
458 SettingsSourceControlRoute: SettingsSourceControlRoute,480 SettingsSourceControlRoute: SettingsSourceControlRoute,
459}481}
apps/web/src/routes/settings.open-source-licenses.tsx+7−0
@@ -0,0 +1,7 @@
1import { createFileRoute } from "@tanstack/react-router";
2
3import { OpenSourceLicensesPanel } from "../components/settings/OpenSourceLicenses";
4
5export const Route = createFileRoute("/settings/open-source-licenses")({
6 component: OpenSourceLicensesPanel,
7});
apps/web/tsconfig.json+2−1
@@ -31,6 +31,7 @@ · 30 unmodified lines
31 "vercel.ts",31 "vercel.ts",
32 "test",32 "test",
33 "scripts/warm-dep-cache.ts",33 "scripts/warm-dep-cache.ts",
34 "../../scripts/lib/public-config.ts"34 "../../scripts/lib/public-config.ts",
35 "../../scripts/lib/third-party-licenses.ts"
35 ]36 ]
36}37}
apps/web/vite.config.ts+10−0
@@ -13,6 +13,7 @@ import pkg from "./package.json" with { type: "json" }; · 12 unmodified lines
13import { DEV_PROXIED_PATH_PREFIXES } from "@t3tools/shared/devProxy";13import { DEV_PROXIED_PATH_PREFIXES } from "@t3tools/shared/devProxy";
14 14
15import { loadRepoEnv } from "../../scripts/lib/public-config";15import { loadRepoEnv } from "../../scripts/lib/public-config";
16import { thirdPartyLicensesPlugin } from "../../scripts/lib/third-party-licenses";
16 17
17const repoEnv = loadRepoEnv();18const repoEnv = loadRepoEnv();
18Object.assign(process.env, repoEnv);19Object.assign(process.env, repoEnv);
@@ -156,6 +157,15 @@ export default defineConfig(() => { · 137 unmodified lines
156 assetsInclude: ["**/*.wasm"],157 assetsInclude: ["**/*.wasm"],
157 plugins: [158 plugins: [
158 devCompressionPlugin(),159 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 }),
159 tanstackRouter(),169 tanstackRouter(),
160 react(),170 react(),
161 babel({171 babel({
docs/internals/open-source-licenses.md+84−0
@@ -0,0 +1,84 @@
1# Open source license notices
2
3License 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
12Neither path depends on the connected environment or an RPC.
13
14## What the build collects
15
16The generator follows installed production and optional dependencies, including dependencies of
17workspace packages, and omits first-party `@t3tools/*` packages. The web manifest starts from the
18web, server, and desktop package manifests. The mobile manifest starts from the mobile package
19manifest. During the web bundle, the generator also checks emitted module ids to catch a bundled
20npm import missing from a package manifest.
21
22The mobile manifest deliberately follows the complete production dependency closure declared by
23Expo and React Native. That is conservative and can include build tooling that is not present in
24the final JavaScript bundle, but it avoids dropping a notice when platform bundling changes.
25
26The build fails when a collected package has no distributable license identifier or contains no
27license or notice file. This turns missing attribution into a build error instead of an empty row
28in a release.
29
30## Custom notices and package overrides
31
32The repository-level `third-party-licenses.config.json` holds manually maintained exceptions for
33all clients. Add an entry to `customNotices` for adapted icons, fonts, media, native modules, or
34another 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
46Paths in `noticeFile` are relative to the config file. The notice file should contain the complete
47copyright, attribution, and license text required for redistribution. `bundles` controls which
48generated manifests include the entry; omit it only when the notice belongs in every client.
49
50Use `packageOverrides` only when an installed npm archive omits its notice or has incorrect
51metadata:
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
64installed version of that package. An override can use `repositoryUrl` instead of `name` when
65several 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
74The generator also reuses an installed sibling package's notice when both packages declare the
75same normalized repository and license. A name-and-version override always wins over these
76repository fallbacks.
77
78The `@react-grab/cli` override uses the root React Grab repository's MIT license because the CLI's
79npm archive omits both its license field and license file. Keep the override until the published
80CLI package carries that metadata itself.
81
82Generated mobile files live under `apps/mobile/.generated/` and are ignored. Do not commit or edit
83them; updating dependencies, configuration, or a notice file is enough for the next Metro startup
84to refresh the manifest.
docs/user/open-source-licenses.md+12−0
@@ -0,0 +1,12 @@
1# Open source licenses
2
3T3 Code includes third-party software and adapted assets. To read their license and attribution
4notices:
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
10The page lists the package version, license identifier, and the parts of T3 Code that may include
11it. Select a row to read the complete notice text. Use the search field to find a package, version,
12license, or app component.
packages/shared/package.json+4−0
@@ -231,6 +231,10 @@ · 230 unmodified lines
231 "types": "./src/usageFormat.ts",231 "types": "./src/usageFormat.ts",
232 "import": "./src/usageFormat.ts"232 "import": "./src/usageFormat.ts"
233 },233 },
234 "./thirdPartyLicenses": {
235 "types": "./src/thirdPartyLicenses.ts",
236 "import": "./src/thirdPartyLicenses.ts"
237 },
234 "./claudeCompaction": {238 "./claudeCompaction": {
235 "types": "./src/claudeCompaction.ts",239 "types": "./src/claudeCompaction.ts",
236 "import": "./src/claudeCompaction.ts"240 "import": "./src/claudeCompaction.ts"
packages/shared/src/thirdPartyLicenses.test.ts+63−0
@@ -0,0 +1,63 @@
1import { describe, expect, it } from "vite-plus/test";
2
3import {
4 decodeThirdPartyLicenseManifest,
5 filterThirdPartyLicenseEntries,
6 findThirdPartyLicenseEntry,
7 formatLicenseBundles,
8 thirdPartyLicenseEntryKey,
9 type ThirdPartyLicenseEntry,
10} from "./thirdPartyLicenses.js";
11
12const 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
33describe("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 @@
1export 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
11export interface ThirdPartyLicenseManifest {
12 readonly schemaVersion: 1;
13 readonly entries: ReadonlyArray<ThirdPartyLicenseEntry>;
14}
15
16function isRecord(value: unknown): value is Record<string, unknown> {
17 return typeof value === "object" && value !== null && !Array.isArray(value);
18}
19
20function isStringArray(value: unknown): value is ReadonlyArray<string> {
21 return Array.isArray(value) && value.every((entry) => typeof entry === "string");
22}
23
24function 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
51export 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
61export 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
80const 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
90export function formatLicenseBundles(bundles: ReadonlyArray<string>): string {
91 return bundles.map((bundle) => BUNDLE_LABELS[bundle] ?? bundle).join(", ");
92}
93
94export function thirdPartyLicenseEntryKey(entry: ThirdPartyLicenseEntry): string {
95 return `${entry.kind}:${entry.name}:${entry.version ?? "custom"}`;
96}
97
98export 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
3import * as NodeFSP from "node:fs/promises";
4import * as NodeOS from "node:os";
5import * as NodePath from "node:path";
6
7import { afterEach, describe, expect, it } from "vite-plus/test";
8
9import { generateThirdPartyLicenseManifest } from "./third-party-licenses.js";
10
11const tempDirectories: string[] = [];
12
13async 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
18async 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
62afterEach(async () => {
63 await Promise.all(
64 tempDirectories
65 .splice(0)
66 .map((directory) => NodeFSP.rm(directory, { force: true, recursive: true })),
67 );
68});
69
70describe("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
3import * as NodeFSP from "node:fs/promises";
4import * as NodePath from "node:path";
5import * as NodeURL from "node:url";
6import * as NodeModule from "node:module";
7
8import type { Plugin } from "vite-plus";
9
10export const THIRD_PARTY_LICENSES_FILE_NAME = "third-party-licenses.json";
11
12export 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
22export interface ThirdPartyLicenseManifest {
23 readonly schemaVersion: 1;
24 readonly entries: ReadonlyArray<ThirdPartyLicenseEntry>;
25}
26
27export interface ThirdPartyLicensePackageManifest {
28 readonly bundle: string;
29 readonly path: string | URL;
30}
31
32export interface ThirdPartyLicensesPluginOptions {
33 readonly configFile?: string | URL;
34 readonly packageManifests: ReadonlyArray<ThirdPartyLicensePackageManifest>;
35 readonly bundleName: string;
36}
37
38interface 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
49interface 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
58interface 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
67interface ThirdPartyLicensesConfig {
68 readonly customNotices: ReadonlyArray<CustomNoticeConfigEntry>;
69 readonly packageOverrides: ReadonlyArray<PackageNoticeOverrideConfigEntry>;
70}
71
72interface CollectedPackage {
73 readonly bundles: Set<string>;
74 readonly packageJson: PackageJson;
75 readonly packageRoot: string;
76}
77
78interface PackageCollection {
79 readonly byIdentity: Map<string, CollectedPackage>;
80}
81
82const EMPTY_CONFIG: ThirdPartyLicensesConfig = {
83 customNotices: [],
84 packageOverrides: [],
85};
86
87const NOTICE_FILE_PATTERN = /^(?:licen[cs]e|copying|notice)(?:[._-].*)?$/i;
88const FIRST_PARTY_PACKAGE_PREFIX = "@t3tools/";
89
90function asPath(value: string | URL): string {
91 return value instanceof URL ? NodeURL.fileURLToPath(value) : NodePath.resolve(value);
92}
93
94function isRecord(value: unknown): value is Record<string, unknown> {
95 return typeof value === "object" && value !== null && !Array.isArray(value);
96}
97
98function 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
106function 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
119function 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
136function 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
158function 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
186async 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
206async 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
213function 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
220async 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
247async 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
266function 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
275async 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
319function 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
329async 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
361function 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
381function 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
402function 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
409async 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
461function 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
468async 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
486function 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
497function 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
519async 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
569async 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
603function 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
611export 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
655function 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
665function serializeManifest(manifest: ThirdPartyLicenseManifest): string {
666 return `${JSON.stringify(manifest)}\n`;
667}
668
669export 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