@buoy-gg/shared-ui 3.0.2 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/hooks/safe-area-impl.js +1 -1
- package/lib/commonjs/icons/lucide-icons.js +28 -2
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/license/FeatureGate.js +60 -11
- package/lib/commonjs/license/LicenseEntryModal.js +12 -2
- package/lib/commonjs/license/openPricing.js +36 -0
- package/lib/commonjs/storage/devToolsStorageKeys.js +1 -0
- package/lib/commonjs/stores/ignoredPatternsStore.js +229 -0
- package/lib/commonjs/stores/index.js +26 -1
- package/lib/commonjs/ui/components/CompactRow.js +14 -4
- package/lib/commonjs/ui/components/ExpandedInfoRow.js +13 -3
- package/lib/commonjs/utils/index.js +6 -0
- package/lib/commonjs/utils/safeExpoRouter.js +59 -4
- package/lib/module/hooks/safe-area-impl.js +1 -1
- package/lib/module/icons/lucide-icons.js +25 -0
- package/lib/module/index.js +1 -1
- package/lib/module/license/FeatureGate.js +61 -12
- package/lib/module/license/LicenseEntryModal.js +13 -3
- package/lib/module/license/openPricing.js +31 -0
- package/lib/module/storage/devToolsStorageKeys.js +1 -0
- package/lib/module/stores/ignoredPatternsStore.js +223 -0
- package/lib/module/stores/index.js +2 -1
- package/lib/module/ui/components/CompactRow.js +14 -4
- package/lib/module/ui/components/ExpandedInfoRow.js +13 -3
- package/lib/module/utils/index.js +1 -1
- package/lib/module/utils/safeExpoRouter.js +58 -4
- package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/commonjs/icons/lucide-icons.d.ts +1 -0
- package/lib/typescript/commonjs/icons/lucide-icons.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/license/FeatureGate.d.ts +14 -1
- package/lib/typescript/commonjs/license/FeatureGate.d.ts.map +1 -1
- package/lib/typescript/commonjs/license/LicenseEntryModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/license/openPricing.d.ts +14 -0
- package/lib/typescript/commonjs/license/openPricing.d.ts.map +1 -0
- package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts +1 -0
- package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts.map +1 -1
- package/lib/typescript/commonjs/stores/ignoredPatternsStore.d.ts +84 -0
- package/lib/typescript/commonjs/stores/ignoredPatternsStore.d.ts.map +1 -0
- package/lib/typescript/commonjs/stores/index.d.ts +1 -0
- package/lib/typescript/commonjs/stores/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/CompactRow.d.ts +3 -1
- package/lib/typescript/commonjs/ui/components/CompactRow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ExpandedInfoRow.d.ts +3 -1
- package/lib/typescript/commonjs/ui/components/ExpandedInfoRow.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/index.d.ts +1 -1
- package/lib/typescript/commonjs/utils/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/safeExpoRouter.d.ts +9 -0
- package/lib/typescript/commonjs/utils/safeExpoRouter.d.ts.map +1 -1
- package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/module/icons/lucide-icons.d.ts +1 -0
- package/lib/typescript/module/icons/lucide-icons.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/license/FeatureGate.d.ts +14 -1
- package/lib/typescript/module/license/FeatureGate.d.ts.map +1 -1
- package/lib/typescript/module/license/LicenseEntryModal.d.ts.map +1 -1
- package/lib/typescript/module/license/openPricing.d.ts +14 -0
- package/lib/typescript/module/license/openPricing.d.ts.map +1 -0
- package/lib/typescript/module/storage/devToolsStorageKeys.d.ts +1 -0
- package/lib/typescript/module/storage/devToolsStorageKeys.d.ts.map +1 -1
- package/lib/typescript/module/stores/ignoredPatternsStore.d.ts +84 -0
- package/lib/typescript/module/stores/ignoredPatternsStore.d.ts.map +1 -0
- package/lib/typescript/module/stores/index.d.ts +1 -0
- package/lib/typescript/module/stores/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/CompactRow.d.ts +3 -1
- package/lib/typescript/module/ui/components/CompactRow.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ExpandedInfoRow.d.ts +3 -1
- package/lib/typescript/module/ui/components/ExpandedInfoRow.d.ts.map +1 -1
- package/lib/typescript/module/utils/index.d.ts +1 -1
- package/lib/typescript/module/utils/index.d.ts.map +1 -1
- package/lib/typescript/module/utils/safeExpoRouter.d.ts +9 -0
- package/lib/typescript/module/utils/safeExpoRouter.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -7,7 +7,7 @@ exports.useNativeSafeAreaInsets = exports.safeAreaType = exports.hasSafeAreaPack
|
|
|
7
7
|
/**
|
|
8
8
|
* Auto-generated safe area implementation
|
|
9
9
|
* Detected: none
|
|
10
|
-
* Generated at: 2026-06-
|
|
10
|
+
* Generated at: 2026-06-30T15:42:30.947Z
|
|
11
11
|
*
|
|
12
12
|
* DO NOT EDIT - This file is generated by scripts/detect-safe-area.js
|
|
13
13
|
*
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
7
|
-
exports.ZapIcon = exports.Zap = exports.XIcon = exports.XCircleIcon = exports.XCircle = exports.X = exports.WifiIcon = exports.Wifi = exports.VolumeIcon = exports.Volume = exports.UsersIcon = exports.Users = exports.UserIcon = exports.User = exports.UploadIcon = exports.Upload = exports.UnlockIcon = exports.Unlock = exports.TriangleAlertIcon = exports.TriangleAlert = exports.TrashIcon = exports.Trash2Icon = exports.Trash2 = exports.Trash = exports.TouchpadIcon = exports.Touchpad = exports.TimerIcon = exports.Timer = exports.TestTube2Icon = exports.TestTube2 = exports.SquareIcon = exports.SquareDashedIcon = exports.SquareDashed = exports.Square = exports.SmartphoneIcon = exports.Smartphone = exports.ShieldIcon = exports.Shield = exports.SettingsIcon = exports.Settings = exports.ServerIcon = exports.Server = exports.SearchIcon = exports.Search = exports.RouteIcon = exports.Route = exports.RefreshCwIcon = exports.RefreshCw = exports.PowerIcon = exports.Power = exports.PlusIcon = exports.Plus = void 0;
|
|
6
|
+
exports.Play = exports.Pin = exports.PhoneIcon = exports.Phone = exports.PauseIcon = exports.Pause = exports.PaletteIcon = exports.Palette = exports.NavigationIcon = exports.Navigation = exports.MusicIcon = exports.Music = exports.MinusIcon = exports.Minus = exports.Maximize2Icon = exports.Maximize2 = exports.LockIcon = exports.Lock = exports.LinkIcon = exports.Link = exports.LayersIcon = exports.Layers = exports.KeyIcon = exports.Key = exports.InfoIcon = exports.Info = exports.ImageIcon = exports.Image = exports.HomeIcon = exports.Home = exports.HelpCircle = exports.HashIcon = exports.Hash = exports.HardDriveIcon = exports.HardDrive = exports.HandIcon = exports.Hand = exports.GlobeIcon = exports.Globe = exports.GitBranchIcon = exports.GitBranch = exports.FloatWindow = exports.FlaskConicalIcon = exports.FlaskConical = exports.FilterIcon = exports.Filter = exports.FilmIcon = exports.Film = exports.FileTextIcon = exports.FileText = exports.FileJsonIcon = exports.FileJson = exports.FileCodeIcon = exports.FileCode = exports.EyeOffIcon = exports.EyeOff = exports.EyeIcon = exports.Eye = exports.Edit3Icon = exports.Edit3 = exports.DownloadIcon = exports.Download = exports.DockBottom = exports.DatabaseIcon = exports.Database = exports.CopyIcon = exports.Copy = exports.CloudIcon = exports.Cloud = exports.ClockIcon = exports.Clock = exports.ChevronUpIcon = exports.ChevronUp = exports.ChevronRightIcon = exports.ChevronRight = exports.ChevronLeftIcon = exports.ChevronLeft = exports.ChevronDownIcon = exports.ChevronDown = exports.CheckSquareIcon = exports.CheckSquare = exports.CheckIcon = exports.CheckCircleIcon = exports.CheckCircle2Icon = exports.CheckCircle2 = exports.CheckCircle = exports.Check = exports.BugIcon = exports.Bug = exports.BoxIcon = exports.Box = exports.BarChart3Icon = exports.BarChart3 = exports.AlertTriangleIcon = exports.AlertTriangle = exports.AlertOctagon = exports.AlertCircleIcon = exports.AlertCircle = exports.ActivityIcon = exports.Activity = void 0;
|
|
7
|
+
exports.ZapIcon = exports.Zap = exports.XIcon = exports.XCircleIcon = exports.XCircle = exports.X = exports.WifiIcon = exports.Wifi = exports.VolumeIcon = exports.Volume = exports.UsersIcon = exports.Users = exports.UserIcon = exports.User = exports.UploadIcon = exports.Upload = exports.UnlockIcon = exports.Unlock = exports.TriangleAlertIcon = exports.TriangleAlert = exports.TrashIcon = exports.Trash2Icon = exports.Trash2 = exports.Trash = exports.TouchpadIcon = exports.Touchpad = exports.TimerIcon = exports.Timer = exports.TestTube2Icon = exports.TestTube2 = exports.SquareIcon = exports.SquareDashedIcon = exports.SquareDashed = exports.Square = exports.SmartphoneIcon = exports.Smartphone = exports.ShieldIcon = exports.Shield = exports.SettingsIcon = exports.Settings = exports.ServerIcon = exports.Server = exports.SearchIcon = exports.Search = exports.RouteIcon = exports.Route = exports.RefreshCwIcon = exports.RefreshCw = exports.PowerIcon = exports.Power = exports.PlusIcon = exports.Plus = exports.PlayIcon = void 0;
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var OriginalIcons = _interopRequireWildcard(require("./lucide-icons-original-full.js"));
|
|
10
10
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
@@ -524,6 +524,32 @@ const Eye = ({
|
|
|
524
524
|
})]
|
|
525
525
|
});
|
|
526
526
|
exports.Eye = Eye;
|
|
527
|
+
const Pin = ({
|
|
528
|
+
size = 24,
|
|
529
|
+
color = "currentColor",
|
|
530
|
+
strokeWidth = 2,
|
|
531
|
+
...props
|
|
532
|
+
}) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(Svg, {
|
|
533
|
+
width: size,
|
|
534
|
+
height: size,
|
|
535
|
+
viewBox: "0 0 24 24",
|
|
536
|
+
...props,
|
|
537
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(Circle, {
|
|
538
|
+
cx: 12,
|
|
539
|
+
cy: 9,
|
|
540
|
+
r: 5,
|
|
541
|
+
stroke: color,
|
|
542
|
+
strokeWidth: strokeWidth
|
|
543
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(Line, {
|
|
544
|
+
x1: 12,
|
|
545
|
+
y1: 14,
|
|
546
|
+
x2: 12,
|
|
547
|
+
y2: 21,
|
|
548
|
+
stroke: color,
|
|
549
|
+
strokeWidth: strokeWidth
|
|
550
|
+
})]
|
|
551
|
+
});
|
|
552
|
+
exports.Pin = Pin;
|
|
527
553
|
const EyeOff = ({
|
|
528
554
|
size = 24,
|
|
529
555
|
color = "currentColor",
|
package/lib/commonjs/index.js
CHANGED
|
@@ -36,6 +36,7 @@ var _exportNames = {
|
|
|
36
36
|
useSafeSegments: true,
|
|
37
37
|
useSafeGlobalSearchParams: true,
|
|
38
38
|
getSafeRouter: true,
|
|
39
|
+
getSafeCurrentPathname: true,
|
|
39
40
|
isExpoRouterAvailable: true,
|
|
40
41
|
isPlainObjectUtil: true,
|
|
41
42
|
useSafeAreaInsets: true,
|
|
@@ -216,6 +217,12 @@ Object.defineProperty(exports, "getSafeAreaInsets", {
|
|
|
216
217
|
return _index3.getSafeAreaInsets;
|
|
217
218
|
}
|
|
218
219
|
});
|
|
220
|
+
Object.defineProperty(exports, "getSafeCurrentPathname", {
|
|
221
|
+
enumerable: true,
|
|
222
|
+
get: function () {
|
|
223
|
+
return _index3.getSafeCurrentPathname;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
219
226
|
Object.defineProperty(exports, "getSafeRouter", {
|
|
220
227
|
enumerable: true,
|
|
221
228
|
get: function () {
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.UpgradePrompt = exports.UpgradeModal = exports.ProUpgradeModal = exports.ProFeatureBanner = exports.ProBadge = exports.FeatureGate = void 0;
|
|
6
|
+
exports.WEEKEND_PASS_LABEL = exports.UpgradePrompt = exports.UpgradeModal = exports.ProUpgradeModal = exports.ProFeatureBanner = exports.ProBadge = exports.FeatureGate = void 0;
|
|
7
7
|
exports.useFeatureGate = useFeatureGate;
|
|
8
8
|
var _react = _interopRequireWildcard(require("react"));
|
|
9
9
|
var _reactNative = require("react-native");
|
|
10
10
|
var _gameUIColors = require("../ui/gameUI/constants/gameUIColors.js");
|
|
11
11
|
var _lucideIcons = require("../icons/lucide-icons.js");
|
|
12
12
|
var _LicenseEntryModal = require("./LicenseEntryModal.js");
|
|
13
|
+
var _openPricing = require("./openPricing.js");
|
|
13
14
|
var _license = require("@buoy-gg/license");
|
|
14
15
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
16
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
@@ -19,18 +20,33 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
19
20
|
* Wraps premium features and shows an upgrade prompt for free users.
|
|
20
21
|
*/
|
|
21
22
|
|
|
23
|
+
// Weekend Pass branding — a distinct violet so it never reads as the gold PRO.
|
|
24
|
+
const WEEKEND_PASS_LABEL = exports.WEEKEND_PASS_LABEL = "WEEKEND PASS";
|
|
25
|
+
const WEEKEND_VIOLET = "#BF5AF2";
|
|
26
|
+
|
|
22
27
|
/**
|
|
23
|
-
* Simple Pro badge for marking premium features
|
|
28
|
+
* Simple Pro badge for marking premium features.
|
|
29
|
+
*
|
|
30
|
+
* Reason-aware: when the free Weekend Pass is the active unlock (a free user on
|
|
31
|
+
* a weekend), it shows a violet "WEEKEND PASS" badge instead of the gold "PRO"
|
|
32
|
+
* so people know Pro is free this weekend. A real license — or a free user on a
|
|
33
|
+
* weekday seeing the badge as a Pro-feature marker — shows "PRO".
|
|
24
34
|
*/
|
|
25
35
|
const ProBadge = ({
|
|
26
36
|
style
|
|
27
|
-
}) =>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
}) => {
|
|
38
|
+
const {
|
|
39
|
+
reason
|
|
40
|
+
} = (0, _license.useProAccess)();
|
|
41
|
+
const weekend = reason === "weekend";
|
|
42
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
43
|
+
style: [styles.proBadge, weekend && styles.weekendBadge, style],
|
|
44
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
45
|
+
style: [styles.proBadgeText, weekend && styles.weekendBadgeText],
|
|
46
|
+
children: weekend ? WEEKEND_PASS_LABEL : "PRO"
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
};
|
|
34
50
|
|
|
35
51
|
/**
|
|
36
52
|
* Simple Pro upgrade modal - minimal modal with just upgrade button
|
|
@@ -63,6 +79,10 @@ const UpgradePrompt = ({
|
|
|
63
79
|
const handlePress = (0, _react.useCallback)(() => {
|
|
64
80
|
if (onUpgradePress) {
|
|
65
81
|
onUpgradePress();
|
|
82
|
+
} else if (_openPricing.isWeb) {
|
|
83
|
+
// Desktop dashboard: buy on the website, not via the mobile Buoy.init()
|
|
84
|
+
// code path. (Already have a key? Enter it from the toolbar Upgrade button.)
|
|
85
|
+
(0, _openPricing.openPricing)();
|
|
66
86
|
} else {
|
|
67
87
|
setShowLicenseModal(true);
|
|
68
88
|
}
|
|
@@ -190,13 +210,21 @@ const FeatureGate = ({
|
|
|
190
210
|
*/
|
|
191
211
|
exports.FeatureGate = FeatureGate;
|
|
192
212
|
function useFeatureGate() {
|
|
193
|
-
const
|
|
213
|
+
const {
|
|
214
|
+
isPro,
|
|
215
|
+
isLicensed,
|
|
216
|
+
isWeekendFree,
|
|
217
|
+
reason
|
|
218
|
+
} = (0, _license.useProAccess)();
|
|
194
219
|
const showUpgrade = () => {
|
|
195
|
-
|
|
220
|
+
(0, _openPricing.openPricing)("https://buoy.gg/pro");
|
|
196
221
|
};
|
|
197
222
|
return {
|
|
198
223
|
hasAccess: isPro,
|
|
199
224
|
isPro,
|
|
225
|
+
isLicensed,
|
|
226
|
+
isWeekendFree,
|
|
227
|
+
reason,
|
|
200
228
|
showUpgrade
|
|
201
229
|
};
|
|
202
230
|
}
|
|
@@ -214,6 +242,12 @@ const ProFeatureBanner = ({
|
|
|
214
242
|
const [showLicenseModal, setShowLicenseModal] = (0, _react.useState)(false);
|
|
215
243
|
const license = (0, _license.useLicense)();
|
|
216
244
|
const handleUpgrade = (0, _react.useCallback)(() => {
|
|
245
|
+
// Desktop dashboard: open pricing in the browser instead of the mobile
|
|
246
|
+
// license-entry modal.
|
|
247
|
+
if (_openPricing.isWeb) {
|
|
248
|
+
(0, _openPricing.openPricing)();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
217
251
|
setShowLicenseModal(true);
|
|
218
252
|
}, []);
|
|
219
253
|
const handleCloseModal = (0, _react.useCallback)(() => {
|
|
@@ -289,6 +323,12 @@ const UpgradeModal = ({
|
|
|
289
323
|
const [showLicenseModal, setShowLicenseModal] = (0, _react.useState)(false);
|
|
290
324
|
const license = (0, _license.useLicense)();
|
|
291
325
|
const handleUpgrade = (0, _react.useCallback)(() => {
|
|
326
|
+
// Desktop dashboard: send users straight to the pricing page in their
|
|
327
|
+
// browser instead of the mobile Buoy.init() license-entry modal.
|
|
328
|
+
if (_openPricing.isWeb) {
|
|
329
|
+
(0, _openPricing.openPricing)();
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
292
332
|
setShowLicenseModal(true);
|
|
293
333
|
}, []);
|
|
294
334
|
const handleCloseLicenseModal = (0, _react.useCallback)(() => {
|
|
@@ -424,6 +464,15 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
424
464
|
color: _gameUIColors.buoyColors.primary,
|
|
425
465
|
letterSpacing: 0.5
|
|
426
466
|
},
|
|
467
|
+
// Weekend Pass variant — violet instead of the gold/primary PRO.
|
|
468
|
+
weekendBadge: {
|
|
469
|
+
backgroundColor: WEEKEND_VIOLET + "1A",
|
|
470
|
+
borderColor: WEEKEND_VIOLET + "55"
|
|
471
|
+
},
|
|
472
|
+
weekendBadgeText: {
|
|
473
|
+
color: WEEKEND_VIOLET,
|
|
474
|
+
letterSpacing: 0.4
|
|
475
|
+
},
|
|
427
476
|
container: {
|
|
428
477
|
padding: 20,
|
|
429
478
|
alignItems: "center",
|
|
@@ -8,6 +8,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _absoluteFill = require("../utils/absoluteFill.js");
|
|
10
10
|
var _macOSDesignSystemColors = require("../ui/gameUI/constants/macOSDesignSystemColors.js");
|
|
11
|
+
var _openPricing = require("./openPricing.js");
|
|
11
12
|
var _lucideIcons = require("../icons/lucide-icons.js");
|
|
12
13
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
14
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
@@ -41,7 +42,7 @@ const LicenseEntryModal = ({
|
|
|
41
42
|
}) => {
|
|
42
43
|
const [copied, setCopied] = _react.default.useState(false);
|
|
43
44
|
const handleGetLicense = (0, _react.useCallback)(() => {
|
|
44
|
-
|
|
45
|
+
(0, _openPricing.openPricing)(purchaseUrl);
|
|
45
46
|
}, [purchaseUrl]);
|
|
46
47
|
const handleCopyCode = (0, _react.useCallback)(() => {
|
|
47
48
|
if (Clipboard?.setString) {
|
|
@@ -107,7 +108,16 @@ const LicenseEntryModal = ({
|
|
|
107
108
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
108
109
|
style: styles.dividerLine
|
|
109
110
|
})]
|
|
110
|
-
}), /*#__PURE__*/(0, _jsxRuntime.
|
|
111
|
+
}), _openPricing.isWeb ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
112
|
+
style: styles.instructionsContainer,
|
|
113
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
114
|
+
style: styles.instructionsNote,
|
|
115
|
+
children: "Click the Upgrade button in the top toolbar and paste your license key. It also unlocks automatically when a connected device is running Buoy Pro."
|
|
116
|
+
})
|
|
117
|
+
}) :
|
|
118
|
+
/*#__PURE__*/
|
|
119
|
+
/* Option 2: Instructions */
|
|
120
|
+
(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
111
121
|
style: styles.instructionsContainer,
|
|
112
122
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
113
123
|
style: styles.instructionsHeader,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isWeb = exports.PRICING_URL = void 0;
|
|
7
|
+
exports.openPricing = openPricing;
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
const PRICING_URL = exports.PRICING_URL = "https://buoy.gg/pricing";
|
|
10
|
+
|
|
11
|
+
/** True when running on the desktop dashboard / web (react-native-web). */
|
|
12
|
+
const isWeb = exports.isWeb = _reactNative.Platform.OS === "web";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Open an upgrade/pricing URL.
|
|
16
|
+
*
|
|
17
|
+
* On the desktop dashboard (web) the upgrade flow is NOT the mobile
|
|
18
|
+
* `Buoy.init()` code path — the user buys on the website (or enters a key in
|
|
19
|
+
* the toolbar). Route the link through the Electron shell bridge so it lands in
|
|
20
|
+
* the user's DEFAULT browser instead of a bare in-app window; fall back to
|
|
21
|
+
* window.open for a plain browser tab. On native, use React Native Linking.
|
|
22
|
+
*/
|
|
23
|
+
function openPricing(url = PRICING_URL) {
|
|
24
|
+
// Access the browser globals via globalThis so this file doesn't depend on
|
|
25
|
+
// the DOM lib (the shared package targets React Native).
|
|
26
|
+
const win = globalThis.window;
|
|
27
|
+
if (isWeb && win) {
|
|
28
|
+
if (win.buoyShell?.openExternal) {
|
|
29
|
+
win.buoyShell.openExternal(url);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
win.open?.(url, "_blank", "noopener");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
_reactNative.Linking.openURL(url);
|
|
36
|
+
}
|
|
@@ -98,6 +98,7 @@ const devToolsStorageKeys = exports.devToolsStorageKeys = {
|
|
|
98
98
|
filters: () => `${devToolsStorageKeys.storage.root()}_filters`,
|
|
99
99
|
eventFilters: () => `${devToolsStorageKeys.storage.root()}_event_filters`,
|
|
100
100
|
keyFilters: () => `${devToolsStorageKeys.storage.root()}_key_filters`,
|
|
101
|
+
pinnedKeys: () => `${devToolsStorageKeys.storage.root()}_pinned_keys`,
|
|
101
102
|
preferences: () => `${devToolsStorageKeys.storage.root()}_preferences`,
|
|
102
103
|
activeTab: () => `${devToolsStorageKeys.storage.root()}_active_tab`,
|
|
103
104
|
isMonitoring: () => `${devToolsStorageKeys.storage.root()}_is_monitoring`,
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ignoredPatternsStore = void 0;
|
|
7
|
+
exports.isUrlIgnored = isUrlIgnored;
|
|
8
|
+
exports.urlMatchesIgnoredPattern = urlMatchesIgnoredPattern;
|
|
9
|
+
exports.useIgnoredPatterns = useIgnoredPatterns;
|
|
10
|
+
var _react = require("react");
|
|
11
|
+
var _persistentStorage = require("../utils/persistentStorage.js");
|
|
12
|
+
var _devToolsStorageKeys = require("../storage/devToolsStorageKeys.js");
|
|
13
|
+
/**
|
|
14
|
+
* Shared "ignored patterns" store for network-style URL filtering.
|
|
15
|
+
*
|
|
16
|
+
* This is the SINGLE source of truth for the domains/URL patterns that the
|
|
17
|
+
* Network tool (and now the Events tool) hide from their lists. Both tools read
|
|
18
|
+
* and mutate this one singleton, so an ignore toggled in either place is shared
|
|
19
|
+
* everywhere and persisted to the same storage key.
|
|
20
|
+
*
|
|
21
|
+
* Lives in @buoy-gg/shared-ui (a hard dependency of both @buoy-gg/network and
|
|
22
|
+
* @buoy-gg/events) so the filter logic can be shared without @buoy-gg/events
|
|
23
|
+
* having to take a hard dependency on @buoy-gg/network (which is optional).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* How an ignored-pattern entry should be compared against captured URLs.
|
|
28
|
+
*
|
|
29
|
+
* - `contains`: case-insensitive substring match on the full URL (legacy behavior).
|
|
30
|
+
* - `exact`: smart equality — if pattern starts with `/`, matches URL.pathname;
|
|
31
|
+
* if it starts with `http://`/`https://`, matches origin+pathname; otherwise
|
|
32
|
+
* matches URL.host.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/** A single exclude pattern with its match mode. */
|
|
36
|
+
|
|
37
|
+
/** Patterns hidden by default — Buoy's own license API traffic. */
|
|
38
|
+
const DEFAULT_PATTERNS = [{
|
|
39
|
+
value: "api.keygen.sh",
|
|
40
|
+
mode: "contains"
|
|
41
|
+
}];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Returns true when `url` matches the ignored `pattern` according to its mode.
|
|
45
|
+
*
|
|
46
|
+
* `contains` → case-insensitive substring on the full URL (legacy behavior).
|
|
47
|
+
* `exact` → smart equality: pattern starting with `/` compares URL.pathname,
|
|
48
|
+
* full URLs compare origin+pathname (ignoring query/hash), bare
|
|
49
|
+
* values compare URL.host. Falls back to literal equality if URL
|
|
50
|
+
* parsing fails (e.g. relative URLs).
|
|
51
|
+
*/
|
|
52
|
+
function urlMatchesIgnoredPattern(url, pattern) {
|
|
53
|
+
const lowerUrl = url.toLowerCase();
|
|
54
|
+
const lowerValue = pattern.value.toLowerCase();
|
|
55
|
+
if (pattern.mode === "contains") {
|
|
56
|
+
return lowerUrl.includes(lowerValue);
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const parsed = new URL(url);
|
|
60
|
+
if (lowerValue.startsWith("/")) {
|
|
61
|
+
return parsed.pathname.toLowerCase() === lowerValue;
|
|
62
|
+
}
|
|
63
|
+
if (lowerValue.startsWith("http://") || lowerValue.startsWith("https://")) {
|
|
64
|
+
return `${parsed.origin}${parsed.pathname}`.toLowerCase() === lowerValue;
|
|
65
|
+
}
|
|
66
|
+
return parsed.host.toLowerCase() === lowerValue;
|
|
67
|
+
} catch {
|
|
68
|
+
return lowerUrl === lowerValue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Convenience: does `url` match ANY of the ignored `patterns`? */
|
|
73
|
+
function isUrlIgnored(url, patterns) {
|
|
74
|
+
if (!url || patterns.length === 0) return false;
|
|
75
|
+
return patterns.some(pattern => urlMatchesIgnoredPattern(url, pattern));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Tolerate the legacy `string[]` persisted format and stray junk. */
|
|
79
|
+
function migratePatterns(raw) {
|
|
80
|
+
if (!Array.isArray(raw)) return [];
|
|
81
|
+
const migrated = [];
|
|
82
|
+
for (const entry of raw) {
|
|
83
|
+
if (typeof entry === "string" && entry.trim()) {
|
|
84
|
+
migrated.push({
|
|
85
|
+
value: entry,
|
|
86
|
+
mode: "contains"
|
|
87
|
+
});
|
|
88
|
+
} else if (entry && typeof entry === "object" && typeof entry.value === "string") {
|
|
89
|
+
const e = entry;
|
|
90
|
+
migrated.push({
|
|
91
|
+
value: e.value,
|
|
92
|
+
mode: e.mode === "exact" ? "exact" : "contains"
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return migrated;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Always keep the default patterns present (e.g. Buoy's license API). */
|
|
100
|
+
function ensureDefaults(patterns) {
|
|
101
|
+
const result = [...patterns];
|
|
102
|
+
for (const def of DEFAULT_PATTERNS) {
|
|
103
|
+
if (!result.some(p => p.value === def.value)) {
|
|
104
|
+
result.push({
|
|
105
|
+
...def
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Singleton store. One instance per JS runtime, so every consumer (Network tool,
|
|
113
|
+
* Events tool, on mobile or on the desktop dashboard) shares the same patterns.
|
|
114
|
+
*/
|
|
115
|
+
class IgnoredPatternsStore {
|
|
116
|
+
patterns = ensureDefaults(DEFAULT_PATTERNS.map(p => ({
|
|
117
|
+
...p
|
|
118
|
+
})));
|
|
119
|
+
listeners = new Set();
|
|
120
|
+
loaded = false;
|
|
121
|
+
loadPromise = null;
|
|
122
|
+
/** Set once a mutation happens so a slow initial load can't clobber it. */
|
|
123
|
+
dirty = false;
|
|
124
|
+
|
|
125
|
+
/** Current patterns (stable reference until a mutation occurs). */
|
|
126
|
+
getPatterns() {
|
|
127
|
+
return this.patterns;
|
|
128
|
+
}
|
|
129
|
+
subscribe(listener) {
|
|
130
|
+
this.listeners.add(listener);
|
|
131
|
+
// Lazily hydrate from storage the first time anyone cares.
|
|
132
|
+
void this.ensureLoaded();
|
|
133
|
+
return () => {
|
|
134
|
+
this.listeners.delete(listener);
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
emit() {
|
|
138
|
+
this.listeners.forEach(l => l());
|
|
139
|
+
}
|
|
140
|
+
ensureLoaded() {
|
|
141
|
+
if (this.loaded) return Promise.resolve();
|
|
142
|
+
if (this.loadPromise) return this.loadPromise;
|
|
143
|
+
this.loadPromise = (async () => {
|
|
144
|
+
try {
|
|
145
|
+
const stored = await _persistentStorage.persistentStorage.getItem(_devToolsStorageKeys.devToolsStorageKeys.network.ignoredDomains());
|
|
146
|
+
// A mutation may have raced this read — never overwrite user intent.
|
|
147
|
+
if (stored && !this.dirty) {
|
|
148
|
+
const migrated = ensureDefaults(migratePatterns(JSON.parse(stored)));
|
|
149
|
+
this.patterns = migrated;
|
|
150
|
+
}
|
|
151
|
+
} catch {
|
|
152
|
+
// Silently fall back to defaults.
|
|
153
|
+
} finally {
|
|
154
|
+
this.loaded = true;
|
|
155
|
+
this.emit();
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
return this.loadPromise;
|
|
159
|
+
}
|
|
160
|
+
persist() {
|
|
161
|
+
_persistentStorage.persistentStorage.setItem(_devToolsStorageKeys.devToolsStorageKeys.network.ignoredDomains(), JSON.stringify(this.patterns)).catch(() => {
|
|
162
|
+
// Silently fail — patterns remain in memory.
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
commit(next) {
|
|
166
|
+
this.dirty = true;
|
|
167
|
+
this.patterns = next;
|
|
168
|
+
this.emit();
|
|
169
|
+
this.persist();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Add a pattern (no-op if its value already exists). */
|
|
173
|
+
add(pattern) {
|
|
174
|
+
const value = pattern.value.trim();
|
|
175
|
+
if (!value) return;
|
|
176
|
+
if (this.patterns.some(p => p.value === value)) return;
|
|
177
|
+
this.commit([...this.patterns, {
|
|
178
|
+
value,
|
|
179
|
+
mode: pattern.mode
|
|
180
|
+
}]);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Remove a pattern by value. */
|
|
184
|
+
remove(value) {
|
|
185
|
+
if (!this.patterns.some(p => p.value === value)) return;
|
|
186
|
+
this.commit(this.patterns.filter(p => p.value !== value));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Add as `contains` if absent, otherwise remove (used by detail-view chips). */
|
|
190
|
+
toggle(value) {
|
|
191
|
+
const trimmed = value.trim();
|
|
192
|
+
if (!trimmed) return;
|
|
193
|
+
this.commit(this.patterns.some(p => p.value === trimmed) ? this.patterns.filter(p => p.value !== trimmed) : [...this.patterns, {
|
|
194
|
+
value: trimmed,
|
|
195
|
+
mode: "contains"
|
|
196
|
+
}]);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Flip an existing pattern between `contains` and `exact`. */
|
|
200
|
+
toggleMode(value) {
|
|
201
|
+
if (!this.patterns.some(p => p.value === value)) return;
|
|
202
|
+
this.commit(this.patterns.map(p => p.value === value ? {
|
|
203
|
+
...p,
|
|
204
|
+
mode: p.mode === "contains" ? "exact" : "contains"
|
|
205
|
+
} : p));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const ignoredPatternsStore = exports.ignoredPatternsStore = new IgnoredPatternsStore();
|
|
209
|
+
/**
|
|
210
|
+
* Subscribe to the shared ignored-patterns store. All consumers in a runtime
|
|
211
|
+
* see the same patterns and the same mutations.
|
|
212
|
+
*/
|
|
213
|
+
function useIgnoredPatterns() {
|
|
214
|
+
const [patterns, setPatterns] = (0, _react.useState)(() => ignoredPatternsStore.getPatterns());
|
|
215
|
+
(0, _react.useEffect)(() => {
|
|
216
|
+
return ignoredPatternsStore.subscribe(() => {
|
|
217
|
+
setPatterns(ignoredPatternsStore.getPatterns());
|
|
218
|
+
});
|
|
219
|
+
}, []);
|
|
220
|
+
const values = (0, _react.useMemo)(() => new Set(patterns.map(p => p.value)), [patterns]);
|
|
221
|
+
return {
|
|
222
|
+
patterns,
|
|
223
|
+
values,
|
|
224
|
+
add: pattern => ignoredPatternsStore.add(pattern),
|
|
225
|
+
remove: value => ignoredPatternsStore.remove(value),
|
|
226
|
+
toggle: value => ignoredPatternsStore.toggle(value),
|
|
227
|
+
toggleMode: value => ignoredPatternsStore.toggleMode(value)
|
|
228
|
+
};
|
|
229
|
+
}
|
|
@@ -9,4 +9,29 @@ Object.defineProperty(exports, "BaseEventStore", {
|
|
|
9
9
|
return _BaseEventStore.BaseEventStore;
|
|
10
10
|
}
|
|
11
11
|
});
|
|
12
|
-
|
|
12
|
+
Object.defineProperty(exports, "ignoredPatternsStore", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _ignoredPatternsStore.ignoredPatternsStore;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "isUrlIgnored", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _ignoredPatternsStore.isUrlIgnored;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "urlMatchesIgnoredPattern", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _ignoredPatternsStore.urlMatchesIgnoredPattern;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(exports, "useIgnoredPatterns", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () {
|
|
33
|
+
return _ignoredPatternsStore.useIgnoredPatterns;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
var _BaseEventStore = require("./BaseEventStore.js");
|
|
37
|
+
var _ignoredPatternsStore = require("./ignoredPatternsStore.js");
|
|
@@ -14,6 +14,7 @@ function CompactRow({
|
|
|
14
14
|
statusSublabel,
|
|
15
15
|
primaryText,
|
|
16
16
|
secondaryText,
|
|
17
|
+
secondaryAccessory,
|
|
17
18
|
expandedContent,
|
|
18
19
|
isExpanded,
|
|
19
20
|
badgeText,
|
|
@@ -65,10 +66,13 @@ function CompactRow({
|
|
|
65
66
|
style: styles.queryHash,
|
|
66
67
|
numberOfLines: isExpanded ? undefined : 2,
|
|
67
68
|
children: primaryText
|
|
68
|
-
}), !isExpanded && secondaryText ? /*#__PURE__*/(0, _jsxRuntime.
|
|
69
|
-
style: styles.
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
}), !isExpanded && (secondaryText || secondaryAccessory) ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
70
|
+
style: styles.secondaryRow,
|
|
71
|
+
children: [secondaryText ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
72
|
+
style: styles.secondaryText,
|
|
73
|
+
numberOfLines: 1,
|
|
74
|
+
children: secondaryText
|
|
75
|
+
}) : null, secondaryAccessory]
|
|
72
76
|
}) : null]
|
|
73
77
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
74
78
|
style: styles.rightSection,
|
|
@@ -189,6 +193,12 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
189
193
|
color: _gameUIColors.buoyColors.text,
|
|
190
194
|
lineHeight: 16
|
|
191
195
|
},
|
|
196
|
+
secondaryRow: {
|
|
197
|
+
flexDirection: "row",
|
|
198
|
+
alignItems: "center",
|
|
199
|
+
gap: 6,
|
|
200
|
+
marginTop: 1
|
|
201
|
+
},
|
|
192
202
|
secondaryText: {
|
|
193
203
|
fontSize: 10,
|
|
194
204
|
color: _gameUIColors.buoyColors.textMuted,
|
|
@@ -44,10 +44,12 @@ function ExpandedInfoRow({
|
|
|
44
44
|
function PillBadge({
|
|
45
45
|
color,
|
|
46
46
|
children,
|
|
47
|
-
icon
|
|
47
|
+
icon,
|
|
48
|
+
size = "md"
|
|
48
49
|
}) {
|
|
50
|
+
const isSm = size === "sm";
|
|
49
51
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
50
|
-
style: [styles.pill, {
|
|
52
|
+
style: [styles.pill, isSm && styles.pillSm, {
|
|
51
53
|
backgroundColor: color + "20",
|
|
52
54
|
borderColor: color + "40"
|
|
53
55
|
}],
|
|
@@ -55,7 +57,7 @@ function PillBadge({
|
|
|
55
57
|
style: styles.pillIcon,
|
|
56
58
|
children: icon
|
|
57
59
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
58
|
-
style: [styles.pillText, {
|
|
60
|
+
style: [styles.pillText, isSm && styles.pillTextSm, {
|
|
59
61
|
color
|
|
60
62
|
}],
|
|
61
63
|
children: children
|
|
@@ -83,6 +85,10 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
83
85
|
borderRadius: 999,
|
|
84
86
|
borderWidth: 1
|
|
85
87
|
},
|
|
88
|
+
pillSm: {
|
|
89
|
+
paddingHorizontal: 5,
|
|
90
|
+
paddingVertical: 1
|
|
91
|
+
},
|
|
86
92
|
pillIcon: {
|
|
87
93
|
marginRight: 4
|
|
88
94
|
},
|
|
@@ -91,5 +97,9 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
91
97
|
fontWeight: "700",
|
|
92
98
|
fontFamily: "monospace",
|
|
93
99
|
letterSpacing: 0.5
|
|
100
|
+
},
|
|
101
|
+
pillTextSm: {
|
|
102
|
+
fontSize: 9,
|
|
103
|
+
letterSpacing: 0.3
|
|
94
104
|
}
|
|
95
105
|
});
|
|
@@ -57,6 +57,12 @@ Object.defineProperty(exports, "getSafeAreaInsets", {
|
|
|
57
57
|
return _getSafeAreaInsets.getSafeAreaInsets;
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
|
+
Object.defineProperty(exports, "getSafeCurrentPathname", {
|
|
61
|
+
enumerable: true,
|
|
62
|
+
get: function () {
|
|
63
|
+
return _safeExpoRouter.getSafeCurrentPathname;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
60
66
|
Object.defineProperty(exports, "getSafeRouter", {
|
|
61
67
|
enumerable: true,
|
|
62
68
|
get: function () {
|