@incode-sdks/incode-integrate 0.2.0 → 0.4.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 (3) hide show
  1. package/README.md +82 -5
  2. package/agent.js +61 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -16,11 +16,19 @@ Apply the plan explicitly:
16
16
  npx @incode-sdks/incode-integrate --apply
17
17
  ```
18
18
 
19
- Scripted example:
19
+ ## Interactive Setup
20
20
 
21
- ```sh
22
- npx @incode-sdks/incode-integrate --version 9.14.0 --api-url demo --backend-sessions yes --sdk-config manual --idv standard-nfc --flow-handling step-by-step --apply
23
- ```
21
+ In an interactive terminal, the CLI asks for:
22
+
23
+ - Incode customer GitHub token
24
+ - React Native NPM token
25
+ - API URL: Demo, Production, or custom
26
+ - API key, if the client is embedding the key
27
+ - whether sessions are initiated on the backend, which enables the recommended no-api-key client approach
28
+ - SDK configuration mode: online dashboard flow or manual in-code flow
29
+ - manual IDV preset: Standard, Standard + NFC, or Standard + Session Recording
30
+ - flow handling: end-to-end or step-by-step
31
+ - iOS podspec source
24
32
 
25
33
  ## What It Updates
26
34
 
@@ -30,4 +38,73 @@ npx @incode-sdks/incode-integrate --version 9.14.0 --api-url demo --backend-sess
30
38
  - generated Incode flow files under `src/`
31
39
  - ignored local files such as `.env.local` and `.npmrc` when credentials are provided
32
40
 
33
- Secrets are not written to generated TypeScript, Gradle, Podfile, or README files.
41
+ Secrets are not written to Gradle, Podfile, README, or committed generated TypeScript files.
42
+
43
+ When `--apply` receives tokens, it writes them only to ignored local files:
44
+
45
+ - `.env.local`: shell exports for `GITHUB_USERNAME`, `GITHUB_TOKEN`, `INCODE_API_URL`, optional `INCODE_API_KEY`, and optional `INCODE_SESSION_TOKEN`
46
+ - `.npmrc`: npm registry auth for the React Native SDK package
47
+ - `src/incodeRuntimeConfig.local.ts`: demo-app credential prefill generated from `.env.local`
48
+
49
+ React Native does not automatically load `.env.local` at runtime. The generated local runtime config lets the demo app prefill the API key or backend session token from ignored local values.
50
+
51
+ ## Backend Sessions
52
+
53
+ Use backend sessions for the recommended no-api-key client approach:
54
+
55
+ ```sh
56
+ npx @incode-sdks/incode-integrate --backend-sessions yes --apply
57
+ ```
58
+
59
+ In backend-session mode, the generated `src/IncodeExample.ts` appends `/0/` to the SDK `apiConfig.url`, for example `https://demo-api.incodesmile.com/0/`.
60
+
61
+ The client app should pass the backend-created `session_token` as `sessionToken`; the helper maps it to the React Native SDK's `sessionConfig.token`.
62
+
63
+ ## Online Dashboard Flows
64
+
65
+ When `--sdk-config online` is used, the generated helper puts the dashboard flow ID into `SESSION_CONFIG.configurationId` and starts onboarding with `IncodeSdk.startFlow({ sessionConfig })`.
66
+
67
+ Manual mode uses generated `flowConfig` modules with `IncodeSdk.startOnboarding(...)` or `startOnboardingSection(...)`.
68
+
69
+ ## Scripted Examples
70
+
71
+ ```sh
72
+ npx @incode-sdks/incode-integrate --version 9.14.0 --pod-source https --api-url demo --backend-sessions yes --sdk-config manual --idv standard-nfc --flow-handling step-by-step
73
+ npx @incode-sdks/incode-integrate --version 9.14.0 --api-url production --backend-sessions no --sdk-config manual --idv standard-recording --flow-handling end-to-end --apply
74
+ npx @incode-sdks/incode-integrate --sdk-config online --api-url demo --dashboard-url "https://dashboard.incodesmile.com/flows/example" --configuration-id "example-config-id"
75
+ npx @incode-sdks/incode-integrate --yes
76
+ ```
77
+
78
+ ## Important Flags
79
+
80
+ - `--apply`: update files; without this flag the CLI only prints a dry-run plan
81
+ - `--yes`: use non-interactive defaults
82
+ - `--version`: SDK version, for example `9.14.0`
83
+ - `--pod-source`: `ssh` or `https`
84
+ - `--api-url`: `demo`, `production`, or a custom URL
85
+ - `--backend-sessions`: `yes` for the no-api-key client approach, `no` to use a client API key at runtime
86
+ - `--api-key`: API key for client API-key mode
87
+ - `--github-username`: GitHub username for Incode package access
88
+ - `--github-token`: Incode customer GitHub token for package access
89
+ - `--npm-token`: NPM token for React Native SDK package access
90
+ - `--sdk-config`: `online` or `manual`
91
+ - `--dashboard-url`: dashboard flow URL for online SDK configuration
92
+ - `--configuration-id`: online configuration ID when known
93
+ - `--idv`: `standard`, `standard-nfc`, or `standard-recording`
94
+ - `--flow-handling`: `end-to-end` or `step-by-step`
95
+ - `--test-mode`: default the sample app to SDK test mode
96
+ - `--no-test-mode`: default the sample app to real-device mode
97
+
98
+ ## IDV Presets
99
+
100
+ - `standard`: `IdScan`, `SelfieScan`, `FaceMatch`
101
+ - `standard-nfc`: `IdScan`, `NFCScan`, `SelfieScan`, `FaceMatch`
102
+ - `standard-recording`: `IdScan`, `SelfieScan`, `FaceMatch`, record-session config, and the Android video streaming dependency
103
+
104
+ SDK package variant is derived from `--idv`:
105
+
106
+ - `standard` installs the standard SDK package
107
+ - `standard-nfc` installs the `nfc` SDK package variant
108
+ - `standard-recording` installs the `streaming` / `vc` SDK package variant
109
+
110
+ The CLI always writes the React Native SDK dependency as an npm registry version in `package.json`; it does not use local tarballs.
package/agent.js CHANGED
@@ -164,6 +164,10 @@ function normalizeVersion(version) {
164
164
  return trimmed || DEFAULT_SDK_VERSION;
165
165
  }
166
166
 
167
+ function escapeRegExp(value) {
168
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
169
+ }
170
+
167
171
  function normalizeVariant(variant) {
168
172
  const normalized = String(variant || "").trim().toLowerCase();
169
173
  const aliases = {
@@ -457,6 +461,20 @@ function parseStringConstant(content, name) {
457
461
  return double?.[1] || "";
458
462
  }
459
463
 
464
+ function parseObjectStringProperty(content, name) {
465
+ const quoted = content.match(
466
+ new RegExp(`["']${escapeRegExp(name)}["']\\s*:\\s*["']([^"']*)["']`),
467
+ );
468
+ if (quoted) {
469
+ return quoted[1];
470
+ }
471
+
472
+ const unquoted = content.match(
473
+ new RegExp(`\\b${escapeRegExp(name)}\\b\\s*:\\s*["']([^"']*)["']`),
474
+ );
475
+ return unquoted?.[1] || "";
476
+ }
477
+
460
478
  function parseBooleanConstant(content, name, fallback) {
461
479
  const match = content.match(new RegExp(`export const ${name}(?::[^=]+)? = (true|false)`));
462
480
  return match ? match[1] === "true" : fallback;
@@ -480,10 +498,14 @@ function extractConfigurationId(dashboardUrl) {
480
498
  }
481
499
 
482
500
  const fromPath = url.pathname.match(/(?:configuration|configurations|flow|flows)\/([^/?#]+)/i);
483
- return fromPath?.[1] || "";
501
+ if (fromPath) {
502
+ return fromPath[1];
503
+ }
484
504
  } catch {
485
- return "";
505
+ // Fall through and allow a raw flow/configuration ID.
486
506
  }
507
+
508
+ return /^[A-Za-z0-9_-]+$/.test(value) ? value : "";
487
509
  }
488
510
 
489
511
  function inferVariantFromDashboardUrl(dashboardUrl) {
@@ -613,6 +635,9 @@ function inferProductOptions(context) {
613
635
  const sdkConfigMode = normalizeConfigMode(parseStringConstant(incodeFlow, "SDK_CONFIGURATION_MODE"), "manual");
614
636
  const flowHandling = normalizeFlowHandling(parseStringConstant(incodeFlow, "FLOW_HANDLING"), "end-to-end");
615
637
  const dashboardUrl = parseStringConstant(incodeFlow, "DASHBOARD_FLOW_URL");
638
+ const configurationId =
639
+ parseObjectStringProperty(incodeFlow, "configurationId") ||
640
+ extractConfigurationId(dashboardUrl);
616
641
  const apiUrl = normalizeApiUrl(
617
642
  localSecrets.INCODE_API_URL || parseStringConstant(incodeRuntimeConfig, "INCODE_DEFAULT_API_URL"),
618
643
  API_URLS.demo.url,
@@ -634,7 +659,7 @@ function inferProductOptions(context) {
634
659
  apiKeyProvided: Boolean(localSecrets.INCODE_API_KEY),
635
660
  apiUrl,
636
661
  backendSessions: localBackendSessions,
637
- configurationId: extractConfigurationId(dashboardUrl),
662
+ configurationId,
638
663
  dashboardUrl,
639
664
  dynamicLocalization: androidAppBuildGradle.includes("com.incode.sdk:extensions"),
640
665
  flowHandling,
@@ -651,6 +676,7 @@ function inferProductOptions(context) {
651
676
  sdkConfigMode,
652
677
  sdkVariant: currentDependency.variant,
653
678
  sdkVersion: currentDependency.version,
679
+ sessionToken: localSecrets.INCODE_SESSION_TOKEN || "",
654
680
  testMode,
655
681
  videoStreaming: androidAppBuildGradle.includes("com.incode.sdk:video-streaming"),
656
682
  };
@@ -766,6 +792,7 @@ async function promptForProductOptions(context, options) {
766
792
  sdkConfigMode,
767
793
  sdkVariant: variantForIdv(idv),
768
794
  sdkVersion,
795
+ sessionToken: current.sessionToken,
769
796
  videoStreaming: Boolean(IDV_PRESETS[idv].videoStreaming),
770
797
  };
771
798
  } finally {
@@ -836,6 +863,7 @@ function chooseProductOptions(context, options) {
836
863
  sdkConfigMode,
837
864
  sdkVariant: variantForIdv(idv),
838
865
  sdkVersion: normalizeVersion(options.version || DEFAULT_SDK_VERSION),
866
+ sessionToken: current.sessionToken,
839
867
  testMode: options.testMode === undefined ? current.testMode : options.testMode,
840
868
  videoStreaming:
841
869
  options.videoStreaming === undefined
@@ -882,8 +910,6 @@ export type FlowHandling = 'end-to-end' | 'step-by-step';
882
910
 
883
911
  export const SDK_CONFIGURATION_MODE: SdkConfigurationMode = '${productOptions.sdkConfigMode}';
884
912
 
885
- export const DASHBOARD_FLOW_URL = '${productOptions.dashboardUrl || ""}';
886
-
887
913
  export const IDV_PRESET = '${productOptions.idv}' as const;
888
914
 
889
915
  export const FLOW_HANDLING: FlowHandling = '${productOptions.flowHandling}';
@@ -925,6 +951,17 @@ export const INCODE_DEFAULT_TEST_MODE = ${productOptions.testMode};
925
951
  `;
926
952
  }
927
953
 
954
+ function createLocalRuntimeConfigFile(productOptions) {
955
+ const credential = productOptions.backendSessions
956
+ ? productOptions.sessionToken
957
+ : productOptions.apiKey;
958
+
959
+ return `// Generated from ignored local env values for the demo app.
960
+ // Do not commit real credentials in this file.
961
+ export const INCODE_DEFAULT_CREDENTIAL = ${toTs(credential || "")};
962
+ `;
963
+ }
964
+
928
965
  function createLocalCredentialEnvFile(productOptions) {
929
966
  const lines = [
930
967
  "# Local Incode SDK credentials and runtime values. This file is ignored by git.",
@@ -950,7 +987,11 @@ function createLocalCredentialEnvFile(productOptions) {
950
987
  }
951
988
 
952
989
  if (productOptions.backendSessions) {
953
- lines.push('export INCODE_SESSION_TOKEN="${INCODE_SESSION_TOKEN:-<token-from-your-backend>}"');
990
+ if (productOptions.sessionToken) {
991
+ lines.push(`export INCODE_SESSION_TOKEN="${escapeEnvValue(productOptions.sessionToken)}"`);
992
+ } else {
993
+ lines.push('export INCODE_SESSION_TOKEN="${INCODE_SESSION_TOKEN:-<token-from-your-backend>}"');
994
+ }
954
995
  }
955
996
 
956
997
  return `${lines.join("\n")}\n`;
@@ -969,6 +1010,7 @@ import {
969
1010
  FLOW_CONFIG,
970
1011
  FLOW_HANDLING,
971
1012
  FLOW_RECORD_SESSION_CONFIG,
1013
+ SDK_CONFIGURATION_MODE,
972
1014
  SECTION_FLOW_CONFIGS,
973
1015
  SESSION_CONFIG,
974
1016
  } from './incodeFlow';
@@ -1020,6 +1062,10 @@ export async function startConfiguredOnboarding(options: StartIncodeOptions) {
1020
1062
 
1021
1063
  const sessionConfig = buildSessionConfig(options);
1022
1064
 
1065
+ if (SDK_CONFIGURATION_MODE === 'online') {
1066
+ return IncodeSdk.startFlow({ sessionConfig });
1067
+ }
1068
+
1023
1069
  if (FLOW_HANDLING === 'step-by-step') {
1024
1070
  const session = await IncodeSdk.setupOnboardingSession({ sessionConfig });
1025
1071
  let lastResult: Awaited<ReturnType<typeof IncodeSdk.startOnboardingSection>> | undefined;
@@ -1091,7 +1137,7 @@ function generatePlan(context, sdkSelection, productOptions) {
1091
1137
  const changes = [
1092
1138
  {
1093
1139
  action: "gitignore_entries",
1094
- entries: [".env.local", ".incode.local.env", ".npmrc"],
1140
+ entries: [".env.local", ".incode.local.env", ".npmrc", "src/incodeRuntimeConfig.local.ts"],
1095
1141
  file: ".gitignore",
1096
1142
  title: "Keep local SDK credentials out of source control",
1097
1143
  },
@@ -1145,6 +1191,12 @@ function generatePlan(context, sdkSelection, productOptions) {
1145
1191
  file: "src/incodeRuntimeConfig.ts",
1146
1192
  title: "Generate non-secret runtime defaults",
1147
1193
  },
1194
+ {
1195
+ action: "write_file",
1196
+ content: createLocalRuntimeConfigFile(productOptions),
1197
+ file: "src/incodeRuntimeConfig.local.ts",
1198
+ title: "Generate ignored local runtime credential defaults",
1199
+ },
1148
1200
  {
1149
1201
  action: "write_file",
1150
1202
  content: createExampleFile(),
@@ -1251,7 +1303,6 @@ function printPlan(context, plan, options) {
1251
1303
  console.log(`Dashboard flow URL: ${product.dashboardUrl || "not provided"}`);
1252
1304
  console.log(`Dashboard environment: ${labelForApiUrl(product.apiUrl)}`);
1253
1305
  console.log(`Configuration ID: ${product.configurationId || "not inferred"}`);
1254
- console.log("Dashboard flow config: will be fetched by API when the flow-config endpoint is connected.");
1255
1306
  }
1256
1307
  console.log(`IDV preset: ${product.idv}`);
1257
1308
  console.log(`Flow handling: ${product.flowHandling}`);
@@ -1261,10 +1312,10 @@ function printPlan(context, plan, options) {
1261
1312
  console.log(`Feature deps: ${featureDeps.join(", ") || "none"}`);
1262
1313
  console.log("");
1263
1314
  console.log(
1264
- "Secrets are never written to generated TypeScript, Gradle, Podfile, or README files.",
1315
+ "Secrets are never written to Gradle, Podfile, README, or committed generated TypeScript files.",
1265
1316
  );
1266
1317
  console.log(
1267
- "When --apply receives tokens, they are written only to ignored local files: .env.local and .npmrc.",
1318
+ "When --apply receives tokens, they are written only to ignored local files: .env.local, .npmrc, and src/incodeRuntimeConfig.local.ts.",
1268
1319
  );
1269
1320
  console.log("");
1270
1321
  console.log(options.apply ? "Applying changes:" : "Planned changes:");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@incode-sdks/incode-integrate",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "CLI integration auditor and fixer for adding the Incode React Native SDK to client apps.",
5
5
  "main": "agent.js",
6
6
  "bin": {