@amplytools/react-native-amply-sdk 0.11.0 → 0.12.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.
@@ -22,7 +22,7 @@ Pod::Spec.new do |s|
22
22
  s.dependency 'React-RCTFabric'
23
23
 
24
24
  # Amply KMP SDK from CocoaPods
25
- s.dependency 'AmplySDK', '~> 0.11.0'
25
+ s.dependency 'AmplySDK', '~> 0.12.0'
26
26
 
27
27
  s.libraries = 'c++'
28
28
  s.pod_target_xcconfig = {
@@ -15,7 +15,7 @@ buildscript {
15
15
  apply plugin: 'com.android.library'
16
16
  apply plugin: 'org.jetbrains.kotlin.android'
17
17
 
18
- def amplySdkVersion = project.findProperty('amplySdkVersion') ?: System.getenv('AMPLY_SDK_VERSION') ?: '0.11.0'
18
+ def amplySdkVersion = project.findProperty('amplySdkVersion') ?: System.getenv('AMPLY_SDK_VERSION') ?: '0.12.0'
19
19
  group = 'tools.amply'
20
20
  version = amplySdkVersion
21
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amplytools/react-native-amply-sdk",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "React Native SDK for Amply: Mobile SDK to orchestrate in-app experiences and campaigns remotely, without app releases",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,12 +33,13 @@
33
33
  "build": "expo-module build",
34
34
  "build:plugin": "expo-module build plugin",
35
35
  "clean": "expo-module clean",
36
- "test": "node scripts/codegen.js --check && expo-module test && yarn check:seam-tokens",
36
+ "test": "node scripts/codegen.js --check && expo-module test && yarn check:seam-tokens && yarn check:sample-parity",
37
37
  "prepare": "expo-module prepare",
38
38
  "prepublishOnly": "expo-module prepublishOnly",
39
39
  "codegen": "node scripts/codegen.js",
40
40
  "codegen:check": "node scripts/codegen.js --check",
41
- "check:seam-tokens": "bash scripts/check-seam-tokens.sh"
41
+ "check:seam-tokens": "bash scripts/check-seam-tokens.sh",
42
+ "check:sample-parity": "bash scripts/check-sample-parity.sh"
42
43
  },
43
44
  "peerDependencies": {
44
45
  "expo": ">=54.0.0",
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Every control the sample spec requires must exist in every sample that claims the
4
+ # platform, tagged with its manifest id. Nothing else notices when one of them is missing.
5
+ #
6
+ # The six Amply samples are six copies of the same screen, and they drifted because
7
+ # copy-paste has no compiler. In the KMP repo, `ios-spm-sample/HomeViewController.swift`
8
+ # reached 886 lines while its CocoaPods twin sat at 566 — the same screen, forked and then
9
+ # edited on one side only, with `GateDemo.swift` existing in exactly one of them. Both
10
+ # projects built clean the whole time. Nobody noticed, because nothing checked. The two RN
11
+ # examples here forked the same way: `bare/src/hooks/useAmplyDemo.ts` and
12
+ # `expo/src/hooks/useAmplyDemo.ts` are two copies of one hook, and `bare` still imports the
13
+ # SDK under a legacy alias no integrator would type.
14
+ #
15
+ # This is that check. It greps each example's own sources for every manifest id required on
16
+ # the `rn` column and fails naming the id, the example, and where it looked. It is a parity
17
+ # check, not a UI test: it answers "is this control present in this sample", which is the
18
+ # only question consistency actually turns on.
19
+ #
20
+ # Spec: multiplatform-library-template/samples/SAMPLE_APP_SPEC.md (canonical, KMP repo)
21
+ # Manifest: example/sample-manifest.txt (verbatim copy of the KMP repo's canonical file;
22
+ # this script diffs the two when both are present)
23
+ #
24
+ # Usage: bash scripts/check-sample-parity.sh
25
+ set -uo pipefail
26
+
27
+ cd "$(dirname "$0")/.."
28
+
29
+ MANIFEST="example/sample-manifest.txt"
30
+ SPEC_PATH="multiplatform-library-template/samples/SAMPLE_APP_SPEC.md"
31
+
32
+ if [ ! -f "$MANIFEST" ]; then
33
+ echo "check-sample-parity: FAIL — missing $MANIFEST" >&2
34
+ exit 1
35
+ fi
36
+
37
+ # Quote characters an id can be wrapped in: JSX uses ", JS also uses ' and ` for template
38
+ # literals. The id literal must sit next to `testID` on one line.
39
+ Q="[\"'\`]"
40
+
41
+ # ---------------------------------------------------------------------------------------
42
+ # The canonical copy. The manifest lives in the KMP repo; this repo carries a duplicate
43
+ # because it is published to npm and mirrored publicly and so cannot depend on a sibling
44
+ # checkout existing. Duplication that nothing checks is drift with extra steps, so: when
45
+ # the KMP repo is reachable, diff it and fail on any difference. When it is not, say so
46
+ # loudly and print the revision, so a stale copy shows up in the output instead of passing
47
+ # silently.
48
+ # ---------------------------------------------------------------------------------------
49
+ revision=$(grep -m1 '^# revision:' "$MANIFEST" | sed 's/^# revision:[[:space:]]*//')
50
+ if [ -z "$revision" ]; then
51
+ echo "check-sample-parity: FAIL — $MANIFEST has no '# revision:' header" >&2
52
+ exit 1
53
+ fi
54
+
55
+ peer=""
56
+ for candidate in \
57
+ "${AMPLY_SAMPLE_MANIFEST_PEER:-}" \
58
+ "../multiplatform-library-template/samples/sample-manifest.txt"
59
+ do
60
+ [ -n "$candidate" ] && [ -f "$candidate" ] && { peer="$candidate"; break; }
61
+ done
62
+
63
+ if [ -n "$peer" ]; then
64
+ if ! diff -u "$peer" "$MANIFEST" > /tmp/check-sample-parity-peer.diff 2>&1; then
65
+ echo "check-sample-parity: FAIL — this repo's manifest copy has diverged from the canonical one." >&2
66
+ echo " canonical: $peer" >&2
67
+ echo " copy: $MANIFEST" >&2
68
+ sed 's/^/ /' /tmp/check-sample-parity-peer.diff >&2
69
+ rm -f /tmp/check-sample-parity-peer.diff
70
+ exit 1
71
+ fi
72
+ rm -f /tmp/check-sample-parity-peer.diff
73
+ peer_state="verified byte-identical against $peer"
74
+ else
75
+ peer_state="NOT VERIFIED — the KMP repo was not found; set AMPLY_SAMPLE_MANIFEST_PEER to check it"
76
+ fi
77
+
78
+ # ---------------------------------------------------------------------------------------
79
+ # Parse the manifest into: id <TAB> android <TAB> ios <TAB> rn <TAB> note
80
+ # ---------------------------------------------------------------------------------------
81
+ rows=$(
82
+ awk -F'|' '
83
+ /^[[:space:]]*#/ { next }
84
+ /^[[:space:]]*$/ { next }
85
+ NF < 4 { next }
86
+ {
87
+ for (i = 1; i <= NF; i++) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", $i) }
88
+ note = (NF >= 5) ? $5 : ""
89
+ print $1 "\t" $2 "\t" $3 "\t" $4 "\t" note
90
+ }
91
+ ' "$MANIFEST"
92
+ )
93
+
94
+ if [ -z "$rows" ]; then
95
+ echo "check-sample-parity: FAIL — parsed zero rows from $MANIFEST; the parser is broken" >&2
96
+ exit 1
97
+ fi
98
+
99
+ row_count=$(printf '%s\n' "$rows" | wc -l | tr -d ' ')
100
+
101
+ # ---------------------------------------------------------------------------------------
102
+ # The examples. Each one searches its own sources plus the shared UI module. An absent root
103
+ # is skipped rather than fatal; an example with NO existing root at all is fatal, because
104
+ # that means this list is stale.
105
+ #
106
+ # The shared module is on BOTH examples' search paths, and that is a trap this check walks
107
+ # into unless it is told not to. A control id living in example/shared satisfies the grep
108
+ # for bare and expo alike — including for a host that never imports the module and still
109
+ # renders its own old screen. That is not hypothetical: on the day the shared module landed,
110
+ # `expo` reported "all 40 required controls present" while app/index.tsx was still building
111
+ # its own ScrollView out of useAmplyDemo. Green, and completely wrong.
112
+ #
113
+ # The native samples do not have this hole. Both Xcode projects list ../ios-shared/Sources
114
+ # and both Gradle modules include the shared srcDir, so on those platforms presence in the
115
+ # shared source IS compilation into the target. Metro resolving a package is not the same
116
+ # claim: a module on the search path is only rendered if something imports it.
117
+ #
118
+ # So each RN host must also prove it MOUNTS the shared module. `mount` below is a file that
119
+ # must exist and must import SHARED_PKG. Without this, the consolidation can be reported
120
+ # complete while half of it was never wired.
121
+ # ---------------------------------------------------------------------------------------
122
+ SHARED_PKG="@amply/sample-shared"
123
+ samples=(
124
+ "bare|example/bare/src example/shared|example/bare/src/App.tsx"
125
+ "expo|example/expo/app example/expo/src example/expo/components example/shared|example/expo/app/index.tsx"
126
+ )
127
+
128
+ ATTR="testID"
129
+ INCLUDES="--include=*.ts --include=*.tsx --include=*.js --include=*.jsx"
130
+
131
+ fail=0
132
+ checked_total=0
133
+
134
+ echo "check-sample-parity: manifest revision $revision — $row_count rows"
135
+ echo " peer copy: $peer_state"
136
+ echo
137
+
138
+ for entry in "${samples[@]}"; do
139
+ name="${entry%%|*}"
140
+ rest="${entry#*|}"
141
+ roots="${rest%%|*}"
142
+ mount="${rest#*|}"
143
+
144
+ existing_roots=""
145
+ for r in $roots; do
146
+ [ -d "$r" ] && existing_roots="$existing_roots $r"
147
+ done
148
+
149
+ if [ -z "$existing_roots" ]; then
150
+ echo "check-sample-parity: FAIL — example '$name' has no source root on disk (looked in: $roots)" >&2
151
+ echo " the sample list in this script is stale." >&2
152
+ fail=1
153
+ continue
154
+ fi
155
+
156
+ # Does this host actually MOUNT the shared module, or does it merely have it on a search
157
+ # path? Only the second is true of a host still rendering its own screen, and the id grep
158
+ # below cannot tell the difference — every shared id would satisfy it either way.
159
+ if [ -d "example/shared" ]; then
160
+ if [ ! -f "$mount" ]; then
161
+ echo "check-sample-parity: FAIL — example '$name' has no entry point at $mount" >&2
162
+ echo " the mount path in this script is stale." >&2
163
+ fail=1
164
+ continue
165
+ fi
166
+ if ! grep -qF "$SHARED_PKG" "$mount"; then
167
+ echo "check-sample-parity: FAIL — example '$name' does not mount the shared UI module." >&2
168
+ echo " $mount never imports $SHARED_PKG, so this example still" >&2
169
+ echo " renders its own screen. Every control id below would be" >&2
170
+ echo " satisfied by example/shared regardless — a green that means" >&2
171
+ echo " nothing. Mount the shared module in $mount." >&2
172
+ fail=1
173
+ continue
174
+ fi
175
+ fi
176
+
177
+ missing=()
178
+ unbound=()
179
+ required=0
180
+
181
+ while IFS=$'\t' read -r id android ios rn note; do
182
+ [ "$rn" = "x" ] || continue
183
+ required=$((required + 1))
184
+
185
+ id_esc=$(printf '%s' "$id" | sed 's/[.[\*^$()+?{}|\\]/\\&/g')
186
+
187
+ case "$note" in
188
+ launcharg:*)
189
+ # Not a UI control — the launch key just has to appear in the source. On RN the
190
+ # transport is a deeplink query param rather than a launch argument (neither
191
+ # example can read one without a new native dependency); the key spelling is the
192
+ # same, which is why one manifest row covers all three platforms.
193
+ # shellcheck disable=SC2086
194
+ if ! grep -RIlE $INCLUDES -e "$id_esc" $existing_roots > /dev/null 2>&1; then
195
+ missing+=("$id")
196
+ fi
197
+ continue
198
+ ;;
199
+ prefix:*) pattern="${Q}${id_esc}\\." ;;
200
+ *) pattern="${Q}${id_esc}${Q}" ;;
201
+ esac
202
+
203
+ # shellcheck disable=SC2086
204
+ hits=$(grep -RInE $INCLUDES -e "$pattern" $existing_roots 2>/dev/null)
205
+ if [ -z "$hits" ]; then
206
+ missing+=("$id")
207
+ continue
208
+ fi
209
+ if ! printf '%s\n' "$hits" | grep -q "$ATTR"; then
210
+ unbound+=("$id")
211
+ fi
212
+ done <<< "$rows"
213
+
214
+ checked_total=$((checked_total + required))
215
+
216
+ if [ ${#missing[@]} -eq 0 ] && [ ${#unbound[@]} -eq 0 ]; then
217
+ echo " OK $name (rn) — all $required required controls present"
218
+ continue
219
+ fi
220
+
221
+ fail=1
222
+ echo " FAIL $name (rn) — $((${#missing[@]} + ${#unbound[@]})) of $required required controls not satisfied"
223
+ echo " searched:$existing_roots"
224
+ if [ ${#missing[@]} -gt 0 ]; then
225
+ echo " missing (${#missing[@]}):"
226
+ for id in "${missing[@]}"; do echo " - $id"; done
227
+ fi
228
+ if [ ${#unbound[@]} -gt 0 ]; then
229
+ echo " present but not on a line with \`$ATTR\` (${#unbound[@]}):"
230
+ for id in "${unbound[@]}"; do echo " - $id"; done
231
+ fi
232
+ echo
233
+ done
234
+
235
+ # ---------------------------------------------------------------------------------------
236
+ # Retired ids: controls that were required once and must NOT come back. A presence-only
237
+ # check cannot see a control that should have been deleted, so without this a removal is
238
+ # held together by trust. Failed on sight, wherever they appear.
239
+ # ---------------------------------------------------------------------------------------
240
+ retired_ids=$(grep -E '^# +retired: +amply\.' "$MANIFEST" | sed -E 's/^# +retired: +([^ ]+).*/\1/')
241
+ for rid in $retired_ids; do
242
+ rid_esc=$(printf '%s' "$rid" | sed 's/[.[\*^$()+?{}|\\]/\\&/g')
243
+ # shellcheck disable=SC2086
244
+ retired_roots=""
245
+ for r in example/bare/src example/expo/app example/expo/src example/expo/components example/shared; do [ -d "$r" ] && retired_roots="$retired_roots $r"; done
246
+ # shellcheck disable=SC2086
247
+ hits=$(grep -RIlE --include=*.ts --include=*.tsx --include=*.js --include=*.jsx -e "${Q}${rid_esc}${Q}" $retired_roots 2>/dev/null || true)
248
+ if [ -n "$hits" ]; then
249
+ echo "check-sample-parity: FAIL — retired control '$rid' is still in the source:" >&2
250
+ printf ' %s\n' $hits >&2
251
+ echo " It was deliberately removed; see the manifest's Retired block." >&2
252
+ fail=1
253
+ fi
254
+ done
255
+
256
+ if [ "$fail" -ne 0 ]; then
257
+ echo "check-sample-parity: FAIL" >&2
258
+ echo " Each missing id is a control $SPEC_PATH requires and the example" >&2
259
+ echo " does not have. Tag the real widget with it: testID=\"<id>\"." >&2
260
+ exit 1
261
+ fi
262
+
263
+ echo "check-sample-parity: OK — manifest revision $revision, $checked_total control checks across ${#samples[@]} examples."