create-octane-xplat 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/package.json +1 -1
  2. package/template/.gitattributes +1 -0
  3. package/template/App_Resources/Android/app.gradle +25 -0
  4. package/template/App_Resources/Android/before-plugins.gradle +15 -0
  5. package/template/App_Resources/Android/src/main/AndroidManifest.xml +43 -0
  6. package/template/App_Resources/Android/src/main/res/drawable/ic_launcher_foreground.xml +15 -0
  7. package/template/App_Resources/Android/src/main/res/drawable-hdpi/background.png +0 -0
  8. package/template/App_Resources/Android/src/main/res/drawable-hdpi/logo.png +0 -0
  9. package/template/App_Resources/Android/src/main/res/drawable-ldpi/background.png +0 -0
  10. package/template/App_Resources/Android/src/main/res/drawable-ldpi/logo.png +0 -0
  11. package/template/App_Resources/Android/src/main/res/drawable-mdpi/background.png +0 -0
  12. package/template/App_Resources/Android/src/main/res/drawable-mdpi/logo.png +0 -0
  13. package/template/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml +8 -0
  14. package/template/App_Resources/Android/src/main/res/drawable-xhdpi/background.png +0 -0
  15. package/template/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png +0 -0
  16. package/template/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png +0 -0
  17. package/template/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png +0 -0
  18. package/template/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png +0 -0
  19. package/template/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png +0 -0
  20. package/template/App_Resources/Android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  21. package/template/App_Resources/Android/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  22. package/template/App_Resources/Android/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  23. package/template/App_Resources/Android/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  24. package/template/App_Resources/Android/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  25. package/template/App_Resources/Android/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  26. package/template/App_Resources/Android/src/main/res/values/colors.xml +14 -0
  27. package/template/App_Resources/Android/src/main/res/values/ic_launcher_background.xml +4 -0
  28. package/template/App_Resources/Android/src/main/res/values/styles.xml +42 -0
  29. package/template/App_Resources/Android/src/main/res/values-v21/colors.xml +5 -0
  30. package/template/App_Resources/Android/src/main/res/values-v21/styles.xml +34 -0
  31. package/template/App_Resources/Android/src/main/res/values-v29/styles.xml +18 -0
  32. package/template/App_Resources/Android/src/main/res/xml/network_security_config.xml +14 -0
  33. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json +122 -0
  34. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png +0 -0
  35. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-20.png +0 -0
  36. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png +0 -0
  37. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png +0 -0
  38. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png +0 -0
  39. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png +0 -0
  40. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png +0 -0
  41. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png +0 -0
  42. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png +0 -0
  43. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png +0 -0
  44. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png +0 -0
  45. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png +0 -0
  46. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png +0 -0
  47. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png +0 -0
  48. package/template/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png +0 -0
  49. package/template/App_Resources/iOS/Assets.xcassets/Contents.json +6 -0
  50. package/template/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json +23 -0
  51. package/template/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png +0 -0
  52. package/template/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png +0 -0
  53. package/template/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@3x.png +0 -0
  54. package/template/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json +23 -0
  55. package/template/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png +0 -0
  56. package/template/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png +0 -0
  57. package/template/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@3x.png +0 -0
  58. package/template/App_Resources/iOS/Assets.xcassets/octane.imageset/Contents.json +8 -0
  59. package/template/App_Resources/iOS/Assets.xcassets/octane.imageset/octane.png +0 -0
  60. package/template/App_Resources/iOS/Assets.xcassets/octane.imageset/octane@2x.png +0 -0
  61. package/template/App_Resources/iOS/Assets.xcassets/octane.imageset/octane@3x.png +0 -0
  62. package/template/App_Resources/iOS/Info.plist +52 -0
  63. package/template/App_Resources/iOS/LaunchScreen.storyboard +53 -0
  64. package/template/App_Resources/iOS/build.xcconfig +7 -0
  65. package/template/App_Resources/iOS/src/OctaneShaders.metal +128 -0
  66. package/template/gitignore +2 -0
  67. package/template/index.html +1 -1
  68. package/template/nativescript.config.ts +13 -0
  69. package/template/package.json +19 -2
  70. package/template/patches/@nativescript-community__vite-octane@0.2.1.patch +41 -0
  71. package/template/patches/@nativescript__vite@8.0.11.patch +294 -0
  72. package/template/pnpm-workspace.yaml +25 -3
  73. package/template/references.d.ts +1 -0
  74. package/template/src/App.tsrx +5 -3
  75. package/template/src/native.ts +33 -0
  76. package/template/src/style.css +19 -0
  77. package/template/tsconfig.json +4 -2
  78. package/template/tsconfig.native.json +18 -0
  79. package/template/vite.config.native.mts +85 -0
  80. /package/template/src/{main.tsrx → main.web.tsrx} +0 -0
@@ -0,0 +1,53 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
3
+ <device id="retina5_9" orientation="portrait">
4
+ <adaptation id="fullscreen"/>
5
+ </device>
6
+ <dependencies>
7
+ <deployment identifier="iOS"/>
8
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
9
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
10
+ </dependencies>
11
+ <scenes>
12
+ <!--View Controller-->
13
+ <scene sceneID="EHf-IW-A2E">
14
+ <objects>
15
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
16
+ <layoutGuides>
17
+ <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
18
+ <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
19
+ </layoutGuides>
20
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
21
+ <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
22
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
23
+ <subviews>
24
+ <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="LaunchScreen.AspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="wtH-rr-YfP">
25
+ <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
26
+ </imageView>
27
+ <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="LaunchScreen.Center" translatesAutoresizingMaskIntoConstraints="NO" id="s1z-aa-wYv">
28
+ <rect key="frame" x="0.0" y="150" width="375" height="512"/>
29
+ </imageView>
30
+ </subviews>
31
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
32
+ <constraints>
33
+ <constraint firstAttribute="trailing" secondItem="s1z-aa-wYv" secondAttribute="trailing" id="4Rf-kf-vql"/>
34
+ <constraint firstAttribute="bottom" secondItem="wtH-rr-YfP" secondAttribute="bottom" id="5CS-At-4ka"/>
35
+ <constraint firstItem="wtH-rr-YfP" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="Awn-b8-xf1"/>
36
+ <constraint firstItem="s1z-aa-wYv" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="BpJ-vb-eKA"/>
37
+ <constraint firstItem="s1z-aa-wYv" firstAttribute="trailing" secondItem="wtH-rr-YfP" secondAttribute="trailing" id="QIM-7J-OZz"/>
38
+ <constraint firstAttribute="trailing" secondItem="wtH-rr-YfP" secondAttribute="trailing" id="RXg-rW-UK8"/>
39
+ <constraint firstItem="s1z-aa-wYv" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="Rcp-LG-48z"/>
40
+ <constraint firstItem="wtH-rr-YfP" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="cch-8E-tYu"/>
41
+ </constraints>
42
+ </view>
43
+ </viewController>
44
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
45
+ </objects>
46
+ <point key="canvasLocation" x="509.60000000000002" y="237.93103448275863"/>
47
+ </scene>
48
+ </scenes>
49
+ <resources>
50
+ <image name="LaunchScreen.AspectFill" width="768" height="1024"/>
51
+ <image name="LaunchScreen.Center" width="384" height="512"/>
52
+ </resources>
53
+ </document>
@@ -0,0 +1,7 @@
1
+ // You can add custom settings here
2
+ // for example you can uncomment the following line to force distribution code signing
3
+ // CODE_SIGN_IDENTITY = iPhone Distribution
4
+ // To build for device with XCode you need to specify your development team.
5
+ // DEVELOPMENT_TEAM = YOUR_TEAM_ID;
6
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
7
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
@@ -0,0 +1,128 @@
1
+ #include <metal_stdlib>
2
+ #include <SwiftUI/SwiftUI_Metal.h>
3
+ using namespace metal;
4
+
5
+ // Stitchable shaders for SwiftUI's `.distortionEffect` / `.colorEffect` (iOS 17+).
6
+ // SwiftUI supplies the leading parameters — `position` for a distortion, plus
7
+ // `color` for a colour effect — and appends the arguments passed at the call
8
+ // site in `OctaneLogo.swift`.
9
+
10
+ /// Rising heat haze.
11
+ ///
12
+ /// A distortion shader maps a destination pixel back to the source pixel it
13
+ /// should sample, so the returned offset reads as the image bending. Amplitude
14
+ /// is weighted by `base^2` (1 at the bottom, 0 at the top) so the mark shimmers
15
+ /// like air over hot metal instead of wobbling as a rigid block, and the two
16
+ /// waves use incommensurate frequencies so the pattern never visibly repeats.
17
+ [[ stitchable ]] float2 octaneHeatHaze(float2 position, float time, float2 size, float intensity) {
18
+ float2 uv = position / size;
19
+ float base = 1.0 - uv.y;
20
+ float wave = sin(uv.y * 26.0 - time * 3.1) * 0.6
21
+ + sin(uv.y * 11.0 - time * 1.9 + uv.x * 4.0) * 0.4;
22
+ float amp = 2.4 * base * base * intensity;
23
+ return position + float2(wave * amp, 0.0);
24
+ }
25
+
26
+ /// Ember sheen sweeping diagonally across the ink.
27
+ ///
28
+ /// SwiftUI hands colour effects premultiplied alpha, so the colour has to be
29
+ /// un-premultiplied before mixing and re-premultiplied on the way out —
30
+ /// blending straight into premultiplied RGB would bleed the highlight into the
31
+ /// transparent surround and halo the glyph edges.
32
+ [[ stitchable ]] half4 octaneSheen(float2 position, half4 color, float time, float2 size, float intensity) {
33
+ if (color.a < 0.004h) {
34
+ return color;
35
+ }
36
+ half3 rgb = color.rgb / color.a;
37
+
38
+ float2 uv = position / size;
39
+ float sweep = fract(time * 0.22);
40
+ float d = (uv.x * 0.85 + (1.0 - uv.y) * 0.35) - (sweep * 1.8 - 0.4);
41
+ float band = exp(-(d * d) * 42.0);
42
+
43
+ half3 ember = half3(1.0h, 0.255h, 0.353h); // the logo's #FF415A accent
44
+ half3 gold = half3(1.0h, 0.72h, 0.36h);
45
+ half3 hot = mix(ember, gold, half(band));
46
+
47
+ half glow = half(band) * half(intensity);
48
+ rgb = mix(rgb, hot, glow * 0.9h);
49
+ rgb += half3(glow * 0.25h);
50
+
51
+ return half4(saturate(rgb) * color.a, color.a);
52
+ }
53
+
54
+ /// Per-shard pseudo-random pair. Deterministic in the cell index, so a shard
55
+ /// keeps the same trajectory for the whole burst instead of jittering per frame.
56
+ static inline float2 shardNoise(float2 cell) {
57
+ float3 a = fract(float3(cell.xyx) * float3(0.1031, 0.1030, 0.0973));
58
+ a += dot(a, a.yzx + 33.33);
59
+ return fract(float2((a.x + a.y) * a.z, (a.x + a.z) * a.y));
60
+ }
61
+
62
+ /// Ignition burst: the mark shatters outward and reassembles.
63
+ ///
64
+ /// A layer effect (rather than a distortion) because each shard has to read
65
+ /// from somewhere else in the image: sampling `position - offset` with an
66
+ /// offset that is constant across a grid cell translates that whole cell as a
67
+ /// rigid chip, which is what reads as breaking apart. A distortion shader can
68
+ /// only nudge its own pixel, so it smears instead of shattering.
69
+ ///
70
+ /// `progress` runs 0 -> 1 once per burst and the displacement rides
71
+ /// `sin(p * pi)`, so the shards fly out and land back exactly where they
72
+ /// started — no separate reassembly pass, and the mark cannot get stranded
73
+ /// mid-explosion if a frame is dropped.
74
+ [[ stitchable ]] half4 octaneShatter(float2 position, SwiftUI::Layer layer, float2 size, float progress) {
75
+ if (progress <= 0.0001) {
76
+ return layer.sample(position);
77
+ }
78
+
79
+ float p = clamp(progress, 0.0, 1.0);
80
+ float fly = sin(p * M_PI_F);
81
+ float ease = fly * fly;
82
+
83
+ float2 cells = float2(22.0, 5.0);
84
+ float2 cellSize = size / cells;
85
+ float2 rnd = shardNoise(floor(position / cellSize));
86
+
87
+ float2 centre = size * 0.5;
88
+ float2 radial = (position - centre) / max(size.x * 0.5, 1.0);
89
+ float2 dir = radial + (rnd - 0.5) * 0.9;
90
+ float len = max(length(dir), 0.0001);
91
+ dir /= len;
92
+
93
+ // Capped so the outermost shards stay inside the host view; anything
94
+ // travelling further is clipped by its bounds rather than fading out.
95
+ float2 offset = dir * mix(22.0, 70.0, rnd.x) * ease;
96
+ offset.y += (rnd.y - 0.5) * 42.0 * ease;
97
+ float2 src = position - offset;
98
+
99
+ // Chromatic split along the flight path — the shards read as moving fast
100
+ // rather than merely being displaced.
101
+ float split = ease * 3.5;
102
+ half4 cr = layer.sample(src + dir * split);
103
+ half4 cg = layer.sample(src);
104
+ half4 cb = layer.sample(src - dir * split);
105
+ half alpha = max(max(cr.a, cg.a), cb.a);
106
+ if (alpha < 0.004h) {
107
+ return half4(0.0h);
108
+ }
109
+ half3 rgb = half3(cr.r, cg.g, cb.b) / alpha;
110
+
111
+ // Expanding shockwave ring, brightest just after the tap.
112
+ float ring = exp(-pow((length(radial) - p * 1.5) * 5.0, 2.0)) * (1.0 - p);
113
+
114
+ half3 ember = half3(1.0h, 0.255h, 0.353h);
115
+ half3 gold = half3(1.0h, 0.78h, 0.35h);
116
+ half heat = half(ease);
117
+ // Ember-leaning, and only a partial mix: pushing all the way to `hot` turns
118
+ // every shard the same flat gold and the mark stops being recognisable
119
+ // mid-flight. Holding some ink back keeps them reading as burning pieces
120
+ // of the logo.
121
+ half3 hot = mix(ember, gold, heat * 0.75h);
122
+
123
+ rgb = mix(rgb, hot, heat * 0.6h);
124
+ rgb += half3(heat * 0.18h * half(rnd.x) + half(ring) * 0.45h);
125
+
126
+ alpha *= 1.0h - heat * 0.2h;
127
+ return half4(saturate(rgb) * alpha, alpha);
128
+ }
@@ -1,2 +1,4 @@
1
1
  node_modules
2
2
  dist
3
+ platforms/
4
+ hooks/
@@ -7,6 +7,6 @@
7
7
  </head>
8
8
  <body>
9
9
  <div id="root"></div>
10
- <script type="module" src="/src/main.tsrx"></script>
10
+ <script type="module" src="/src/main.web.tsrx"></script>
11
11
  </body>
12
12
  </html>
@@ -0,0 +1,13 @@
1
+ import { NativeScriptConfig } from '@nativescript/core';
2
+
3
+ export default {
4
+ id: 'org.nativescript.octanexplat',
5
+ appPath: 'src',
6
+ appResourcesPath: 'App_Resources',
7
+ android: {
8
+ v8Flags: '--expose_gc',
9
+ markingMode: 'none',
10
+ },
11
+ bundler: 'vite',
12
+ bundlerConfigPath: 'vite.config.native.mts',
13
+ } as NativeScriptConfig;
@@ -2,18 +2,35 @@
2
2
  "name": "octane-xplat-app",
3
3
  "private": true,
4
4
  "type": "module",
5
+ "main": "src/native.ts",
5
6
  "scripts": {
7
+ "xplat": "xplat",
6
8
  "dev": "vite",
9
+ "dev:ios": "ns run ios",
10
+ "dev:android": "ns run android",
7
11
  "build": "vite build",
8
- "typecheck": "tsrx-tsc --noEmit"
12
+ "build:ios": "ns build ios",
13
+ "build:android": "ns build android",
14
+ "typecheck": "tsrx-tsc --noEmit && tsrx-tsc --noEmit -p tsconfig.native.json"
9
15
  },
10
16
  "dependencies": {
11
- "@octane-xplat/ui": "^0.2.0",
17
+ "@nativescript-community/octane": "0.2.1",
18
+ "@nativescript-community/ui-canvas": "4.6.52",
19
+ "@nativescript/core": "9.1.2",
20
+ "@octane-xplat/ui": "^0.3.0",
21
+ "@valor/nativescript-websockets": "2.0.3",
12
22
  "octane": "0.4.0"
13
23
  },
14
24
  "devDependencies": {
25
+ "@nativescript-community/vite-octane": "0.2.1",
26
+ "@nativescript/android": "9.1.1",
27
+ "@nativescript/ios": "9.1.0",
28
+ "@nativescript/types": "9.0.0",
29
+ "@nativescript/vite": "8.0.11",
30
+ "@octane-xplat/cli": "^0.3.0",
15
31
  "@octanejs/vite-plugin": "0.1.58",
16
32
  "@tsrx/typescript-plugin": "0.4.7",
33
+ "nativescript": "9.1.1",
17
34
  "typescript": "5.9.3",
18
35
  "vite": "8.3.0"
19
36
  }
@@ -0,0 +1,41 @@
1
+ diff --git a/dist/server/strategy.js b/dist/server/strategy.js
2
+ index 7d84b37e2ed3a026da5b31c8a11027e0580877b2..f6594a913ba64837135e994cb183add13c6a0530 100644
3
+ --- a/dist/server/strategy.js
4
+ +++ b/dist/server/strategy.js
5
+ @@ -14,7 +14,9 @@ import { purgeTransformCachesForHotUpdate, runHotUpdatePrologue, typescriptServe
6
+ * otherwise the re-fetch can be served from the previous save's transform and
7
+ * the screen ends up one save behind.
8
+ */
9
+ -const SCRIPT_FILE_RE = /\.(?:[mc]?[jt]sx?)$/i;
10
+ +// `.tsrx` is Octane's component extension — without it the deferred delta is
11
+ +// never emitted and component edits silently never reach the device.
12
+ +const SCRIPT_FILE_RE = /\.(?:[mc]?[jt]sx?|tsrx)$/i;
13
+ export const octaneServerStrategy = {
14
+ ...typescriptServerStrategy,
15
+ flavor: 'octane',
16
+ @@ -24,7 +26,7 @@ export const octaneServerStrategy = {
17
+ if (!state)
18
+ return;
19
+ const { root, metrics, emitSummary } = state;
20
+ - const { moduleGraph, verbose, sharedTransformRequest } = deps;
21
+ + const { moduleGraph, verbose, sharedTransformRequest, wss, isSocketClientOpen } = deps;
22
+ const { file, server } = ctx;
23
+ if (!SCRIPT_FILE_RE.test(file)) {
24
+ emitSummary();
25
+ @@ -71,6 +73,16 @@ export const octaneServerStrategy = {
26
+ const gm = moduleGraph.get(normalizedId);
27
+ if (gm)
28
+ moduleGraph.emitDelta([gm], []);
29
+ + // The ns:hmr-delta broadcast IS the update delivery for this flavor —
30
+ + // count open sockets so the always-on summary reflects reality
31
+ + // instead of reporting recipients=0 for a delivered update.
32
+ + try {
33
+ + wss?.clients?.forEach((client) => {
34
+ + if (isSocketClientOpen?.(client))
35
+ + metrics.recipients += 1;
36
+ + });
37
+ + }
38
+ + catch { }
39
+ }
40
+ catch (e) {
41
+ if (verbose)
@@ -0,0 +1,294 @@
1
+ diff --git a/hmr/server/core-bundle.js b/hmr/server/core-bundle.js
2
+ index 993097b9d861a2a7c477f4e664213efec3eb6293..7a9b95180cbd47e1d9c71180bc4582a450333a7c 100644
3
+ --- a/hmr/server/core-bundle.js
4
+ +++ b/hmr/server/core-bundle.js
5
+ @@ -607,17 +607,32 @@ export function buildCoreMainShimCode() {
6
+ * class/function values. `exportNames` comes from the same disk-based export
7
+ * discovery the per-module bridge relies on (collectStaticExportNamesFromFile).
8
+ */
9
+ -const RESERVED_EXPORT_NAMES = new Set(['default', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'let', 'static', 'yield', 'await', 'eval', 'arguments', '__ns_core_sub_ns__']);
10
+ +// `default` is emitted separately (below); `__ns_core_sub_ns__` is this shim's
11
+ +// own local — a module exporting that name can't collide with it.
12
+ +const NON_DECLARABLE_EXPORT_NAMES = new Set(['default', '__ns_core_sub_ns__']);
13
+ +// Reserved words can't be declaration bindings (`export const enum` is a
14
+ +// syntax error), but they ARE legal export names — the specifier position in
15
+ +// `export { x as name }` accepts any IdentifierName. Emit those through a
16
+ +// temp + alias so a reserved-word export (zod's `enum`/`catch`/… shapes this
17
+ +// same code path) still reaches the bridged namespace.
18
+ +const RESERVED_EXPORT_NAMES = new Set(['break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'let', 'static', 'yield', 'await', 'eval', 'arguments']);
19
+ export function buildCoreSubShimCode(sub, exportNames, hasOwnDefaultExport = false) {
20
+ const canonical = normalizeCoreSub(sub);
21
+ const registryKey = `@nativescript/core/${canonical}`;
22
+ - const names = Array.from(new Set(exportNames)).filter((n) => /^[A-Za-z_$][\w$]*$/.test(n) && !RESERVED_EXPORT_NAMES.has(n));
23
+ + const names = Array.from(new Set(exportNames)).filter((n) => /^[A-Za-z_$][\w$]*$/.test(n) && !NON_DECLARABLE_EXPORT_NAMES.has(n));
24
+ const lines = [];
25
+ lines.push(`/* @nativescript/core bridge (bundle mode) — sub: ${canonical} */`);
26
+ lines.push(`import ${JSON.stringify(CORE_BUNDLE_PATH)};`);
27
+ lines.push(`const __ns_core_sub_ns__ = (globalThis.__NS_CORE_MODULES__ || {})[${JSON.stringify(registryKey)}];`);
28
+ lines.push(`if (!__ns_core_sub_ns__) { throw new Error(${JSON.stringify(`[ns-core] core bundle registry missing '${registryKey}' — restart the dev server or set NS_CORE_PER_MODULE=1`)}); }`);
29
+ + let aliasIdx = 0;
30
+ for (const name of names) {
31
+ + if (RESERVED_EXPORT_NAMES.has(name)) {
32
+ + const alias = `__ns_core_alias_${aliasIdx++}`;
33
+ + lines.push(`const ${alias} = __ns_core_sub_ns__[${JSON.stringify(name)}];`);
34
+ + lines.push(`export { ${alias} as ${name} };`);
35
+ + continue;
36
+ + }
37
+ lines.push(`export const ${name} = __ns_core_sub_ns__.${name};`);
38
+ }
39
+ // Deep-subpath contract (see isDeepCoreSubpath / per-module bridge): a sub
40
+ diff --git a/hmr/server/deps-bundle.js b/hmr/server/deps-bundle.js
41
+ index 50e5abddddf80b35044d45eb108a64dd175bf6e7..799c7257a8a730515633f19f6221ad26ff8678eb 100644
42
+ --- a/hmr/server/deps-bundle.js
43
+ +++ b/hmr/server/deps-bundle.js
44
+ @@ -219,8 +219,37 @@ function isPackageRootSpec(spec) {
45
+ return false;
46
+ return pkgSub.split('/').length === (pkgSub.startsWith('@') ? 2 : 1);
47
+ }
48
+ -function resolveSpecToFile(spec, projectRoot, workspaceRoot, platform) {
49
+ +/**
50
+ + * `/node_modules/<pkg>/<sub...>` → the exports-mapped file on disk
51
+ + * (`rouzer/http` → `rouzer/dist/http.js`). Without this, non-root specs fall
52
+ + * back to `resolveId` and land on Vite's dep-optimizer artifacts under
53
+ + * `.vite/deps/` — flat bundles whose generated shared chunks
54
+ + * (`foo-<hash>.js`) this pipeline cannot serve to the device.
55
+ + */
56
+ +function resolvePackageSubpathFromSpecRoots(spec, roots, platform) {
57
+ + const pkgSub = spec.replace(/^\/node_modules\//, '');
58
+ + const parts = pkgSub.split('/');
59
+ + const nameSegs = parts[0].startsWith('@') ? 2 : 1;
60
+ + if (parts.length <= nameSegs)
61
+ + return null;
62
+ + const subpath = parts.slice(nameSegs).join('/');
63
+ + for (const root of roots) {
64
+ + const pkgDir = path.join(root, 'node_modules', ...parts.slice(0, nameSegs));
65
+ + if (!existsSync(path.join(pkgDir, 'package.json')))
66
+ + continue;
67
+ + const resolved = resolvePackageSubpathEntry(pkgDir, subpath, platform);
68
+ + if (resolved)
69
+ + return resolved;
70
+ + }
71
+ + return null;
72
+ +}
73
+ +export function resolveSpecToFile(spec, projectRoot, workspaceRoot, platform) {
74
+ const roots = [projectRoot, ...(workspaceRoot && path.resolve(workspaceRoot) !== path.resolve(projectRoot) ? [workspaceRoot] : [])];
75
+ + // `.vite/` is Vite's dep-optimizer store — generated artifacts with
76
+ + // content-hashed names, not package files. Resolving them here would mint
77
+ + // deps-bundle entries/shims for files whose names churn per optimization.
78
+ + if (spec.includes('/.vite/'))
79
+ + return null;
80
+ // Package roots honor the declared entry (exports/module/main) FIRST —
81
+ // matching esbuild's resolution of bare imports inside the bundle. The
82
+ // index fan-out would otherwise pick stray root files like a shipped
83
+ @@ -236,6 +265,12 @@ function resolveSpecToFile(spec, projectRoot, workspaceRoot, platform) {
84
+ if (abs)
85
+ return abs;
86
+ }
87
+ + // Non-root specs (`rouzer/http`, `qubu/sqlite`): consult the package's
88
+ + // exports map so the real dist file wins — the alternative downstream is
89
+ + // the dep optimizer's `.vite/deps` artifact, whose sibling chunks 404.
90
+ + const subpathEntry = resolvePackageSubpathFromSpecRoots(spec, roots, platform);
91
+ + if (subpathEntry)
92
+ + return subpathEntry;
93
+ return isPackageRootSpec(spec) ? null : resolvePackageRootEntry(spec, roots, platform);
94
+ }
95
+ // Never bundled: `@nativescript/core` belongs to the /ns/core bridge realm,
96
+ @@ -869,7 +904,15 @@ export function collectDepsModuleExportInfo(absPath, platform, seen = new Set())
97
+ names.delete('default');
98
+ return { names: Array.from(names).sort(), hasDefault: hasExistingDefaultExport(code) };
99
+ }
100
+ -const RESERVED_EXPORT_NAMES = new Set(['default', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'let', 'static', 'yield', 'await', 'eval', 'arguments', '__ns_dep_ns__']);
101
+ +// `default` is emitted separately (below); `__ns_dep_ns__` is this shim's own
102
+ +// local — a package exporting that name can't collide with it.
103
+ +const NON_DECLARABLE_EXPORT_NAMES = new Set(['default', '__ns_dep_ns__']);
104
+ +// Reserved words can't be declaration bindings (`export const enum` is a
105
+ +// syntax error), but they ARE legal export names — the specifier position in
106
+ +// `export { x as name }` accepts any IdentifierName. Emit those through a
107
+ +// temp + alias so packages like zod (`enum`, `catch`, `function`, …) keep
108
+ +// their full surface on the bridged namespace.
109
+ +const RESERVED_EXPORT_NAMES = new Set(['break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'let', 'static', 'yield', 'await', 'eval', 'arguments']);
110
+ /**
111
+ * Shim served at `/ns/m/node_modules/...` for a bundled module. The static
112
+ * bundle import guarantees the registry is populated before the body reads it
113
+ @@ -880,13 +923,20 @@ const RESERVED_EXPORT_NAMES = new Set(['default', 'break', 'case', 'catch', 'cla
114
+ export function buildDepsShimCode(key, exportNames, hasOwnDefaultExport) {
115
+ // JS_IDENTIFIER_RE (not ASCII \w): Angular exports `ɵɵdefineInjectable`-style
116
+ // names — dropping them here breaks every `import * as i0` consumer.
117
+ - const names = Array.from(new Set(exportNames)).filter((n) => JS_IDENTIFIER_RE.test(n) && !RESERVED_EXPORT_NAMES.has(n));
118
+ + const names = Array.from(new Set(exportNames)).filter((n) => JS_IDENTIFIER_RE.test(n) && !NON_DECLARABLE_EXPORT_NAMES.has(n));
119
+ const lines = [];
120
+ lines.push(`/* ns-deps bridge (bundle mode) — ${key} */`);
121
+ lines.push(`import ${JSON.stringify(DEPS_BUNDLE_PATH)};`);
122
+ lines.push(`const __ns_dep_ns__ = (globalThis.__NS_DEPS_MODULES__ || {})[${JSON.stringify(key)}];`);
123
+ lines.push(`if (!__ns_dep_ns__) { throw new Error(${JSON.stringify(`[ns-deps] deps bundle registry missing '${key}' — restart the dev server or set NS_DEPS_PER_MODULE=1`)}); }`);
124
+ + let aliasIdx = 0;
125
+ for (const name of names) {
126
+ + if (RESERVED_EXPORT_NAMES.has(name)) {
127
+ + const alias = `__ns_dep_alias_${aliasIdx++}`;
128
+ + lines.push(`const ${alias} = __ns_dep_ns__[${JSON.stringify(name)}];`);
129
+ + lines.push(`export { ${alias} as ${name} };`);
130
+ + continue;
131
+ + }
132
+ lines.push(`export const ${name} = __ns_dep_ns__.${name};`);
133
+ }
134
+ if (hasOwnDefaultExport) {
135
+ diff --git a/hmr/server/device-transform-helpers.js b/hmr/server/device-transform-helpers.js
136
+ index 9364ce9942b84fffd0e3a6eb6700d8253fa66e7e..93ee2bf2570303277d7b4350018b00fbbb31e2a4 100644
137
+ --- a/hmr/server/device-transform-helpers.js
138
+ +++ b/hmr/server/device-transform-helpers.js
139
+ @@ -81,12 +81,18 @@ function bareSpecifierFromFlatDepPath(depPath) {
140
+ }
141
+ catch { }
142
+ }
143
+ - const bareSpecifier = resolved || candidates[0];
144
+ + // Unverified decode → NOT a package: `depPath` is a generated optimizer
145
+ + // artifact (a hash-named shared chunk like `http-Dm_BQhab.js`, whose
146
+ + // base64url '_' decodes to a bogus '/'). Returning null lets the caller
147
+ + // keep the real `.vite/deps` URL instead of emitting a garbage specifier.
148
+ + if (!resolved) {
149
+ + return null;
150
+ + }
151
+ if (!warnedVendorMisses.has(depPath)) {
152
+ warnedVendorMisses.add(depPath);
153
+ - console.warn(`[ns-vite] vendor-manifest miss: prebundled dep '${depPath}' is not in the vendor bundle — serving '${bareSpecifier}' over HTTP via /ns/m/node_modules/${resolved ? '' : ' (could not verify the package on disk; the specifier is a best-effort decode of the flattened id)'}.`);
154
+ + console.warn(`[ns-vite] vendor-manifest miss: prebundled dep '${depPath}' is not in the vendor bundle — serving '${resolved}' over HTTP via /ns/m/node_modules/${resolved}.`);
155
+ }
156
+ - return bareSpecifier;
157
+ + return resolved;
158
+ }
159
+ function rewriteVitePrebundleImportsForDevice(code, preserveVendorImports) {
160
+ const imports = collectTopLevelImportRecords(code);
161
+ @@ -111,6 +117,15 @@ function rewriteVitePrebundleImportsForDevice(code, preserveVendorImports) {
162
+ if (bareSpecifier) {
163
+ replacement = imp.text.replace(source, bareSpecifier);
164
+ }
165
+ + else {
166
+ + // Generated optimizer chunk (hash-named sibling of an
167
+ + // optimized entry) — not a package and not decodable to one.
168
+ + // Route the real artifact URL back through the bridge: the
169
+ + // /ns/m route serves it from the dep-optimizer store (the
170
+ + // `?v` stamp rides along as forcedVer) or emits an
171
+ + // actionable optimizeDeps.exclude error.
172
+ + replacement = imp.text.replace(source, `/ns/m/node_modules/.vite/deps/${depPath}`);
173
+ + }
174
+ }
175
+ edits.push({
176
+ start: imp.start,
177
+ diff --git a/hmr/server/websocket-ns-m-request.js b/hmr/server/websocket-ns-m-request.js
178
+ index b54aefcc839e95906d76d02e522c052a706ca76f..d49159de94ba05dcfc372aa7a3c513e0f5c0a0d3 100644
179
+ --- a/hmr/server/websocket-ns-m-request.js
180
+ +++ b/hmr/server/websocket-ns-m-request.js
181
+ @@ -1,6 +1,9 @@
182
+ import * as path from 'path';
183
+ +import { realpathSync } from 'fs';
184
+ import { filterExistingNodeModulesTransformCandidates, getBlockedDeviceNodeModulesReason, resolveCandidateFilePath, tryReadRawExplicitJavaScriptModule } from './websocket-module-specifiers.js';
185
+ import { collapseLegacyNsMTags } from './websocket-ns-m-paths.js';
186
+ +import { resolveSpecToFile } from './deps-bundle.js';
187
+ +import { resolvePlatform } from '../../helpers/cli-flags.js';
188
+ function normalizeAbsoluteFilesystemSpec(spec, serverRoot) {
189
+ const toPosix = (value) => value.replace(/\\/g, '/');
190
+ const rootPosix = toPosix(serverRoot);
191
+ @@ -93,6 +96,29 @@ export function createNsMRequestContext(requestUrl, serverRoot, appVirtualWithSl
192
+ return { kind: 'next' };
193
+ }
194
+ }
195
+ +/**
196
+ + * Canonical request URL for a resolved node_modules file. Project-root files
197
+ + * keep the `/node_modules/...` form so module identity matches the /ns/m
198
+ + * spec space; files outside the project root (hoisted/workspace) use the
199
+ + * `/@fs` form Vite can load directly. Symlinked workspace-source packages
200
+ + * resolve to their realpath so the TS transform applies — same policy as
201
+ + * filterExistingNodeModulesTransformCandidates.
202
+ + */
203
+ +function nodeModulesFileToRequestUrl(absPath, serverRoot) {
204
+ + try {
205
+ + const real = realpathSync(absPath);
206
+ + if (real !== absPath && !real.split(path.sep).includes('node_modules')) {
207
+ + return `/@fs${real.replace(/\\/g, '/')}`;
208
+ + }
209
+ + }
210
+ + catch { }
211
+ + const rootPosix = path.resolve(serverRoot).replace(/\\/g, '/').replace(/\/+$/, '');
212
+ + const absPosix = absPath.replace(/\\/g, '/');
213
+ + if (absPosix.startsWith(`${rootPosix}/`)) {
214
+ + return absPosix.slice(rootPosix.length);
215
+ + }
216
+ + return `/@fs${absPosix}`;
217
+ +}
218
+ async function tryTransformRequest(transformRequest, url, timeoutMs) {
219
+ try {
220
+ return await transformRequest(url, timeoutMs);
221
+ @@ -146,6 +172,38 @@ export async function resolveNsMTransformedModule(options) {
222
+ return { transformed, resolvedCandidate };
223
+ }
224
+ }
225
+ + // Optimizer-artifact requests (`/node_modules/.vite/deps/<file>`): flat
226
+ + // entries and hash-named shared chunks live in the dep-optimizer's store
227
+ + // (often memory-only), keyed by the `?v=<browserHash>` stamp Vite emits —
228
+ + // captured inbound as `forcedVer`. Replay it so Vite resolves the artifact
229
+ + // as-is; a miss falls through to the route's transform-miss branch, which
230
+ + // names optimizeDeps.exclude as the fix.
231
+ + if (spec.includes('/.vite/deps/')) {
232
+ + const depUrl = context.forcedVer ? `${spec}?v=${context.forcedVer}` : spec;
233
+ + transformed = await tryTransformRequest(transformRequest, depUrl, timeoutMs);
234
+ + if (transformed?.code) {
235
+ + return { transformed, resolvedCandidate: depUrl };
236
+ + }
237
+ + return { transformed: null, resolvedCandidate };
238
+ + }
239
+ + // Bare or subpath node_modules specs (`rouzer/http`, `qubu/sqlite`) that
240
+ + // survive the on-disk candidate fan-out would otherwise hit `resolveId`
241
+ + // and land on Vite's dep-optimizer output: `/node_modules/.vite/deps/
242
+ + // <flat>.js` bundles whose generated sibling chunks (`foo-<hash>.js`)
243
+ + // this pipeline cannot serve — and whose flattened ids decode to bogus
244
+ + // bare specifiers downstream (`http-Dm/BQhab`). Prefer the real package
245
+ + // file (exports-map aware) so the device gets the same per-module source
246
+ + // an `optimizeDeps.exclude` entry produces.
247
+ + if (spec.includes('/node_modules/')) {
248
+ + const realFile = resolveSpecToFile(spec.slice(spec.lastIndexOf('/node_modules/')), serverRoot, workspaceRoot, resolvePlatform() || 'ios');
249
+ + const realUrl = realFile ? nodeModulesFileToRequestUrl(realFile, serverRoot) : null;
250
+ + if (realUrl) {
251
+ + transformed = await tryTransformRequest(transformRequest, realUrl, timeoutMs);
252
+ + if (transformed?.code) {
253
+ + return { transformed, resolvedCandidate: realUrl };
254
+ + }
255
+ + }
256
+ + }
257
+ const resolvedId = await resolveId(spec);
258
+ if (resolvedId) {
259
+ transformed = await tryTransformRequest(transformRequest, resolvedId, timeoutMs);
260
+ diff --git a/hmr/server/websocket-ns-m.js b/hmr/server/websocket-ns-m.js
261
+ index 8eeb51ab59497e7f775da3117c4207e05f1814c7..346b676fbb67ce6bebe81cc379fdbe0d0aeac876 100644
262
+ --- a/hmr/server/websocket-ns-m.js
263
+ +++ b/hmr/server/websocket-ns-m.js
264
+ @@ -265,6 +265,30 @@ export function registerNsModuleServerRoute(server, options) {
265
+ catch { }
266
+ // If transformRequest failed, handle bare-module vendor shims for 'vue' and 'pinia'
267
+ if (!transformed?.code) {
268
+ + // Dep-optimizer artifacts (`.vite/deps` entries + generated
269
+ + // shared chunks) aren't package files — when one reaches the
270
+ + // bridge unserved, the fix is keeping that dep out of
271
+ + // optimizeDeps. Name the optimized set so the on-device
272
+ + // error is actionable instead of a generic transform miss.
273
+ + if (spec.includes('/.vite/deps/')) {
274
+ + const optimizedIds = [];
275
+ + try {
276
+ + for (const env of Object.values(server.environments || {})) {
277
+ + const meta = env?.depsOptimizer?.metadata;
278
+ + for (const record of [meta?.optimized, meta?.discovered]) {
279
+ + if (record)
280
+ + optimizedIds.push(...Object.keys(record));
281
+ + }
282
+ + }
283
+ + }
284
+ + catch { }
285
+ + const hint = optimizedIds.length
286
+ + ? ` Currently optimized: ${Array.from(new Set(optimizedIds)).join(', ')} — add the package that pulls in this chunk to optimizeDeps.exclude in the native vite config (or restart with NS_DEPS_PER_MODULE=1).`
287
+ + : ' Add the importing package to optimizeDeps.exclude in the native vite config.';
288
+ + res.statusCode = 404;
289
+ + res.end(`throw new Error(${JSON.stringify(`[ns/m] Vite optimizeDeps artifact '${spec}' is not servable on device — its shared chunks are generated files outside the package tree.${hint}`)});\nexport {};\n`);
290
+ + return;
291
+ + }
292
+ const bare = spec.startsWith('/') ? spec.slice(1) : spec;
293
+ const isBare = bare && !bare.includes('/') && !/\.(ts|tsx|js|jsx|mjs|mts|cts|vue)$/i.test(bare);
294
+ if (isBare && (bare === 'vue' || bare === 'nativescript-vue' || bare === 'pinia')) {
@@ -1,7 +1,29 @@
1
1
  # pnpm 11 settings live here, not in package.json.
2
- # esbuild needs its install script to place the platform binary.
3
- # @nativescript/core lands via optional peers — its scripts are native
4
- # toolchain prep the ns CLI does itself; skipping keeps installs clean.
2
+ #
3
+ # nodeLinker hoisted: Node ESM resolves package imports relative to the
4
+ # symlink path (node_modules/octane/...), not the realpath — so octane's
5
+ # transitive deps (@tsrx/core etc.) must sit at top-level node_modules.
6
+ # NativeScript's bundler wants a flat tree for the same reason.
7
+ nodeLinker: hoisted
8
+ #
9
+ # esbuild + @parcel/watcher need install scripts to place binaries;
10
+ # nativescript's postinstall sets up the local CLI. @nativescript/core's
11
+ # scripts are native toolchain prep the ns CLI does itself.
5
12
  allowBuilds:
6
13
  esbuild: true
14
+ '@parcel/watcher': true
15
+ nativescript: true
7
16
  '@nativescript/core': false
17
+ #
18
+ # vite-octane's octaneServerStrategy early-returns for `.tsrx` files (its
19
+ # SCRIPT_FILE_RE only covers [jt]sx?), so component edits never emit the
20
+ # deferred ns:hmr-delta broadcast — on-device HMR looks dead. The patch also
21
+ # counts open /ns-hmr sockets so `recipients=N` in update logs is real.
22
+ # Drop once a release carries the fix.
23
+ patchedDependencies:
24
+ '@nativescript-community/vite-octane@0.2.1': patches/@nativescript-community__vite-octane@0.2.1.patch
25
+ # Dep/core shims dropped reserved-word named exports (zod's `enum`, `catch`,
26
+ # `function`, …) because `export const enum` is a syntax error — emit them
27
+ # via temp+alias so bridged namespaces keep their full surface. Without it,
28
+ # any app importing such a dep fails module eval on boot/graph-reload.
29
+ '@nativescript/vite@8.0.11': patches/@nativescript__vite@8.0.11.patch
@@ -0,0 +1 @@
1
+ /// <reference types="@nativescript/types" />