react-native-intune 0.1.0-alpha.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.
Files changed (110) hide show
  1. package/LICENSE +27 -0
  2. package/NOTICE +49 -0
  3. package/README.md +164 -0
  4. package/RNIntune.podspec +69 -0
  5. package/android/build.gradle +152 -0
  6. package/android/consumer-rules.pro +28 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/java/com/reactnativeintune/RNIntuneAuth.kt +330 -0
  9. package/android/src/main/java/com/reactnativeintune/RNIntuneAuthCallback.kt +195 -0
  10. package/android/src/main/java/com/reactnativeintune/RNIntuneConfig.kt +63 -0
  11. package/android/src/main/java/com/reactnativeintune/RNIntuneEventQueue.kt +50 -0
  12. package/android/src/main/java/com/reactnativeintune/RNIntuneModule.kt +752 -0
  13. package/android/src/main/java/com/reactnativeintune/RNIntuneMsalConfig.kt +78 -0
  14. package/android/src/main/java/com/reactnativeintune/RNIntuneNotifications.kt +187 -0
  15. package/android/src/main/java/com/reactnativeintune/RNIntunePackage.kt +33 -0
  16. package/android/src/main/java/com/reactnativeintune/RNIntunePolicy.kt +108 -0
  17. package/android/src/main/java/com/reactnativeintune/RNIntuneReset.kt +85 -0
  18. package/android/src/main/java/com/reactnativeintune/RNIntuneResetJournal.kt +140 -0
  19. package/app.plugin.js +10 -0
  20. package/ios/RNIntune.h +20 -0
  21. package/ios/RNIntune.mm +467 -0
  22. package/ios/RNIntuneAuth.h +121 -0
  23. package/ios/RNIntuneAuth.m +390 -0
  24. package/ios/RNIntuneCore.h +150 -0
  25. package/ios/RNIntuneCore.m +535 -0
  26. package/ios/RNIntuneDelegates.h +70 -0
  27. package/ios/RNIntuneDelegates.m +268 -0
  28. package/ios/RNIntunePendingRequests.h +57 -0
  29. package/ios/RNIntunePendingRequests.m +230 -0
  30. package/ios/RNIntunePlistGuard.h +37 -0
  31. package/ios/RNIntunePlistGuard.m +147 -0
  32. package/ios/RNIntunePolicy.h +25 -0
  33. package/ios/RNIntunePolicy.m +112 -0
  34. package/ios/RNIntuneReset.h +34 -0
  35. package/ios/RNIntuneReset.m +93 -0
  36. package/ios/RNIntuneResetJournal.h +69 -0
  37. package/ios/RNIntuneResetJournal.m +106 -0
  38. package/lib/module/NativeIntune.js +20 -0
  39. package/lib/module/NativeIntune.js.map +1 -0
  40. package/lib/module/api/auth.js +66 -0
  41. package/lib/module/api/auth.js.map +1 -0
  42. package/lib/module/api/enrollment.js +104 -0
  43. package/lib/module/api/enrollment.js.map +1 -0
  44. package/lib/module/api/events.js +50 -0
  45. package/lib/module/api/events.js.map +1 -0
  46. package/lib/module/api/lifecycle.js +73 -0
  47. package/lib/module/api/lifecycle.js.map +1 -0
  48. package/lib/module/api/policy.js +23 -0
  49. package/lib/module/api/policy.js.map +1 -0
  50. package/lib/module/api/tokenProvider.js +80 -0
  51. package/lib/module/api/tokenProvider.js.map +1 -0
  52. package/lib/module/index.js +82 -0
  53. package/lib/module/index.js.map +1 -0
  54. package/lib/module/internal/decode.js +153 -0
  55. package/lib/module/internal/decode.js.map +1 -0
  56. package/lib/module/internal/emitter.js +15 -0
  57. package/lib/module/internal/emitter.js.map +1 -0
  58. package/lib/module/internal/errors.js +32 -0
  59. package/lib/module/internal/errors.js.map +1 -0
  60. package/lib/module/package.json +1 -0
  61. package/lib/module/types.js +143 -0
  62. package/lib/module/types.js.map +1 -0
  63. package/lib/typescript/package.json +1 -0
  64. package/lib/typescript/src/NativeIntune.d.ts +120 -0
  65. package/lib/typescript/src/NativeIntune.d.ts.map +1 -0
  66. package/lib/typescript/src/api/auth.d.ts +37 -0
  67. package/lib/typescript/src/api/auth.d.ts.map +1 -0
  68. package/lib/typescript/src/api/enrollment.d.ts +54 -0
  69. package/lib/typescript/src/api/enrollment.d.ts.map +1 -0
  70. package/lib/typescript/src/api/events.d.ts +24 -0
  71. package/lib/typescript/src/api/events.d.ts.map +1 -0
  72. package/lib/typescript/src/api/lifecycle.d.ts +33 -0
  73. package/lib/typescript/src/api/lifecycle.d.ts.map +1 -0
  74. package/lib/typescript/src/api/policy.d.ts +14 -0
  75. package/lib/typescript/src/api/policy.d.ts.map +1 -0
  76. package/lib/typescript/src/api/tokenProvider.d.ts +18 -0
  77. package/lib/typescript/src/api/tokenProvider.d.ts.map +1 -0
  78. package/lib/typescript/src/index.d.ts +67 -0
  79. package/lib/typescript/src/index.d.ts.map +1 -0
  80. package/lib/typescript/src/internal/decode.d.ts +48 -0
  81. package/lib/typescript/src/internal/decode.d.ts.map +1 -0
  82. package/lib/typescript/src/internal/emitter.d.ts +11 -0
  83. package/lib/typescript/src/internal/emitter.d.ts.map +1 -0
  84. package/lib/typescript/src/internal/errors.d.ts +22 -0
  85. package/lib/typescript/src/internal/errors.d.ts.map +1 -0
  86. package/lib/typescript/src/types.d.ts +317 -0
  87. package/lib/typescript/src/types.d.ts.map +1 -0
  88. package/package.json +204 -0
  89. package/plugin/transforms.js +153 -0
  90. package/plugin/withIntune.js +320 -0
  91. package/scripts/cli.mjs +72 -0
  92. package/scripts/doctor.mjs +232 -0
  93. package/scripts/fetch-sdks.mjs +456 -0
  94. package/scripts/lib/checks.mjs +720 -0
  95. package/scripts/lib/project.mjs +196 -0
  96. package/scripts/setup.mjs +233 -0
  97. package/sdk-lock.json +27 -0
  98. package/sdk-versions.json +70 -0
  99. package/src/NativeIntune.ts +130 -0
  100. package/src/api/auth.ts +73 -0
  101. package/src/api/enrollment.ts +112 -0
  102. package/src/api/events.ts +87 -0
  103. package/src/api/lifecycle.ts +75 -0
  104. package/src/api/policy.ts +22 -0
  105. package/src/api/tokenProvider.ts +80 -0
  106. package/src/index.ts +127 -0
  107. package/src/internal/decode.ts +191 -0
  108. package/src/internal/emitter.ts +14 -0
  109. package/src/internal/errors.ts +43 -0
  110. package/src/types.ts +381 -0
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Locates the consumer's project files.
3
+ *
4
+ * `doctor` and `setup` run from the consumer's project root — `npx react-native-intune
5
+ * doctor` — so everything here resolves from `process.cwd()`, never from this package's
6
+ * own directory. Getting that backwards would have the tool inspect itself and report
7
+ * a perfectly configured project every time.
8
+ *
9
+ * Every accessor returns `null` rather than throwing when a file is absent. A React
10
+ * Native project missing its Android folder is a normal state (iOS-only app), not an
11
+ * error, and a tool that crashes on it is a tool people stop running.
12
+ */
13
+
14
+ import fs from 'node:fs';
15
+ import path from 'node:path';
16
+
17
+ export function findProject(cwd = process.cwd()) {
18
+ const root = findProjectRoot(cwd);
19
+ return {
20
+ root,
21
+ isThisPackage: isThisPackage(root),
22
+ android: findAndroid(root),
23
+ ios: findIos(root),
24
+ };
25
+ }
26
+
27
+ /**
28
+ * True when the "project" found is this library's own repository.
29
+ *
30
+ * It qualifies on the surface — a package.json beside an `android/` directory — and
31
+ * configuring it would be the exact mistake these tools exist to prevent: the MAM plugin
32
+ * applied to the library rewrites only the library, builds cleanly, and protects
33
+ * nothing. Refusing here is cheaper than explaining it later.
34
+ */
35
+ function isThisPackage(root) {
36
+ try {
37
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
38
+ return pkg.name === 'react-native-intune';
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Walks up looking for a `package.json` next to an `android/` or `ios/` directory.
46
+ *
47
+ * Not just the nearest `package.json`: in a monorepo that is often a workspace package
48
+ * with no native project in it, and the app lives a level up or across. Requiring the
49
+ * native folder alongside is what distinguishes "the app" from "a package".
50
+ */
51
+ function findProjectRoot(cwd) {
52
+ let dir = path.resolve(cwd);
53
+ for (;;) {
54
+ const hasManifest = fs.existsSync(path.join(dir, 'package.json'));
55
+ const hasNative =
56
+ fs.existsSync(path.join(dir, 'android')) ||
57
+ fs.existsSync(path.join(dir, 'ios'));
58
+ if (hasManifest && hasNative) {
59
+ return dir;
60
+ }
61
+ const parent = path.dirname(dir);
62
+ if (parent === dir) {
63
+ // No native project above us. Return the starting point so the caller reports
64
+ // "nothing to inspect" against a path the user recognises.
65
+ return path.resolve(cwd);
66
+ }
67
+ dir = parent;
68
+ }
69
+ }
70
+
71
+ function findAndroid(root) {
72
+ const dir = path.join(root, 'android');
73
+ if (!fs.existsSync(dir)) {
74
+ return null;
75
+ }
76
+ return {
77
+ dir,
78
+ appBuildGradle: existing(path.join(dir, 'app', 'build.gradle')),
79
+ // Kotlin DSL is not handled by `setup`'s text insertion, but `doctor` still needs to
80
+ // see it so it can say so instead of reporting a missing plugin.
81
+ appBuildGradleKts: existing(path.join(dir, 'app', 'build.gradle.kts')),
82
+ rootBuildGradle: existing(path.join(dir, 'build.gradle')),
83
+ rootBuildGradleKts: existing(path.join(dir, 'build.gradle.kts')),
84
+ manifest: existing(path.join(dir, 'app', 'src', 'main', 'AndroidManifest.xml')),
85
+ gradleProperties: existing(path.join(dir, 'gradle.properties')),
86
+ debugKeystore: existing(path.join(dir, 'app', 'debug.keystore')),
87
+ // The Application subclass, if there is one to inspect. Both languages, both of the
88
+ // conventional source roots.
89
+ applicationSources: findApplicationSources(dir),
90
+ };
91
+ }
92
+
93
+ function findIos(root) {
94
+ const dir = path.join(root, 'ios');
95
+ if (!fs.existsSync(dir)) {
96
+ return null;
97
+ }
98
+ const xcodeproj = firstMatching(dir, (name) => name.endsWith('.xcodeproj'));
99
+ const appName = xcodeproj ? path.basename(xcodeproj, '.xcodeproj') : null;
100
+ const appDir = appName ? existing(path.join(dir, appName)) : null;
101
+
102
+ return {
103
+ dir,
104
+ appName,
105
+ xcodeproj,
106
+ pbxproj: xcodeproj ? existing(path.join(xcodeproj, 'project.pbxproj')) : null,
107
+ infoPlist: appDir ? existing(path.join(appDir, 'Info.plist')) : null,
108
+ // Xcode's default name, which is what the template produces. A custom name is
109
+ // reported as "not found" rather than guessed at — a wrong entitlements file edited
110
+ // silently is worse than an honest miss.
111
+ entitlements: appDir
112
+ ? existing(path.join(appDir, `${appName}.entitlements`))
113
+ : null,
114
+ appDelegate: appDir ? findAppDelegate(appDir) : null,
115
+ podfile: existing(path.join(dir, 'Podfile')),
116
+ };
117
+ }
118
+
119
+ function findApplicationSources(androidDir) {
120
+ const roots = [
121
+ path.join(androidDir, 'app', 'src', 'main', 'java'),
122
+ path.join(androidDir, 'app', 'src', 'main', 'kotlin'),
123
+ ].filter((p) => fs.existsSync(p));
124
+
125
+ const found = [];
126
+ for (const r of roots) {
127
+ walk(r, (file) => {
128
+ if (/(Application)\.(kt|java)$/.test(path.basename(file))) {
129
+ found.push(file);
130
+ }
131
+ });
132
+ }
133
+ return found;
134
+ }
135
+
136
+ function findAppDelegate(appDir) {
137
+ for (const name of ['AppDelegate.swift', 'AppDelegate.mm', 'AppDelegate.m']) {
138
+ const p = path.join(appDir, name);
139
+ if (fs.existsSync(p)) {
140
+ return p;
141
+ }
142
+ }
143
+ return null;
144
+ }
145
+
146
+ // ---------------------------------------------------------------- small helpers
147
+
148
+ function existing(p) {
149
+ return fs.existsSync(p) ? p : null;
150
+ }
151
+
152
+ function firstMatching(dir, predicate) {
153
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
154
+ for (const e of entries) {
155
+ if (predicate(e.name)) {
156
+ return path.join(dir, e.name);
157
+ }
158
+ }
159
+ return null;
160
+ }
161
+
162
+ function walk(dir, onFile, depth = 0) {
163
+ // Bounded: a source tree deeper than this is not holding an Application class, and an
164
+ // unbounded walk over node_modules in a misconfigured project is a hang.
165
+ if (depth > 12) {
166
+ return;
167
+ }
168
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
169
+ const p = path.join(dir, e.name);
170
+ if (e.isDirectory()) {
171
+ if (e.name === 'node_modules' || e.name === 'build' || e.name.startsWith('.')) {
172
+ continue;
173
+ }
174
+ walk(p, onFile, depth + 1);
175
+ } else {
176
+ onFile(p);
177
+ }
178
+ }
179
+ }
180
+
181
+ /** Reads a file, or returns an empty string. Callers treat absent and empty alike. */
182
+ export function read(file) {
183
+ if (!file) {
184
+ return '';
185
+ }
186
+ try {
187
+ return fs.readFileSync(file, 'utf8');
188
+ } catch {
189
+ return '';
190
+ }
191
+ }
192
+
193
+ /** Path relative to the project root, for output a user can act on. */
194
+ export function rel(project, file) {
195
+ return file ? path.relative(project.root, file) : '(not found)';
196
+ }
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Applies the changes `doctor` reports, on command and never automatically.
4
+ *
5
+ * These files live in the consumer's git. A script that edits them at install time
6
+ * fights the developer's own changes, re-applies on every version bump, and produces
7
+ * "why did my project change" reports — which is why mature libraries abandoned
8
+ * install-time patching (SPEC §12.7).
9
+ *
10
+ * Usage:
11
+ * npx react-native-intune setup
12
+ * npx react-native-intune setup --yes # no prompt, for CI
13
+ * npx react-native-intune setup --dry-run # same as doctor
14
+ * npx react-native-intune setup --force # allow a dirty git tree
15
+ *
16
+ * Exit code: non-zero while manual steps remain, so a partial setup cannot be mistaken
17
+ * for a complete one. That is deliberate — the steps it cannot do are the ones that
18
+ * leave the app unprotected.
19
+ */
20
+
21
+ import fs from 'node:fs';
22
+ import readline from 'node:readline';
23
+ import { findProject, rel } from './lib/project.mjs';
24
+ import { gitIsClean, runChecks, summarise } from './doctor.mjs';
25
+
26
+ const args = process.argv.slice(2);
27
+ const flags = {
28
+ yes: args.includes('--yes') || args.includes('-y'),
29
+ dryRun: args.includes('--dry-run'),
30
+ force: args.includes('--force'),
31
+ };
32
+
33
+ async function main() {
34
+ if (flags.dryRun) {
35
+ // Documented as equivalent to doctor, so it runs the same checks rather than a
36
+ // second implementation that can drift out of agreement with it.
37
+ const project = findProject();
38
+ const s = summarise(runChecks(project));
39
+ report(project, s);
40
+ process.exit(s.blocking.length > 0 ? 1 : 0);
41
+ }
42
+
43
+ const project = findProject();
44
+
45
+ if (project.isThisPackage) {
46
+ fail(
47
+ `This is react-native-intune's own repository, not a project that consumes it.\n\n` +
48
+ `Applying the MAM Gradle plugin here is the mistake these commands exist to\n` +
49
+ `catch: it rewrites only the library, builds cleanly, and protects nothing.\n` +
50
+ `Run this from your app's project root instead.`
51
+ );
52
+ }
53
+
54
+ if (!project.android && !project.ios) {
55
+ fail(
56
+ `No native project found under ${project.root}.\n` +
57
+ `Run this from a React Native project root — the folder holding android/ or ios/.`
58
+ );
59
+ }
60
+
61
+ const clean = gitIsClean(project.root);
62
+ if (clean === false && !flags.force) {
63
+ fail(
64
+ `Your git tree has uncommitted changes.\n\n` +
65
+ `This command edits files in your repository. Commit or stash first, so a\n` +
66
+ `revert is one command away — or pass --force if you know what you are doing.`
67
+ );
68
+ }
69
+ if (clean === null) {
70
+ process.stdout.write(
71
+ `Note: not a git repository, so there is no clean revert. Continuing.\n\n`
72
+ );
73
+ }
74
+
75
+ const results = runChecks(project);
76
+ const s = summarise(results);
77
+
78
+ const edits = [];
79
+ for (const r of s.failing) {
80
+ if (typeof r.check.apply !== 'function') {
81
+ continue;
82
+ }
83
+ const edit = r.check.apply(project);
84
+ if (edit) {
85
+ edits.push({ ...edit, check: r.check, before: fs.readFileSync(edit.file, 'utf8') });
86
+ }
87
+ }
88
+
89
+ if (edits.length === 0) {
90
+ process.stdout.write(
91
+ s.failing.length === 0
92
+ ? `Nothing to change — everything this tool can apply is already in place.\n\n`
93
+ : `Nothing here can be applied automatically.\n\n`
94
+ );
95
+ } else {
96
+ process.stdout.write(`\nThese files will change:\n\n`);
97
+ for (const e of edits) {
98
+ process.stdout.write(` ${rel(project, e.file)} — ${e.check.title}\n`);
99
+ process.stdout.write(diff(e.before, e.contents));
100
+ }
101
+
102
+ if (!flags.yes && !(await confirm())) {
103
+ process.stdout.write(`\nNothing written.\n\n`);
104
+ process.exit(1);
105
+ }
106
+
107
+ for (const e of edits) {
108
+ fs.writeFileSync(e.file, e.contents);
109
+ process.stdout.write(` wrote ${rel(project, e.file)}\n`);
110
+ }
111
+ process.stdout.write('\n');
112
+ }
113
+
114
+ // Re-inspect rather than assume the edits worked. A check that still fails after its
115
+ // own apply is a bug worth surfacing here instead of at the next build.
116
+ const after = summarise(runChecks(findProject()));
117
+ report(project, after, { afterApply: true });
118
+ process.exit(after.blocking.length > 0 ? 1 : 0);
119
+ }
120
+
121
+ function report(project, s, { afterApply = false } = {}) {
122
+ if (s.failing.length === 0) {
123
+ process.stdout.write(
124
+ afterApply
125
+ ? `Done. Everything this tool can check is in place.\n\n`
126
+ : `Everything this tool can check is in place.\n\n`
127
+ );
128
+ return;
129
+ }
130
+
131
+ const manual = s.failing.filter((r) => typeof r.check.apply !== 'function');
132
+ const stillBroken = s.failing.filter((r) => typeof r.check.apply === 'function');
133
+
134
+ if (stillBroken.length > 0) {
135
+ process.stdout.write(
136
+ `Still failing after being applied — please report this:\n` +
137
+ stillBroken.map((r) => ` - ${r.check.id}`).join('\n') +
138
+ '\n\n'
139
+ );
140
+ }
141
+
142
+ if (manual.length > 0) {
143
+ process.stdout.write(
144
+ `${manual.length} step${manual.length === 1 ? '' : 's'} left for you. ` +
145
+ `They change your own source or need a value only you have:\n\n`
146
+ );
147
+ for (const r of manual) {
148
+ const danger =
149
+ r.check.severity === 'silent'
150
+ ? ' This one builds and runs while leaving the app unprotected.\n'
151
+ : '';
152
+ process.stdout.write(
153
+ `${r.check.title}\n${danger}\n` +
154
+ r.check
155
+ .instruction(project)
156
+ .split('\n')
157
+ .map((l) => (l.length > 0 ? ` ${l}` : l))
158
+ .join('\n') +
159
+ '\n\n'
160
+ );
161
+ }
162
+ process.stdout.write(
163
+ `Exiting non-zero while these remain, so a partial setup is not mistaken for a\n` +
164
+ `complete one. Run \`npx react-native-intune doctor\` to re-check.\n\n`
165
+ );
166
+ }
167
+ }
168
+
169
+ /**
170
+ * A unified-ish diff, good enough to read before agreeing to it.
171
+ *
172
+ * Deliberately not a dependency: this package ships to consumers, and a diff renderer
173
+ * is not worth a supply-chain entry.
174
+ */
175
+ function diff(before, after) {
176
+ const a = before.split('\n');
177
+ const b = after.split('\n');
178
+ const out = [];
179
+ let i = 0;
180
+ let j = 0;
181
+ while (i < a.length || j < b.length) {
182
+ if (a[i] === b[j]) {
183
+ i += 1;
184
+ j += 1;
185
+ continue;
186
+ }
187
+ // Find where the streams line up again, looking a bounded distance ahead. Enough
188
+ // for inserted blocks, which is all these edits are.
189
+ let resync = -1;
190
+ for (let k = j; k < Math.min(b.length, j + 200); k += 1) {
191
+ if (a[i] !== undefined && b[k] === a[i]) {
192
+ resync = k;
193
+ break;
194
+ }
195
+ }
196
+ if (resync === -1) {
197
+ if (a[i] !== undefined) {
198
+ out.push(` - ${a[i]}`);
199
+ i += 1;
200
+ }
201
+ if (b[j] !== undefined) {
202
+ out.push(` + ${b[j]}`);
203
+ j += 1;
204
+ }
205
+ } else {
206
+ for (let k = j; k < resync; k += 1) {
207
+ out.push(` + ${b[k]}`);
208
+ }
209
+ j = resync;
210
+ }
211
+ }
212
+ return out.length > 0 ? `${out.join('\n')}\n\n` : ' (no textual change)\n\n';
213
+ }
214
+
215
+ function confirm() {
216
+ const rl = readline.createInterface({
217
+ input: process.stdin,
218
+ output: process.stdout,
219
+ });
220
+ return new Promise((resolve) => {
221
+ rl.question('Apply these changes? [y/N] ', (answer) => {
222
+ rl.close();
223
+ resolve(/^y(es)?$/i.test(answer.trim()));
224
+ });
225
+ });
226
+ }
227
+
228
+ function fail(message) {
229
+ process.stderr.write(`\n${message}\n\n`);
230
+ process.exit(1);
231
+ }
232
+
233
+ main().catch((e) => fail(e.stack ?? String(e)));
package/sdk-lock.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "ios": {
3
+ "repo": "microsoftconnect/ms-intune-app-sdk-ios",
4
+ "tag": "21.8.0",
5
+ "fetchedAt": "2026-09-03T10:39:33.572Z",
6
+ "files": {
7
+ "ios/IntuneMAMSwift.xcframework": "f38f0f130bd2adf0dd936acb26958f6c6ebc0b2d5a4bf886d4c8c7d4ae0870ab",
8
+ "ios/IntuneMAMSwiftStub.xcframework": "a03e563feec36f719bfca34b061e590e1c54a03650a3855a8298988e49b9ea9c",
9
+ "ios/IntuneMAMTelemetry.xcframework": "d4582c8ffa014611cb1c10b5717cf13cdbeeb50e34b67624dc884e194636b3de",
10
+ "ios/IntuneMAMResources.bundle": "51d6f666efb80eddb6d35467c9d6491b746405a72c8ebe6cde5de5c68e7181f3",
11
+ "ios/IntuneMAMConfigurator.dmg": "4fc4fbcd6211e62fa355ef1976970e230d806855703f75788727d5dbcb454b61",
12
+ "ios/REDIST.txt": "31c5b0e0c3ee48b60c6f12e56d5a8b6d47754f127750f27627800d2b44b79e56",
13
+ "ios/Microsoft License Terms Intune App SDK for iOS.pdf": "5b12b9a6ee3671a138b67884ff69c5bda17b6cb7bec0646f108cf072930c7394",
14
+ "ios/ThirdPartyNotices.txt": "aa0bbc665cfd7b7549b05c3bd747faa5d383c1ea4a297fb4dc33184180e3663f"
15
+ }
16
+ },
17
+ "android": {
18
+ "repo": "microsoftconnect/ms-intune-app-sdk-android",
19
+ "tag": "12.4.0",
20
+ "fetchedAt": "2026-09-03T10:39:50.835Z",
21
+ "files": {
22
+ "android/Microsoft.Intune.MAM.SDK.aar": "b7e5d0bb86d18f054d8901f168f82e64c79a57334fbb6227136b108b0514e998",
23
+ "android/GradlePlugin/com.microsoft.intune.mam.build.jar": "04ae1e0fbb6b5dfcbf8bd42e8d4f41ad758332ee6c238780a764a9ab1b67446d",
24
+ "android/Microsoft License Terms Intune App SDK for Android.pdf": "5f1000c547c38f94cd6791dcd6e1c032ab2c55e2c28f9c5cf75e6e039f0267a2"
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,70 @@
1
+ {
2
+ "_comment": [
3
+ "Pinned Microsoft SDK versions. Single source of truth for which SDK builds this",
4
+ "library is tested against. Bump deliberately, never automatically: Microsoft requires",
5
+ "apps to stay on recent SDK versions and has previously blocked launches for apps that",
6
+ "fell behind, so a bump is a release-worthy change.",
7
+ "",
8
+ "iOS 21.x requires iOS 17+ and Xcode 26. If a consumer must support iOS 16, they need",
9
+ "SDK 20.x, which Microsoft supports for high-priority security fixes only. See SPEC.md."
10
+ ],
11
+
12
+ "ios": {
13
+ "repo": "microsoftconnect/ms-intune-app-sdk-ios",
14
+ "tag": "21.8.0",
15
+ "requires": { "ios": "17.0", "xcode": "26" },
16
+ "expect": [
17
+ { "glob": "IntuneMAMSwift.xcframework", "as": "ios/IntuneMAMSwift.xcframework", "required": true },
18
+ { "glob": "IntuneMAMSwiftStub.xcframework", "as": "ios/IntuneMAMSwiftStub.xcframework", "required": true },
19
+ { "glob": "IntuneMAMTelemetry.xcframework", "as": "ios/IntuneMAMTelemetry.xcframework", "required": true },
20
+ { "glob": "IntuneMAMResources.bundle", "as": "ios/IntuneMAMResources.bundle", "required": true },
21
+ { "glob": "IntuneMAMConfigurator.dmg", "as": "ios/IntuneMAMConfigurator.dmg", "required": true, "extractDmg": "IntuneMAMConfigurator", "extractAs": "ios/IntuneMAMConfigurator" },
22
+ { "glob": "REDIST.txt", "as": "ios/REDIST.txt", "required": false },
23
+ { "glob": "Microsoft License Terms*.pdf", "as": "ios/", "required": false },
24
+ { "glob": "ThirdPartyNotices.txt", "as": "ios/ThirdPartyNotices.txt", "required": false }
25
+ ],
26
+ "_notes": [
27
+ "IntuneMAMStatic.xcframework is the static variant — not used; the dynamic one keeps",
28
+ "bundle size down when extensions are involved.",
29
+ "libIntuneMAMSwiftFileProvider.xcframework is only for File Provider extensions.",
30
+ "IntuneMAMResources.bundle carries every SDK screen (PIN, blur, diagnostics, MTD",
31
+ "compliance) and 28 localizations. Without it the SDK's UI cannot render."
32
+ ]
33
+ },
34
+
35
+ "android": {
36
+ "repo": "microsoftconnect/ms-intune-app-sdk-android",
37
+ "tag": "12.4.0",
38
+ "expect": [
39
+ { "glob": "**/Microsoft.Intune.MAM.SDK.aar", "as": "android/Microsoft.Intune.MAM.SDK.aar", "required": true },
40
+ { "glob": "**/com.microsoft.intune.mam.build.jar", "as": "android/GradlePlugin/com.microsoft.intune.mam.build.jar", "required": true },
41
+ { "glob": "**/Microsoft.Intune.MAM.SDK.Support*.jar","as": "android/support/", "required": false },
42
+ { "glob": "**/*License*.pdf", "as": "android/", "required": false }
43
+ ]
44
+ },
45
+
46
+ "toolchain": {
47
+ "_comment": [
48
+ "A version bump forces a conscious check against the MAM compatibility matrix and the",
49
+ "host app's toolchain (SPEC.md O-E).",
50
+ "",
51
+ "msal_android has no requirement from Microsoft to satisfy. The Android SDK guide says",
52
+ "only \"integrate MSAL\" and links the MSAL repository, unlike iOS where the coupling is",
53
+ "hard. So this is our choice, pinned for reproducibility rather than compatibility:",
54
+ "8.4.2, the current stable, released 2026-08-21.",
55
+ "",
56
+ "msal_ios is exact, not a range: the Intune SDK does not carry its own MSAL, it drives",
57
+ "the app's. Two consumers on different MSALs is the same hazard pinning the Intune SDK",
58
+ "exists to prevent. Resolved by CocoaPods on 2026-09-04, not guessed.",
59
+ "",
60
+ "`requires` is the compatibility-matrix row for the pinned Android tag above (12.4.0 ->",
61
+ "the 12.0.0 row). Microsoft tests these combinations; mixing rows is untested. These are",
62
+ "the library's own defaults in android/build.gradle. The consumer's app module, where the",
63
+ "MAM Gradle plugin actually runs, must land on the same row -- that is O-E, still open."
64
+ ],
65
+ "javassist": "3.29.2-GA",
66
+ "msal_ios": "2.15.0",
67
+ "msal_android": "8.4.2",
68
+ "requires": { "android": "16", "gradle": "8.11.1", "agp": "8.9.1", "kotlin": "2.1.21", "java": "17" }
69
+ }
70
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Codegen spec — the ONLY file Codegen reads, and the only `Native*.ts` allowed in
3
+ * `jsSrcsDir`. An extra one is picked up and fails confusingly.
4
+ *
5
+ * This file is deliberately loose (CLAUDE.md rule 11). Codegen accepts only
6
+ * `boolean | number | string | Object | Array | Promise | void`: no literal unions, no
7
+ * optional parameters, no enums. So every union and every optional is normalized in
8
+ * `index.ts` before it crosses the bridge, and every aggregate return is `Object` here
9
+ * and a real type there. Nothing in the public API is `Object`.
10
+ *
11
+ * Editing this file means Codegen must re-run — `pod install` on iOS,
12
+ * `generateCodegenArtifactsFromSchema` on Android. A native build that breaks right
13
+ * after a change here is a stale Codegen artifact until proven otherwise.
14
+ */
15
+
16
+ import { TurboModuleRegistry, type TurboModule } from 'react-native';
17
+
18
+ export interface Spec extends TurboModule {
19
+ // ---- lifecycle ----
20
+
21
+ /**
22
+ * Every field is required here even though most are optional in the public API:
23
+ * `index.ts` applies the defaults, so the native side never has to guess one.
24
+ * Empty string means "unset" for `keychainGroupOverride`.
25
+ */
26
+ configure(config: {
27
+ clientId: string;
28
+ tenantId: string;
29
+ authority: string;
30
+ redirectUri: string;
31
+ authMode: string; // 'builtin' | 'external'
32
+ verboseLogging: boolean;
33
+ restartHandledByApp: boolean;
34
+ maxFileProtectionLevel: string; // FileProtectionLevel
35
+ strictMode: boolean;
36
+ keychainGroupOverride: string; // '' = platform default
37
+ telemetryEnabled: boolean;
38
+ // SDK screen branding (SPEC §15 P4). '' leaves the SDK's own default in place.
39
+ brandingBackground: string;
40
+ brandingForeground: string;
41
+ brandingAccent: string;
42
+ brandingSecondaryBackground: string;
43
+ brandingSecondaryForeground: string;
44
+ }): Promise<void>;
45
+
46
+ /** SDK linked and platform prerequisites met. Gate the whole Intune path on this. */
47
+ isSupported(): Promise<boolean>;
48
+
49
+ /** BrokerStatus. */
50
+ getBrokerStatus(): Promise<Object>;
51
+
52
+ openBrokerInstall(): Promise<void>;
53
+
54
+ // ---- auth (builtin mode) ----
55
+
56
+ /** SignInParams in, AuthResult out. */
57
+ signIn(params: Object): Promise<Object>;
58
+ /** SignInParams in, AuthResult out. Rejects E_INTERACTION_REQUIRED. */
59
+ signInSilent(params: Object): Promise<Object>;
60
+ /** AcquireTokenParams in, AuthResult out. For the app's own scopes only. */
61
+ acquireToken(params: Object): Promise<Object>;
62
+ /** AuthAccount[]. */
63
+ getAccounts(): Promise<Object[]>;
64
+
65
+ signOut(params: { accountId: string; wipeIntune: boolean }): Promise<void>;
66
+
67
+ // ---- enrollment ----
68
+
69
+ /**
70
+ * EnrollmentResult. Resolves for every outcome including failures — a non-success
71
+ * status is data, not an exception. Resolved from the SDK's delegate or notification,
72
+ * never from the underlying call returning (SPEC §12.5).
73
+ */
74
+ enroll(params: { accountId: string; upn: string }): Promise<Object>;
75
+
76
+ /**
77
+ * EnrollmentResult. **iOS only** — `loginAndEnrollAccount:` makes the SDK run the
78
+ * sign-in itself and show its own UI, so no MSAL is needed on our side. Android has no
79
+ * counterpart: `registerAccountForMAM` always needs a token from the app.
80
+ *
81
+ * Pass '' for `upn` to let the SDK prompt for the address as well.
82
+ */
83
+ enrollInteractive(params: { upn: string }): Promise<Object>;
84
+
85
+ /** IntuneState — the reconciliation primitive. */
86
+ getState(): Promise<Object>;
87
+
88
+ /**
89
+ * Writes the journal and unregisters. The process is expected to terminate during this
90
+ * call on Android (SPEC §7), so nothing after it is guaranteed to run.
91
+ */
92
+ reset(params: { wipe: boolean; reason: string }): Promise<void>;
93
+
94
+ /**
95
+ * NOT public API. Verifies the account really is gone, then closes the journal —
96
+ * `reset()` in index.ts calls it after the consumer's reset handler has cleared local
97
+ * data. Separate from `reset` because the steps between them may straddle a process
98
+ * death, and the journal is what carries the sequence across it (SPEC §7).
99
+ */
100
+ completeReset(): Promise<void>;
101
+
102
+ // ---- policy ----
103
+
104
+ /** PolicySnapshot. */
105
+ getPolicy(): Promise<Object>;
106
+ /** Opaque key/value for support bundles. No tokens, no UPNs. */
107
+ getDiagnostics(): Promise<Object>;
108
+
109
+ // ---- token provider plumbing ----
110
+
111
+ /**
112
+ * NOT public API. `setTokenProvider` in `index.ts` wraps these; the SDK asks for a MAM
113
+ * service token on a background thread, including on its own retry schedule with no JS
114
+ * call in flight, so it cannot be a callback parameter on `enroll` (SPEC §13.4).
115
+ */
116
+ resolveToken(params: { requestId: string; token: string }): void;
117
+ rejectToken(params: { requestId: string; reason: string }): void;
118
+
119
+ // ---- events ----
120
+
121
+ /**
122
+ * Present only to satisfy `NativeEventEmitter`; ref-counting, no logic. The emitter
123
+ * itself is live from module construction and queues events fired before JS
124
+ * subscribes — a wipe lost during startup means the reset never runs (SPEC §4.4).
125
+ */
126
+ addListener(eventName: string): void;
127
+ removeListeners(count: number): void;
128
+ }
129
+
130
+ export default TurboModuleRegistry.getEnforcing<Spec>('RNIntune');