@livx.cc/appwrap 0.19.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 (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -0
  3. package/package.json +46 -0
  4. package/runtime/App_Resources/Android/app.gradle +34 -0
  5. package/runtime/App_Resources/Android/before-plugins.gradle +15 -0
  6. package/runtime/App_Resources/Android/src/main/AndroidManifest.xml +76 -0
  7. package/runtime/App_Resources/Android/src/main/res/drawable/ic_launcher_foreground.xml +15 -0
  8. package/runtime/App_Resources/Android/src/main/res/drawable-hdpi/background.png +0 -0
  9. package/runtime/App_Resources/Android/src/main/res/drawable-hdpi/logo.png +0 -0
  10. package/runtime/App_Resources/Android/src/main/res/drawable-ldpi/background.png +0 -0
  11. package/runtime/App_Resources/Android/src/main/res/drawable-ldpi/logo.png +0 -0
  12. package/runtime/App_Resources/Android/src/main/res/drawable-mdpi/background.png +0 -0
  13. package/runtime/App_Resources/Android/src/main/res/drawable-mdpi/logo.png +0 -0
  14. package/runtime/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml +8 -0
  15. package/runtime/App_Resources/Android/src/main/res/drawable-xhdpi/background.png +0 -0
  16. package/runtime/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png +0 -0
  17. package/runtime/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png +0 -0
  18. package/runtime/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png +0 -0
  19. package/runtime/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png +0 -0
  20. package/runtime/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png +0 -0
  21. package/runtime/App_Resources/Android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  22. package/runtime/App_Resources/Android/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  23. package/runtime/App_Resources/Android/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  24. package/runtime/App_Resources/Android/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  25. package/runtime/App_Resources/Android/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  26. package/runtime/App_Resources/Android/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  27. package/runtime/App_Resources/Android/src/main/res/values/colors.xml +14 -0
  28. package/runtime/App_Resources/Android/src/main/res/values/ic_launcher_background.xml +4 -0
  29. package/runtime/App_Resources/Android/src/main/res/values/styles.xml +42 -0
  30. package/runtime/App_Resources/Android/src/main/res/values-v21/colors.xml +5 -0
  31. package/runtime/App_Resources/Android/src/main/res/values-v21/styles.xml +32 -0
  32. package/runtime/App_Resources/Android/src/main/res/values-v29/styles.xml +18 -0
  33. package/runtime/App_Resources/Android/src/main/res/xml/file_paths.xml +5 -0
  34. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json +122 -0
  35. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png +0 -0
  36. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-20.png +0 -0
  37. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png +0 -0
  38. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png +0 -0
  39. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png +0 -0
  40. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png +0 -0
  41. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png +0 -0
  42. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png +0 -0
  43. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png +0 -0
  44. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png +0 -0
  45. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png +0 -0
  46. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png +0 -0
  47. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png +0 -0
  48. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png +0 -0
  49. package/runtime/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png +0 -0
  50. package/runtime/App_Resources/iOS/Assets.xcassets/Contents.json +6 -0
  51. package/runtime/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json +23 -0
  52. package/runtime/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png +0 -0
  53. package/runtime/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png +0 -0
  54. package/runtime/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@3x.png +0 -0
  55. package/runtime/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json +23 -0
  56. package/runtime/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png +0 -0
  57. package/runtime/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png +0 -0
  58. package/runtime/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@3x.png +0 -0
  59. package/runtime/App_Resources/iOS/Info.plist +60 -0
  60. package/runtime/App_Resources/iOS/LaunchScreen.storyboard +53 -0
  61. package/runtime/App_Resources/iOS/Podfile +13 -0
  62. package/runtime/App_Resources/iOS/PrivacyInfo.xcprivacy +59 -0
  63. package/runtime/App_Resources/iOS/build.xcconfig +7 -0
  64. package/runtime/app/app.ts +58 -0
  65. package/runtime/app/main-page.ts +116 -0
  66. package/runtime/app/main-page.xml +10 -0
  67. package/runtime/app/shell/active-modules.generated.ts +9 -0
  68. package/runtime/app/shell/android-helpers.ts +76 -0
  69. package/runtime/app/shell/bridge.ts +99 -0
  70. package/runtime/app/shell/capabilities.manifest.ts +207 -0
  71. package/runtime/app/shell/config.ts +35 -0
  72. package/runtime/app/shell/custom-webview.android.ts +270 -0
  73. package/runtime/app/shell/custom-webview.ios.ts +258 -0
  74. package/runtime/app/shell/custom-webview.ts +17 -0
  75. package/runtime/app/shell/env.ts +81 -0
  76. package/runtime/app/shell/events.ts +100 -0
  77. package/runtime/app/shell/fcm-bootstrap.generated.ts +1 -0
  78. package/runtime/app/shell/fcm-firebase.d.ts +16 -0
  79. package/runtime/app/shell/fcm-service.android.ts +40 -0
  80. package/runtime/app/shell/fcm-service.ios.ts +3 -0
  81. package/runtime/app/shell/handlers-android.ts +417 -0
  82. package/runtime/app/shell/handlers-billing.ts +197 -0
  83. package/runtime/app/shell/handlers-extended.ts +405 -0
  84. package/runtime/app/shell/handlers-health.ts +134 -0
  85. package/runtime/app/shell/handlers-media.ts +126 -0
  86. package/runtime/app/shell/handlers-oauth.ts +69 -0
  87. package/runtime/app/shell/handlers-parity.ts +257 -0
  88. package/runtime/app/shell/handlers-push.ts +189 -0
  89. package/runtime/app/shell/handlers-system.ts +70 -0
  90. package/runtime/app/shell/handlers.ts +149 -0
  91. package/runtime/app/shell/ios-frameworks.d.ts +36 -0
  92. package/runtime/app/shell/ios-image.ts +15 -0
  93. package/runtime/app/shell/ios-ui-delegate.ts +131 -0
  94. package/runtime/app/shell/mime.ts +29 -0
  95. package/runtime/app/shell/native-log.ts +22 -0
  96. package/runtime/app/shell/optional-handlers.generated.ts +10 -0
  97. package/runtime/app/shell/orientation.ts +43 -0
  98. package/runtime/app/shell/status-bar.ts +129 -0
  99. package/runtime/app/shell/toast.ts +67 -0
  100. package/runtime/app/shell/web-quirks.ts +131 -0
  101. package/runtime/modules-native/health/App_Resources/Android/src/main/java/cc/livx/appwrap/HealthConnectBridge.kt +74 -0
  102. package/runtime/nativescript.config.ts +15 -0
  103. package/runtime/package.json +21 -0
  104. package/runtime/references.d.ts +2 -0
  105. package/runtime/tsconfig.json +19 -0
  106. package/runtime/webpack.config.js +27 -0
  107. package/scripts/stage-assets.mjs +21 -0
  108. package/src/cli.ts +1328 -0
  109. package/templates/ci/fastlane/Fastfile +43 -0
  110. package/templates/ci/fastlane/Matchfile +3 -0
  111. package/templates/ci/github/workflows/appwrap-pr.yml +28 -0
  112. package/templates/ci/github/workflows/appwrap-release-android.yml +51 -0
  113. package/templates/ci/github/workflows/appwrap-release-ios.yml +41 -0
@@ -0,0 +1,131 @@
1
+ import { SHELL_CONFIG } from './config';
2
+
3
+ /**
4
+ * Framework globals injected at document-start, BEFORE the page's own scripts. `__APPWRAP_BACKEND_ORIGIN__`
5
+ * is the absolute backend origin (empty on web / when unset) — exposed for any page code that wants it
6
+ * (e.g. the SDK's ingestUrl override for the WebSocket, which can't be proxied). Relative HTTP backend
7
+ * calls (`/api/*`, `/functions/*`) are NOT rewritten here — the native app:// scheme handler PROXIES
8
+ * them to the backend so they stay same-origin to the WebView (no CORS dependency). Empty = no-op on web.
9
+ */
10
+ export const APPWRAP_GLOBALS_JS = `
11
+ window.__APPWRAP_BACKEND_ORIGIN__=${JSON.stringify(SHELL_CONFIG.backendOrigin || '')};
12
+ window.__APPWRAP_DEBUG__=${SHELL_CONFIG.debug ? 'true' : 'false'};
13
+ (function(){
14
+ if (!window.__APPWRAP_DEBUG__ || window.__appwrapLogPatched) return;
15
+ window.__appwrapLogPatched = true;
16
+ // Dev: turn the PWA's own logger verbose (common localStorage.DEBUG convention) so the forwarded
17
+ // [appwrap-web] stream isn't empty. Set before the app's modules read it (document-start).
18
+ try { localStorage.setItem('DEBUG', ${JSON.stringify(SHELL_CONFIG.debugLog || '*')}); } catch (e) {}
19
+ // Debug only: forward the WebView's console + uncaught errors to the native log so they're
20
+ // readable HEADLESSLY (\`appwrap logs ios\` → devicectl --console). iOS posts to a message handler
21
+ // → NSLog; on Android webkit.messageHandlers is absent (caught) — use logcat / onConsoleMessage.
22
+ var post = function(s){ try { window.webkit.messageHandlers.appwrapLog.postMessage(s); } catch (e) {} };
23
+ post('[boot] web console forwarding active @ ' + (location && location.href || ''));
24
+ ['log','debug','info','warn','error'].forEach(function(lvl){
25
+ var orig = console[lvl] ? console[lvl].bind(console) : function(){};
26
+ console[lvl] = function(){
27
+ try {
28
+ var parts = [];
29
+ for (var i = 0; i < arguments.length; i++) {
30
+ var a = arguments[i];
31
+ try { parts.push(typeof a === 'string' ? a : JSON.stringify(a)); } catch (e) { parts.push(String(a)); }
32
+ }
33
+ post('[' + lvl + '] ' + parts.join(' '));
34
+ } catch (e) {}
35
+ orig.apply(null, arguments);
36
+ };
37
+ });
38
+ window.addEventListener('error', function(e){ post('[uncaught] ' + (e.message || e) + ' @ ' + (e.filename || '') + ':' + (e.lineno || 0)); });
39
+ window.addEventListener('unhandledrejection', function(e){ var r = e.reason; post('[rejection] ' + ((r && (r.stack || r.message)) || r)); });
40
+ })();`;
41
+
42
+ /**
43
+ * Reject `getUserMedia` for a capability this build did NOT declare (no Info.plist usage key), in JS at
44
+ * document-start — BEFORE the request reaches WebKit's native capture path. This is the ONLY layer that
45
+ * can stop the crash: to set up a capture session WebKit asks TCC to authorize the camera/mic, and on iOS
46
+ * a request for a privacy class with no usage string is a HARD PROCESS-KILL (not a catchable error). That
47
+ * request fires around the WKUIDelegate decision, so a native `deny` there can't prevent it — but if the
48
+ * call is rejected in JS, WebKit's native path is never entered. `camera`/`microphone` = the declared
49
+ * flags (iOS: presence of NSCamera/NSMicrophoneUsageDescription). Patches the prototype so a page can't
50
+ * dodge it via `MediaDevices.prototype.getUserMedia.call(...)`. Declared capabilities pass straight through.
51
+ */
52
+ export function mediaCaptureGuardJs(camera: boolean, microphone: boolean): string {
53
+ return `(function(){
54
+ var proto = window.MediaDevices && window.MediaDevices.prototype;
55
+ if (!proto || !proto.getUserMedia || window.__appwrapGumGuard) return;
56
+ window.__appwrapGumGuard = true;
57
+ var DECLARED = { video: ${!!camera}, audio: ${!!microphone} };
58
+ function missing(c){
59
+ var m = [];
60
+ if (c && c.video && !DECLARED.video) m.push('camera');
61
+ if (c && c.audio && !DECLARED.audio) m.push('microphone');
62
+ return m;
63
+ }
64
+ var orig = proto.getUserMedia;
65
+ proto.getUserMedia = function(constraints){
66
+ var m = missing(constraints);
67
+ if (m.length) return Promise.reject(new DOMException(
68
+ m.join(' & ') + ' not available (capability not enabled for this app)', 'NotAllowedError'));
69
+ return orig.call(this, constraints);
70
+ };
71
+ })();`;
72
+ }
73
+
74
+ /**
75
+ * Native-feel injection shared by both CustomWebViews. Suppresses the "it's a
76
+ * web page" tells: pinch / double-tap zoom, long-press callout & selection,
77
+ * overscroll glow/bounce, and iOS text auto-resizing. Injected at document
78
+ * start (and re-applied on DOMContentLoaded — at document-start <head> may be
79
+ * empty). Idempotent via a window guard, so double-injection is a no-op.
80
+ *
81
+ * Text selection is preserved inside inputs / [contenteditable] so forms and
82
+ * chat composers still work. Native zoom controls / overscroll mode are also
83
+ * turned off at the WebView level by each platform (belt-and-suspenders).
84
+ */
85
+ const NATIVE_FEEL_CSS = [
86
+ 'html{-webkit-text-size-adjust:100%;text-size-adjust:100%}',
87
+ 'html,body{overscroll-behavior:none;touch-action:manipulation}',
88
+ '*{-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}',
89
+ ':where(:not(input,textarea,[contenteditable],[contenteditable] *)){-webkit-user-select:none;user-select:none}',
90
+ ].join('');
91
+
92
+ /** Accessibility-standard reduced-motion reset — collapse animation/transition durations to ~0 and
93
+ * stop infinite loops (animation-iteration-count:1). Injected only when the env hint is set. */
94
+ const REDUCE_MOTION_CSS =
95
+ '*,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;' +
96
+ 'transition-duration:.001ms!important;scroll-behavior:auto!important}';
97
+
98
+ export const NATIVE_FEEL_JS = `(function(){
99
+ if (window.__appwrapNativeFeel) return;
100
+ window.__appwrapNativeFeel = true;
101
+ function apply(){
102
+ var head = document.head || document.documentElement;
103
+ var vp = document.querySelector('meta[name=viewport]');
104
+ if (!vp){ vp = document.createElement('meta'); vp.setAttribute('name','viewport'); head.appendChild(vp); }
105
+ var c = vp.getAttribute('content') || 'width=device-width, initial-scale=1';
106
+ if (!/user-scalable\\s*=\\s*no/.test(c)) c += ', maximum-scale=1, user-scalable=no';
107
+ // viewport-fit=cover makes env(safe-area-inset-*) resolve to real notch /
108
+ // home-indicator insets — the WebView owns the safe areas (contentInset .never).
109
+ if (!/viewport-fit/.test(c)) c += ', viewport-fit=cover';
110
+ vp.setAttribute('content', c);
111
+ if (!document.getElementById('__appwrap_feel')){
112
+ var s = document.createElement('style'); s.id = '__appwrap_feel';
113
+ s.textContent = ${JSON.stringify(NATIVE_FEEL_CSS)};
114
+ head.appendChild(s);
115
+ }
116
+ // Reduced-motion / software-GPU (emulator) safety net: neutralize infinite CSS animations &
117
+ // long transitions — they're free on a real GPU but continuously re-composite on the emulator's
118
+ // software rasterizer (host-CPU burn) and fight the OS reduce-motion setting. Gated on the env
119
+ // hint (see env.ts) so production on real devices is untouched; apps can still override.
120
+ var env = window.__APPWRAP__ || {};
121
+ if ((env.reduceMotion || env.isEmulator) && !document.getElementById('__appwrap_reduce_motion')){
122
+ var rm = document.createElement('style'); rm.id = '__appwrap_reduce_motion';
123
+ rm.textContent = ${JSON.stringify(REDUCE_MOTION_CSS)};
124
+ head.appendChild(rm);
125
+ }
126
+ }
127
+ apply();
128
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', apply);
129
+ // Pinch-zoom on iOS fires gesture* even with user-scalable=no in some builds.
130
+ document.addEventListener('gesturestart', function(e){ e.preventDefault(); }, { passive: false });
131
+ })();`;
@@ -0,0 +1,74 @@
1
+ package cc.livx.appwrap
2
+
3
+ import android.content.Context
4
+ import android.content.Intent
5
+ import androidx.health.connect.client.HealthConnectClient
6
+ import androidx.health.connect.client.PermissionController
7
+ import androidx.health.connect.client.permission.HealthPermission
8
+ import androidx.health.connect.client.records.StepsRecord
9
+ import androidx.health.connect.client.request.AggregateRequest
10
+ import androidx.health.connect.client.time.TimeRangeFilter
11
+ import kotlinx.coroutines.CoroutineScope
12
+ import kotlinx.coroutines.Dispatchers
13
+ import kotlinx.coroutines.launch
14
+ import kotlinx.coroutines.withContext
15
+ import java.time.Instant
16
+ import java.time.LocalDate
17
+ import java.time.ZoneId
18
+
19
+ /**
20
+ * NS-callable bridge over Health Connect (whose client API is Kotlin-`suspend`-only). Wraps each
21
+ * suspend call in a coroutine and delivers the result through a plain callback ON THE MAIN THREAD —
22
+ * so the JS continuation (and the WebView response delivery) runs on the UI thread. Shipped via the
23
+ * app's overrides/ until the framework grows first-class Health Connect support.
24
+ */
25
+ object HealthConnectBridge {
26
+ interface LongCallback { fun onResult(value: Long) }
27
+ interface BoolCallback { fun onResult(value: Boolean) }
28
+
29
+ private val READ_STEPS = HealthPermission.getReadPermission(StepsRecord::class)
30
+
31
+ @JvmStatic
32
+ fun isAvailable(context: Context): Boolean =
33
+ HealthConnectClient.getSdkStatus(context) == HealthConnectClient.SDK_AVAILABLE
34
+
35
+ /** Intent that launches the Health Connect permission UI (for startActivityForResult). */
36
+ @JvmStatic
37
+ fun permissionIntent(context: Context): Intent =
38
+ PermissionController.createRequestPermissionResultContract().createIntent(context, setOf(READ_STEPS))
39
+
40
+ @JvmStatic
41
+ fun hasPermission(context: Context, cb: BoolCallback) {
42
+ val client = HealthConnectClient.getOrCreate(context)
43
+ CoroutineScope(Dispatchers.IO).launch {
44
+ val granted = try {
45
+ client.permissionController.getGrantedPermissions().contains(READ_STEPS)
46
+ } catch (e: Exception) { false }
47
+ withContext(Dispatchers.Main) { cb.onResult(granted) }
48
+ }
49
+ }
50
+
51
+ /** Today's total steps. Prefers `aggregate` (dedupes across sources incl. Wear on real devices);
52
+ * falls back to summing raw records when aggregate yields null (a known HC quirk — e.g. the
53
+ * emulator provider / a single app's manual records). Returns -1 when permission isn't granted. */
54
+ private suspend fun todayTotal(client: HealthConnectClient): Long {
55
+ val zone = ZoneId.systemDefault()
56
+ val range = TimeRangeFilter.between(LocalDate.now(zone).atStartOfDay(zone).toInstant(), Instant.now())
57
+ val agg = client.aggregate(AggregateRequest(setOf(StepsRecord.COUNT_TOTAL), range))[StepsRecord.COUNT_TOTAL]
58
+ if (agg != null) return agg
59
+ val read = client.readRecords(androidx.health.connect.client.request.ReadRecordsRequest(StepsRecord::class, range))
60
+ return read.records.sumOf { it.count }
61
+ }
62
+
63
+ @JvmStatic
64
+ fun readTodaySteps(context: Context, cb: LongCallback) {
65
+ val client = HealthConnectClient.getOrCreate(context)
66
+ CoroutineScope(Dispatchers.IO).launch {
67
+ val result: Long = try {
68
+ if (!client.permissionController.getGrantedPermissions().contains(READ_STEPS)) -1L
69
+ else todayTotal(client)
70
+ } catch (e: Exception) { -2L }
71
+ withContext(Dispatchers.Main) { cb.onResult(result) }
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,15 @@
1
+ import { NativeScriptConfig } from '@nativescript/core';
2
+
3
+ export default {
4
+ // Stamped by `appwrap init` from appwrap.json
5
+ id: 'cc.livx.hellowrap',
6
+ appPath: 'app',
7
+ appResourcesPath: 'App_Resources',
8
+ ios: {
9
+ discardUncaughtJsExceptions: false,
10
+ },
11
+ android: {
12
+ v8Flags: '--expose_gc',
13
+ markingMode: 'none',
14
+ },
15
+ } as NativeScriptConfig;
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "appwrap-shell",
3
+ "main": "app/app.ts",
4
+ "version": "0.18.0",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev:ios": "ns run ios",
8
+ "build:ios": "ns build ios"
9
+ },
10
+ "dependencies": {
11
+ "@nativescript/core": "~9.0.0",
12
+ "@nativescript/secure-storage": "^4.0.1"
13
+ },
14
+ "devDependencies": {
15
+ "@nativescript/ios": "9.0.2",
16
+ "@nativescript/types": "~9.0.0",
17
+ "@nativescript/webpack": "~5.0.25",
18
+ "copy-webpack-plugin": "^13.0.1",
19
+ "typescript": "~5.4.0"
20
+ }
21
+ }
@@ -0,0 +1,2 @@
1
+ /// <reference path="./node_modules/@nativescript/types-ios/index.d.ts" />
2
+ /// <reference path="./node_modules/@nativescript/types-android/lib/android-32.d.ts" />
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "esnext",
4
+ "target": "es2020",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["es2021", "dom"],
7
+ "experimentalDecorators": true,
8
+ "removeComments": false,
9
+ "noEmitHelpers": true,
10
+ "noEmitOnError": true,
11
+ "skipLibCheck": true,
12
+ "baseUrl": ".",
13
+ "paths": {
14
+ "~/*": ["app/*"]
15
+ }
16
+ },
17
+ "include": ["app/**/*.ts", "references.d.ts"],
18
+ "exclude": ["node_modules", "platforms", "app/www"]
19
+ }
@@ -0,0 +1,27 @@
1
+ const webpack = require('@nativescript/webpack');
2
+ const CopyWebpackPlugin = require('copy-webpack-plugin');
3
+ const path = require('path');
4
+
5
+ module.exports = (env) => {
6
+ webpack.init(env);
7
+
8
+ webpack.chainWebpack((config) => {
9
+ // Ship the PWA bundle inside the app package
10
+ config.plugin('CopyWww').use(CopyWebpackPlugin, [
11
+ {
12
+ patterns: [
13
+ {
14
+ // Staged OUTSIDE appPath (see CLI copyPwa) so NS loaders never touch the web
15
+ // app's CSS/assets — copied verbatim into the bundle's `www`, served at app://.
16
+ from: path.resolve(__dirname, 'www-src'),
17
+ to: 'www',
18
+ noErrorOnMissing: true, // server loader: no bundled www (loads serverUrl live)
19
+ force: true,
20
+ },
21
+ ],
22
+ },
23
+ ]);
24
+ });
25
+
26
+ return webpack.resolveConfig();
27
+ };
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ // prepack: stage runtime/ + templates/ into the package so the published tarball is self-contained.
3
+ // The CLI resolves them at the package root (see resolveAssetRoot in src/cli.ts). Excludes build
4
+ // artifacts (node_modules, platforms, hooks, app/www) and OS cruft. Gitignored — tarball-only.
5
+ import { cpSync, rmSync } from 'node:fs';
6
+ import { resolve, dirname } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const pkgDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
10
+ const repoRoot = resolve(pkgDir, '../..');
11
+ const EXCLUDE = /(?:^|\/)(?:node_modules|platforms|hooks|app\/www|\.DS_Store)(?:\/|$)/;
12
+
13
+ for (const name of ['runtime', 'templates']) {
14
+ const srcRoot = resolve(repoRoot, name);
15
+ const dest = resolve(pkgDir, name);
16
+ rmSync(dest, { recursive: true, force: true });
17
+ // Match RELATIVE to srcRoot — testing the absolute path would wrongly exclude everything when the
18
+ // repo is checked out under a path containing "node_modules"/"hooks" (e.g. some CI layouts).
19
+ cpSync(srcRoot, dest, { recursive: true, filter: (src) => !EXCLUDE.test(src.slice(srcRoot.length)) });
20
+ console.log(`staged ${name}/`);
21
+ }