diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 7d0bd37b5..c321eb0c9 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -27,5 +27,17 @@ jobs: - name: Install dependencies run: yarn install --frozen-lockfile + # Whole-corpus check: the docs gate is a ratchet on changed files, so it + # cannot see a pre-existing violation nobody touched. This is what makes a + # framework value outside the enum - in docs OR in the code maps that read + # it - break the build instead of silently mis-resolving. + - name: Verify framework identifiers + run: yarn verify:frameworks + + # Pins the membership of every derived framework map. The refactor risk + # here is not a crash, it is a map quietly gaining or losing an entry. + - name: Test framework registry + run: yarn test:frameworks + - name: Build documentation run: yarn build diff --git a/docs-schema.yml b/docs-schema.yml index 8271e643a..108a2bf6c 100644 --- a/docs-schema.yml +++ b/docs-schema.yml @@ -35,14 +35,30 @@ properties: matrixscan-ar, matrixscan-pick, barcode-selection, barcode-generator, id-capture, id-bolt, label-capture, parser, express, core, platform] + # Canonical framework slugs. These are the SSOT for every framework identifier + # in the repo: they are exactly the `/sdks//` URL segments (with `hosted` + # for the /hosted/ tree), and src/components/utils/frameworks.ts must cover + # this same set - `yarn verify:frameworks` fails the build if the two drift. + # + # `kmp` (Kotlin Multiplatform) is documented only in the in-development docs + # version; that is tracked by UNRELEASED_FRAMEWORK_SLUGS in + # src/constants/unreleasedFrameworks.ts, not by leaving it out of this enum. + # + # Singular `framework` and plural `frameworks` share the set deliberately: a + # page states one platform or several, never two different vocabularies. + framework: + type: string + enum: [ios, android, web, react-native, flutter, cordova, capacitor, kmp, + titanium, linux, net-ios, net-android, hosted] + frameworks: type: array minItems: 1 uniqueItems: true items: type: string - enum: [ios, android, web, react-native, flutter, cordova, capacitor, titanium, - linux, net-ios, net-android, hosted] + enum: [ios, android, web, react-native, flutter, cordova, capacitor, kmp, + titanium, linux, net-ios, net-android, hosted] version_status: type: string diff --git a/docs/hosted/express/configuration/custom-data-transfer/input-configuration.md b/docs/hosted/express/configuration/custom-data-transfer/input-configuration.md index b79644b6e..9ed15192d 100644 --- a/docs/hosted/express/configuration/custom-data-transfer/input-configuration.md +++ b/docs/hosted/express/configuration/custom-data-transfer/input-configuration.md @@ -1,6 +1,7 @@ --- description: "Define the input fields Custom Data Transfer collects during scanning in Scandit Express, and set each field's type and format." -framework: express +framework: hosted +product: express sidebar_label: Input Configuration keywords: - express diff --git a/docs/hosted/express/configuration/custom-data-transfer/output-configuration.md b/docs/hosted/express/configuration/custom-data-transfer/output-configuration.md index 6685af88f..d724835de 100644 --- a/docs/hosted/express/configuration/custom-data-transfer/output-configuration.md +++ b/docs/hosted/express/configuration/custom-data-transfer/output-configuration.md @@ -1,6 +1,7 @@ --- description: "Configure how Custom Data Transfer structures, transforms, and exports scanned data to external systems in Scandit Express." -framework: express +framework: hosted +product: express sidebar_label: Output Configuration keywords: - express diff --git a/docs/hosted/express/configuration/custom-data-transfer/overview.md b/docs/hosted/express/configuration/custom-data-transfer/overview.md index 6ffaf7f15..39c0c0377 100644 --- a/docs/hosted/express/configuration/custom-data-transfer/overview.md +++ b/docs/hosted/express/configuration/custom-data-transfer/overview.md @@ -1,6 +1,7 @@ --- description: "Import and export data in Scandit Express using a configurable CSV file or Google Sheet, aligning formats with your system without coding." -framework: express +framework: hosted +product: express sidebar_label: Overview keywords: - express diff --git a/docs/hosted/express/configuration/device-pairing/bluetooth-pairing.md b/docs/hosted/express/configuration/device-pairing/bluetooth-pairing.md index 0f00bf19d..dde57be82 100644 --- a/docs/hosted/express/configuration/device-pairing/bluetooth-pairing.md +++ b/docs/hosted/express/configuration/device-pairing/bluetooth-pairing.md @@ -1,6 +1,7 @@ --- description: "Set up Bluetooth device pairing in Scandit Express to send scanned data from one device to another." -framework: express +framework: hosted +product: express keywords: - express --- diff --git a/docs/hosted/express/configuration/device-pairing/device-pairing.md b/docs/hosted/express/configuration/device-pairing/device-pairing.md index e2c0e6a0c..77c384ac4 100644 --- a/docs/hosted/express/configuration/device-pairing/device-pairing.md +++ b/docs/hosted/express/configuration/device-pairing/device-pairing.md @@ -1,6 +1,7 @@ --- description: "Use Device Pairing in Scandit Express to transfer scanned barcode data in real time from a sender device to the receiver running your app." -framework: express +framework: hosted +product: express sidebar_label: Overview keywords: - express diff --git a/docs/hosted/express/configuration/device-pairing/online-connection.md b/docs/hosted/express/configuration/device-pairing/online-connection.md index a7e467d7c..0200c9079 100644 --- a/docs/hosted/express/configuration/device-pairing/online-connection.md +++ b/docs/hosted/express/configuration/device-pairing/online-connection.md @@ -1,6 +1,7 @@ --- description: "Set up online device pairing in Scandit Express to send scanned data to a web application." -framework: express +framework: hosted +product: express keywords: - express --- diff --git a/docs/hosted/express/configuration/express-find.md b/docs/hosted/express/configuration/express-find.md index c83a8ac11..5f9bfab77 100644 --- a/docs/hosted/express/configuration/express-find.md +++ b/docs/hosted/express/configuration/express-find.md @@ -1,7 +1,8 @@ --- description: "Find Items in Scandit Express, powered by MatrixScan Find, speeds up picking by scanning many items and highlighting the right ones in AR." -framework: express +framework: hosted +product: express keywords: - express --- diff --git a/docs/hosted/express/configuration/index.md b/docs/hosted/express/configuration/index.md index 31dc2449d..9a778e7f3 100644 --- a/docs/hosted/express/configuration/index.md +++ b/docs/hosted/express/configuration/index.md @@ -2,7 +2,8 @@ description: "Overview of the configuration options available in Scandit Express and how to set them up." toc_max_heading_level: 4 -framework: express +framework: hosted +product: express keywords: - express --- diff --git a/docs/hosted/express/configuration/inventory-count.md b/docs/hosted/express/configuration/inventory-count.md index 6d53502c8..becf26dff 100644 --- a/docs/hosted/express/configuration/inventory-count.md +++ b/docs/hosted/express/configuration/inventory-count.md @@ -1,6 +1,7 @@ --- description: "Use the Inventory Count mode in Scandit Express for fast, accurate stock counting." -framework: express +framework: hosted +product: express keywords: - express --- diff --git a/docs/hosted/express/configuration/scan-labels.md b/docs/hosted/express/configuration/scan-labels.md index 461ef8a28..10f4b20b1 100644 --- a/docs/hosted/express/configuration/scan-labels.md +++ b/docs/hosted/express/configuration/scan-labels.md @@ -1,6 +1,7 @@ --- description: "Scan Labels in Scandit Express, powered by Smart Label Capture, reads multiple barcodes and printed text on a label in one scan." -framework: express +framework: hosted +product: express keywords: - express --- diff --git a/docs/hosted/express/getting-started/installation.md b/docs/hosted/express/getting-started/installation.md index 31bef8f80..5f8657239 100644 --- a/docs/hosted/express/getting-started/installation.md +++ b/docs/hosted/express/getting-started/installation.md @@ -3,7 +3,8 @@ description: "Install Scandit Express on iOS or Android and activate it to add b sidebar_label: 'Installation' displayed_sidebar: expressSidebar sidebar_position: 1 -framework: express +framework: hosted +product: express keywords: - express --- diff --git a/docs/hosted/express/getting-started/rollout.md b/docs/hosted/express/getting-started/rollout.md index 563246a0a..03f64942c 100644 --- a/docs/hosted/express/getting-started/rollout.md +++ b/docs/hosted/express/getting-started/rollout.md @@ -4,7 +4,8 @@ description: "Roll out Scandit Express to production with a mobile device manage sidebar_label: 'Rollout via MDM/EMM' displayed_sidebar: expressSidebar sidebar_position: 2 -framework: express +framework: hosted +product: express keywords: - express --- diff --git a/docs/hosted/express/overview.md b/docs/hosted/express/overview.md index 8f6d5fe04..4e56cfb6d 100644 --- a/docs/hosted/express/overview.md +++ b/docs/hosted/express/overview.md @@ -5,7 +5,8 @@ sidebar_position: 1 sidebar_label: 'Overview' title: 'Scandit Express' displayed_sidebar: expressSidebar -framework: express +framework: hosted +product: express keywords: - express --- diff --git a/docs/hosted/id-bolt/advanced.md b/docs/hosted/id-bolt/advanced.md index 4da47a46f..4602b1ddf 100644 --- a/docs/hosted/id-bolt/advanced.md +++ b/docs/hosted/id-bolt/advanced.md @@ -5,7 +5,8 @@ sidebar_label: "Advanced Options" title: "Advanced Options" displayed_sidebar: boltSidebar toc_max_heading_level: 4 -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt - advanced diff --git a/docs/hosted/id-bolt/api-overview.md b/docs/hosted/id-bolt/api-overview.md index 1281e6312..6f426e3a6 100644 --- a/docs/hosted/id-bolt/api-overview.md +++ b/docs/hosted/id-bolt/api-overview.md @@ -5,7 +5,8 @@ sidebar_label: "API Overview" title: "API Overview" displayed_sidebar: boltSidebar toc_max_heading_level: 4 -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt --- diff --git a/docs/hosted/id-bolt/callbacks.md b/docs/hosted/id-bolt/callbacks.md index 3df255e78..265ba2c58 100644 --- a/docs/hosted/id-bolt/callbacks.md +++ b/docs/hosted/id-bolt/callbacks.md @@ -5,7 +5,8 @@ sidebar_label: "Callbacks" title: "Callbacks" displayed_sidebar: boltSidebar toc_max_heading_level: 4 -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt - callbacks diff --git a/docs/hosted/id-bolt/data-handling.md b/docs/hosted/id-bolt/data-handling.md index b82a4553e..c1bf812f4 100644 --- a/docs/hosted/id-bolt/data-handling.md +++ b/docs/hosted/id-bolt/data-handling.md @@ -5,7 +5,8 @@ sidebar_label: "Data Handling" title: "Data Handling" displayed_sidebar: boltSidebar toc_max_heading_level: 4 -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt - data handling diff --git a/docs/hosted/id-bolt/document-selection.md b/docs/hosted/id-bolt/document-selection.md index 9d08fb11a..032a8b157 100644 --- a/docs/hosted/id-bolt/document-selection.md +++ b/docs/hosted/id-bolt/document-selection.md @@ -5,7 +5,8 @@ sidebar_label: "Document Selection" title: "Document Selection" displayed_sidebar: boltSidebar toc_max_heading_level: 4 -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt - document selection diff --git a/docs/hosted/id-bolt/getting-started.md b/docs/hosted/id-bolt/getting-started.md index 3b9c70460..d886d459d 100644 --- a/docs/hosted/id-bolt/getting-started.md +++ b/docs/hosted/id-bolt/getting-started.md @@ -5,7 +5,8 @@ sidebar_label: "Getting Started" title: "Getting Started" hide_title: false displayed_sidebar: boltSidebar -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt --- diff --git a/docs/hosted/id-bolt/overview.md b/docs/hosted/id-bolt/overview.md index dbfad5ab9..0deda5a56 100644 --- a/docs/hosted/id-bolt/overview.md +++ b/docs/hosted/id-bolt/overview.md @@ -5,7 +5,8 @@ sidebar_position: 1 sidebar_label: "Overview" title: "ID Bolt" displayed_sidebar: boltSidebar -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt --- diff --git a/docs/hosted/id-bolt/release-notes.md b/docs/hosted/id-bolt/release-notes.md index c3f9a15d5..ce84a952f 100644 --- a/docs/hosted/id-bolt/release-notes.md +++ b/docs/hosted/id-bolt/release-notes.md @@ -2,7 +2,8 @@ description: "Release notes for ID Bolt: new features, changes, and fixes by date." toc_max_heading_level: 3 displayed_sidebar: boltSidebar -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt hide_title: true diff --git a/docs/hosted/id-bolt/text-overrides.md b/docs/hosted/id-bolt/text-overrides.md index dfdae7735..a6d15db3b 100644 --- a/docs/hosted/id-bolt/text-overrides.md +++ b/docs/hosted/id-bolt/text-overrides.md @@ -5,7 +5,8 @@ sidebar_label: "Text Overrides" title: "Text Overrides" displayed_sidebar: boltSidebar toc_max_heading_level: 4 -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt - customization diff --git a/docs/hosted/id-bolt/theming.md b/docs/hosted/id-bolt/theming.md index f16294afc..6348acef0 100644 --- a/docs/hosted/id-bolt/theming.md +++ b/docs/hosted/id-bolt/theming.md @@ -5,7 +5,8 @@ sidebar_label: "Theming" title: "Theming" displayed_sidebar: boltSidebar toc_max_heading_level: 4 -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt - theming diff --git a/docs/hosted/id-bolt/validators.md b/docs/hosted/id-bolt/validators.md index 6764ae008..a78b46253 100644 --- a/docs/hosted/id-bolt/validators.md +++ b/docs/hosted/id-bolt/validators.md @@ -5,7 +5,8 @@ sidebar_label: "Validators" title: "Validators" displayed_sidebar: boltSidebar toc_max_heading_level: 4 -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt - validators diff --git a/docs/hosted/id-bolt/workflow.md b/docs/hosted/id-bolt/workflow.md index bec467005..f2ebb5c2c 100644 --- a/docs/hosted/id-bolt/workflow.md +++ b/docs/hosted/id-bolt/workflow.md @@ -5,7 +5,8 @@ sidebar_label: "Workflow Options" title: "Workflow & Scanner Options" displayed_sidebar: boltSidebar toc_max_heading_level: 4 -framework: bolt +framework: hosted +product: id-bolt keywords: - bolt - workflow diff --git a/docs/sdks/net/android/add-sdk.md b/docs/sdks/net/android/add-sdk.md index 09e21e216..be6caefd5 100644 --- a/docs/sdks/net/android/add-sdk.md +++ b/docs/sdks/net/android/add-sdk.md @@ -3,7 +3,7 @@ description: "Add the Scandit Data Capture SDK to your .NET Android project: dep sidebar_position: 1 toc_max_heading_level: 4 pagination_next: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/ai-powered-barcode-scanning.md b/docs/sdks/net/android/ai-powered-barcode-scanning.md index a1f870996..a31f738ef 100644 --- a/docs/sdks/net/android/ai-powered-barcode-scanning.md +++ b/docs/sdks/net/android/ai-powered-barcode-scanning.md @@ -1,7 +1,7 @@ --- description: "import AIPoweredBarcodeScanning from '../../../partials/_ai-powered-barcode-scanning.mdx';" toc_max_heading_level: 4 -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/barcode-capture/configure-barcode-symbologies.md b/docs/sdks/net/android/barcode-capture/configure-barcode-symbologies.md index 3f069dbfc..1d8561d15 100644 --- a/docs/sdks/net/android/barcode-capture/configure-barcode-symbologies.md +++ b/docs/sdks/net/android/barcode-capture/configure-barcode-symbologies.md @@ -2,7 +2,7 @@ description: "Enable and configure barcode symbologies for Barcode Capture on .NET Android." sidebar_position: 3 pagination_next: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/barcode-capture/get-started.md b/docs/sdks/net/android/barcode-capture/get-started.md index 3edd00a2a..302ca319a 100644 --- a/docs/sdks/net/android/barcode-capture/get-started.md +++ b/docs/sdks/net/android/barcode-capture/get-started.md @@ -2,7 +2,7 @@ description: "Add Barcode Capture to your .NET Android app: configure the mode, set up the camera and view, and handle scanned barcodes." sidebar_position: 2 pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/barcode-selection/get-started.md b/docs/sdks/net/android/barcode-selection/get-started.md index 5a397349e..113e69919 100644 --- a/docs/sdks/net/android/barcode-selection/get-started.md +++ b/docs/sdks/net/android/barcode-selection/get-started.md @@ -2,7 +2,7 @@ description: "Add Barcode Selection to your .NET Android app so users can tap or aim to select a single barcode among many." sidebar_position: 2 pagination_next: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/barcode-selection/intro.md b/docs/sdks/net/android/barcode-selection/intro.md index c9425a30f..8c3d5101a 100644 --- a/docs/sdks/net/android/barcode-selection/intro.md +++ b/docs/sdks/net/android/barcode-selection/intro.md @@ -2,7 +2,7 @@ description: "Barcode Selection lets users tap or aim to pick one barcode among many in your .NET Android app." sidebar_position: 1 pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/batch-scanning.md b/docs/sdks/net/android/batch-scanning.md index 951e7f210..f00602635 100644 --- a/docs/sdks/net/android/batch-scanning.md +++ b/docs/sdks/net/android/batch-scanning.md @@ -1,7 +1,7 @@ --- description: "Scan and process many barcodes in one session in your .NET Android app with the Scandit Data Capture SDK." pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/id-capture/advanced.md b/docs/sdks/net/android/id-capture/advanced.md index 3427969a3..a989225cb 100644 --- a/docs/sdks/net/android/id-capture/advanced.md +++ b/docs/sdks/net/android/id-capture/advanced.md @@ -2,7 +2,7 @@ description: "Advanced ID Capture settings for .NET Android: configure document types, extracted fields, and verification." sidebar_position: 4 pagination_next: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/id-capture/get-started.md b/docs/sdks/net/android/id-capture/get-started.md index 891379e6b..18cb3cfa9 100644 --- a/docs/sdks/net/android/id-capture/get-started.md +++ b/docs/sdks/net/android/id-capture/get-started.md @@ -1,7 +1,7 @@ --- description: "Add ID Capture to your .NET Android app to scan and extract data from IDs such as driver's licenses, passports, and ID cards." sidebar_position: 2 -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/id-capture/intro.md b/docs/sdks/net/android/id-capture/intro.md index b80b187ad..93d74fee2 100644 --- a/docs/sdks/net/android/id-capture/intro.md +++ b/docs/sdks/net/android/id-capture/intro.md @@ -5,7 +5,7 @@ title: About ID Capture sidebar_position: 1 toc_max_heading_level: 4 pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/id-capture/supported-documents.md b/docs/sdks/net/android/id-capture/supported-documents.md index bede24184..c71bbf325 100644 --- a/docs/sdks/net/android/id-capture/supported-documents.md +++ b/docs/sdks/net/android/id-capture/supported-documents.md @@ -6,7 +6,7 @@ hide_title: true toc_min_heading_level: 2 toc_max_heading_level: 4 sidebar_position: 3 -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan-ar/get-started.md b/docs/sdks/net/android/matrixscan-ar/get-started.md index 968ddcdfb..c3e34762a 100644 --- a/docs/sdks/net/android/matrixscan-ar/get-started.md +++ b/docs/sdks/net/android/matrixscan-ar/get-started.md @@ -1,7 +1,7 @@ --- description: "Add MatrixScan AR to your .NET Android app: set up the view and the overlays that guide users to the right items." sidebar_position: 2 -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan-ar/intro.md b/docs/sdks/net/android/matrixscan-ar/intro.md index 9722380f7..78ab765fd 100644 --- a/docs/sdks/net/android/matrixscan-ar/intro.md +++ b/docs/sdks/net/android/matrixscan-ar/intro.md @@ -2,7 +2,7 @@ description: "MatrixScan AR adds augmented-reality overlays that highlight barcodes and show extra information in your .NET Android app." displayed_sidebar: netIosSidebar pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan-count/advanced.md b/docs/sdks/net/android/matrixscan-count/advanced.md index f04ab1aad..feb1aa806 100644 --- a/docs/sdks/net/android/matrixscan-count/advanced.md +++ b/docs/sdks/net/android/matrixscan-count/advanced.md @@ -2,7 +2,7 @@ description: "Advanced MatrixScan Count settings for .NET Android to tune counting accuracy, performance, and the user experience." sidebar_position: 3 pagination_next: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan-count/get-started.md b/docs/sdks/net/android/matrixscan-count/get-started.md index d6ff7eadc..0ce8f3234 100644 --- a/docs/sdks/net/android/matrixscan-count/get-started.md +++ b/docs/sdks/net/android/matrixscan-count/get-started.md @@ -1,7 +1,7 @@ --- description: "Add MatrixScan Count to your .NET Android app: set up the mode, scan and count multiple barcodes, and handle the results." sidebar_position: 2 -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan-count/intro.md b/docs/sdks/net/android/matrixscan-count/intro.md index bebf4b152..6c163ef44 100644 --- a/docs/sdks/net/android/matrixscan-count/intro.md +++ b/docs/sdks/net/android/matrixscan-count/intro.md @@ -2,7 +2,7 @@ description: "MatrixScan Count scans and counts many barcodes at once in your .NET Android app." sidebar_position: 1 pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan-find/advanced.md b/docs/sdks/net/android/matrixscan-find/advanced.md index 1414b202d..f6b76905a 100644 --- a/docs/sdks/net/android/matrixscan-find/advanced.md +++ b/docs/sdks/net/android/matrixscan-find/advanced.md @@ -2,7 +2,7 @@ description: "Advanced MatrixScan Find settings for .NET Android: customize matching, overlays, and search behavior." sidebar_position: 3 pagination_next: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan-find/get-started.md b/docs/sdks/net/android/matrixscan-find/get-started.md index 0114b2313..6904d2d80 100644 --- a/docs/sdks/net/android/matrixscan-find/get-started.md +++ b/docs/sdks/net/android/matrixscan-find/get-started.md @@ -1,7 +1,7 @@ --- description: "Add MatrixScan Find to your .NET Android app to search for and highlight specific items with the camera." sidebar_position: 2 -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan-find/intro.md b/docs/sdks/net/android/matrixscan-find/intro.md index 63079ef67..2288a3666 100644 --- a/docs/sdks/net/android/matrixscan-find/intro.md +++ b/docs/sdks/net/android/matrixscan-find/intro.md @@ -2,7 +2,7 @@ description: "MatrixScan Find helps users locate specific items in your .NET Android app by highlighting matches in the camera feed." sidebar_position: 1 pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan-pick/advanced.md b/docs/sdks/net/android/matrixscan-pick/advanced.md index affdc4c26..046af10ef 100644 --- a/docs/sdks/net/android/matrixscan-pick/advanced.md +++ b/docs/sdks/net/android/matrixscan-pick/advanced.md @@ -2,7 +2,7 @@ description: "Advanced MatrixScan Pick settings for .NET Android: customize picking logic, overlays, and workflows." sidebar_position: 3 pagination_next: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan-pick/get-started.md b/docs/sdks/net/android/matrixscan-pick/get-started.md index c4427dcfb..4b73c2327 100644 --- a/docs/sdks/net/android/matrixscan-pick/get-started.md +++ b/docs/sdks/net/android/matrixscan-pick/get-started.md @@ -1,7 +1,7 @@ --- description: "Add MatrixScan Pick to your .NET Android app to guide users through order picking with AR cues." sidebar_position: 2 -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan-pick/intro.md b/docs/sdks/net/android/matrixscan-pick/intro.md index 646aae7e2..283657703 100644 --- a/docs/sdks/net/android/matrixscan-pick/intro.md +++ b/docs/sdks/net/android/matrixscan-pick/intro.md @@ -2,7 +2,7 @@ description: "MatrixScan Pick guides order picking in your .NET Android app with on-screen AR cues for the items to pick." sidebar_position: 1 pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan/advanced.md b/docs/sdks/net/android/matrixscan/advanced.md index 9b084e231..f867a6f92 100644 --- a/docs/sdks/net/android/matrixscan/advanced.md +++ b/docs/sdks/net/android/matrixscan/advanced.md @@ -2,7 +2,7 @@ description: "Advanced MatrixScan settings for .NET Android: tune tracking, overlays, and scanning performance." sidebar_position: 3 pagination_next: null -framework: netAndroid +framework: net-android keywords: - netAndroid diff --git a/docs/sdks/net/android/matrixscan/get-started.md b/docs/sdks/net/android/matrixscan/get-started.md index d805d9c40..d8af7e29a 100644 --- a/docs/sdks/net/android/matrixscan/get-started.md +++ b/docs/sdks/net/android/matrixscan/get-started.md @@ -1,7 +1,7 @@ --- description: "Add MatrixScan to your .NET Android app to detect, track, and highlight multiple barcodes in the same frame." sidebar_position: 2 -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/matrixscan/intro.md b/docs/sdks/net/android/matrixscan/intro.md index da33c690f..32fd7cc4d 100644 --- a/docs/sdks/net/android/matrixscan/intro.md +++ b/docs/sdks/net/android/matrixscan/intro.md @@ -2,7 +2,7 @@ description: "MatrixScan detects, tracks, and highlights multiple barcodes in the same frame in your .NET Android app." sidebar_position: 1 pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/parser/get-started.md b/docs/sdks/net/android/parser/get-started.md index 589016758..c9f39926c 100644 --- a/docs/sdks/net/android/parser/get-started.md +++ b/docs/sdks/net/android/parser/get-started.md @@ -3,7 +3,7 @@ description: "Use the Scandit parser in your .NET Android app to turn barcode da sidebar_position: 2 pagination_prev: null pagination_next: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/release-notes.md b/docs/sdks/net/android/release-notes.md index 6ebcd675d..893499f94 100644 --- a/docs/sdks/net/android/release-notes.md +++ b/docs/sdks/net/android/release-notes.md @@ -5,7 +5,7 @@ displayed_sidebar: netAndroidSidebar hide_title: true title: Release Notes pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/single-scanning.md b/docs/sdks/net/android/single-scanning.md index b76eeb751..e6bd4c4eb 100644 --- a/docs/sdks/net/android/single-scanning.md +++ b/docs/sdks/net/android/single-scanning.md @@ -2,7 +2,7 @@ description: "Scan a single barcode at a time in your .NET Android app with the Scandit Data Capture SDK." toc_max_heading_level: 4 pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/sparkscan/advanced.md b/docs/sdks/net/android/sparkscan/advanced.md index f1a3c90b7..34ba5085a 100644 --- a/docs/sdks/net/android/sparkscan/advanced.md +++ b/docs/sdks/net/android/sparkscan/advanced.md @@ -2,7 +2,7 @@ description: "Advanced SparkScan settings for .NET Android: customize the UI, scanning behavior, and feedback beyond the defaults." sidebar_position: 3 pagination_next: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/sparkscan/get-started.md b/docs/sdks/net/android/sparkscan/get-started.md index 179a89bfe..7aab41b2f 100644 --- a/docs/sdks/net/android/sparkscan/get-started.md +++ b/docs/sdks/net/android/sparkscan/get-started.md @@ -1,7 +1,7 @@ --- description: "Add SparkScan to your .NET Android app: create a data capture context, configure the mode, add the view, and handle scanned barcodes." sidebar_position: 2 -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/android/sparkscan/intro.md b/docs/sdks/net/android/sparkscan/intro.md index e6e48d0b9..b9d873799 100644 --- a/docs/sdks/net/android/sparkscan/intro.md +++ b/docs/sdks/net/android/sparkscan/intro.md @@ -2,7 +2,7 @@ description: "SparkScan is a prebuilt smartphone scanning UI for fast, ergonomic barcode scanning that drops on top of any .NET Android app." sidebar_position: 1 pagination_prev: null -framework: netAndroid +framework: net-android keywords: - netAndroid --- diff --git a/docs/sdks/net/ios/add-sdk.md b/docs/sdks/net/ios/add-sdk.md index 44dad6c57..8b0f652bc 100644 --- a/docs/sdks/net/ios/add-sdk.md +++ b/docs/sdks/net/ios/add-sdk.md @@ -3,7 +3,7 @@ description: "Add the Scandit Data Capture SDK to your .NET iOS project: depende sidebar_position: 1 toc_max_heading_level: 4 pagination_next: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/ai-powered-barcode-scanning.md b/docs/sdks/net/ios/ai-powered-barcode-scanning.md index cff13dce6..5d508e8e8 100644 --- a/docs/sdks/net/ios/ai-powered-barcode-scanning.md +++ b/docs/sdks/net/ios/ai-powered-barcode-scanning.md @@ -1,7 +1,7 @@ --- description: "import AIPoweredBarcodeScanning from '../../../partials/_ai-powered-barcode-scanning.mdx';" toc_max_heading_level: 4 -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/barcode-capture/configure-barcode-symbologies.md b/docs/sdks/net/ios/barcode-capture/configure-barcode-symbologies.md index 4c139ba71..e7aac752b 100644 --- a/docs/sdks/net/ios/barcode-capture/configure-barcode-symbologies.md +++ b/docs/sdks/net/ios/barcode-capture/configure-barcode-symbologies.md @@ -2,7 +2,7 @@ description: "Enable and configure barcode symbologies for Barcode Capture on .NET iOS." sidebar_position: 3 pagination_next: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/barcode-capture/get-started.md b/docs/sdks/net/ios/barcode-capture/get-started.md index 01f6d4e42..fb2548638 100644 --- a/docs/sdks/net/ios/barcode-capture/get-started.md +++ b/docs/sdks/net/ios/barcode-capture/get-started.md @@ -2,7 +2,7 @@ description: "Add Barcode Capture to your .NET iOS app: configure the mode, set up the camera and view, and handle scanned barcodes." sidebar_position: 2 pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/barcode-selection/get-started.md b/docs/sdks/net/ios/barcode-selection/get-started.md index d00b80c27..a38152ae6 100644 --- a/docs/sdks/net/ios/barcode-selection/get-started.md +++ b/docs/sdks/net/ios/barcode-selection/get-started.md @@ -2,7 +2,7 @@ description: "Add Barcode Selection to your .NET iOS app so users can tap or aim to select a single barcode among many." sidebar_position: 2 pagination_next: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/barcode-selection/intro.md b/docs/sdks/net/ios/barcode-selection/intro.md index 583e41b0c..46c1f3616 100644 --- a/docs/sdks/net/ios/barcode-selection/intro.md +++ b/docs/sdks/net/ios/barcode-selection/intro.md @@ -2,7 +2,7 @@ description: "Barcode Selection lets users tap or aim to pick one barcode among many in your .NET iOS app." sidebar_position: 1 pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/batch-scanning.md b/docs/sdks/net/ios/batch-scanning.md index e137ef4e1..22da320d5 100644 --- a/docs/sdks/net/ios/batch-scanning.md +++ b/docs/sdks/net/ios/batch-scanning.md @@ -1,7 +1,7 @@ --- description: "Scan and process many barcodes in one session in your .NET iOS app with the Scandit Data Capture SDK." pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/id-capture/advanced.md b/docs/sdks/net/ios/id-capture/advanced.md index 09f9a0f7f..0d1f34952 100644 --- a/docs/sdks/net/ios/id-capture/advanced.md +++ b/docs/sdks/net/ios/id-capture/advanced.md @@ -2,7 +2,7 @@ description: "Advanced ID Capture settings for .NET iOS: configure document types, extracted fields, and verification." sidebar_position: 4 pagination_next: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/id-capture/get-started.md b/docs/sdks/net/ios/id-capture/get-started.md index 3817a37e0..a217ddb04 100644 --- a/docs/sdks/net/ios/id-capture/get-started.md +++ b/docs/sdks/net/ios/id-capture/get-started.md @@ -1,7 +1,7 @@ --- description: "Add ID Capture to your .NET iOS app to scan and extract data from IDs such as driver's licenses, passports, and ID cards." sidebar_position: 2 -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/id-capture/intro.md b/docs/sdks/net/ios/id-capture/intro.md index 89a749028..939aa3694 100644 --- a/docs/sdks/net/ios/id-capture/intro.md +++ b/docs/sdks/net/ios/id-capture/intro.md @@ -5,7 +5,7 @@ title: About ID Capture sidebar_position: 1 toc_max_heading_level: 4 pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/id-capture/supported-documents.md b/docs/sdks/net/ios/id-capture/supported-documents.md index e9c45de56..12326edbf 100644 --- a/docs/sdks/net/ios/id-capture/supported-documents.md +++ b/docs/sdks/net/ios/id-capture/supported-documents.md @@ -6,7 +6,7 @@ hide_title: true toc_min_heading_level: 2 toc_max_heading_level: 4 sidebar_position: 3 -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan-ar/get-started.md b/docs/sdks/net/ios/matrixscan-ar/get-started.md index e03da39ba..90305bc79 100644 --- a/docs/sdks/net/ios/matrixscan-ar/get-started.md +++ b/docs/sdks/net/ios/matrixscan-ar/get-started.md @@ -1,7 +1,7 @@ --- description: "Add MatrixScan AR to your .NET iOS app: set up the view and the overlays that guide users to the right items while scanning." sidebar_position: 2 -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan-ar/intro.md b/docs/sdks/net/ios/matrixscan-ar/intro.md index e74a49871..32131b482 100644 --- a/docs/sdks/net/ios/matrixscan-ar/intro.md +++ b/docs/sdks/net/ios/matrixscan-ar/intro.md @@ -2,7 +2,7 @@ description: "MatrixScan AR adds augmented-reality overlays that highlight barcodes and show extra information in your .NET iOS app." displayed_sidebar: netIosSidebar pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan-count/advanced.md b/docs/sdks/net/ios/matrixscan-count/advanced.md index aee4d7583..a936d066d 100644 --- a/docs/sdks/net/ios/matrixscan-count/advanced.md +++ b/docs/sdks/net/ios/matrixscan-count/advanced.md @@ -2,7 +2,7 @@ description: "Advanced MatrixScan Count settings for .NET iOS to tune counting accuracy, performance, and the user experience." sidebar_position: 3 pagination_next: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan-count/get-started.md b/docs/sdks/net/ios/matrixscan-count/get-started.md index 7579fc49b..e4514f870 100644 --- a/docs/sdks/net/ios/matrixscan-count/get-started.md +++ b/docs/sdks/net/ios/matrixscan-count/get-started.md @@ -1,7 +1,7 @@ --- description: "Add MatrixScan Count to your .NET iOS app: set up the mode, scan and count multiple barcodes, and handle the results." sidebar_position: 2 -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan-count/intro.md b/docs/sdks/net/ios/matrixscan-count/intro.md index f9b529e2e..96fcc20c7 100644 --- a/docs/sdks/net/ios/matrixscan-count/intro.md +++ b/docs/sdks/net/ios/matrixscan-count/intro.md @@ -2,7 +2,7 @@ description: "MatrixScan Count scans and counts many barcodes at once in your .NET iOS app." sidebar_position: 1 pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan-find/advanced.md b/docs/sdks/net/ios/matrixscan-find/advanced.md index c6978574b..533886992 100644 --- a/docs/sdks/net/ios/matrixscan-find/advanced.md +++ b/docs/sdks/net/ios/matrixscan-find/advanced.md @@ -2,7 +2,7 @@ description: "Advanced MatrixScan Find settings for .NET iOS: customize matching, overlays, and search behavior." sidebar_position: 3 pagination_next: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan-find/get-started.md b/docs/sdks/net/ios/matrixscan-find/get-started.md index 258d631a3..49dd94fdb 100644 --- a/docs/sdks/net/ios/matrixscan-find/get-started.md +++ b/docs/sdks/net/ios/matrixscan-find/get-started.md @@ -1,7 +1,7 @@ --- description: "Add MatrixScan Find to your .NET iOS app to search for and highlight specific items with the camera." sidebar_position: 2 -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan-find/intro.md b/docs/sdks/net/ios/matrixscan-find/intro.md index 0791d8af0..944a279e8 100644 --- a/docs/sdks/net/ios/matrixscan-find/intro.md +++ b/docs/sdks/net/ios/matrixscan-find/intro.md @@ -2,7 +2,7 @@ description: "MatrixScan Find helps users locate specific items in your .NET iOS app by highlighting matches in the camera feed." sidebar_position: 1 pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan-pick/advanced.md b/docs/sdks/net/ios/matrixscan-pick/advanced.md index 75091c07e..abe132765 100644 --- a/docs/sdks/net/ios/matrixscan-pick/advanced.md +++ b/docs/sdks/net/ios/matrixscan-pick/advanced.md @@ -2,7 +2,7 @@ description: "Advanced MatrixScan Pick settings for .NET iOS: customize picking logic, overlays, and workflows." sidebar_position: 3 pagination_next: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan-pick/get-started.md b/docs/sdks/net/ios/matrixscan-pick/get-started.md index e0a1d7adb..24669d833 100644 --- a/docs/sdks/net/ios/matrixscan-pick/get-started.md +++ b/docs/sdks/net/ios/matrixscan-pick/get-started.md @@ -1,7 +1,7 @@ --- description: "Add MatrixScan Pick to your .NET iOS app to guide users through order picking with AR cues." sidebar_position: 2 -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan-pick/intro.md b/docs/sdks/net/ios/matrixscan-pick/intro.md index b8a7f9712..5921c6ddb 100644 --- a/docs/sdks/net/ios/matrixscan-pick/intro.md +++ b/docs/sdks/net/ios/matrixscan-pick/intro.md @@ -2,7 +2,7 @@ description: "MatrixScan Pick guides order picking in your .NET iOS app with on-screen AR cues for the items to pick." sidebar_position: 1 pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan/advanced.md b/docs/sdks/net/ios/matrixscan/advanced.md index 5a246c1ba..dd3405f5d 100644 --- a/docs/sdks/net/ios/matrixscan/advanced.md +++ b/docs/sdks/net/ios/matrixscan/advanced.md @@ -2,7 +2,7 @@ description: "Advanced MatrixScan settings for .NET iOS: tune tracking, overlays, and scanning performance." sidebar_position: 3 pagination_next: null -framework: netIos +framework: net-ios keywords: - netIos diff --git a/docs/sdks/net/ios/matrixscan/get-started.md b/docs/sdks/net/ios/matrixscan/get-started.md index 64de292bd..32dbac3dd 100644 --- a/docs/sdks/net/ios/matrixscan/get-started.md +++ b/docs/sdks/net/ios/matrixscan/get-started.md @@ -1,7 +1,7 @@ --- description: "Add MatrixScan to your .NET iOS app to detect, track, and highlight multiple barcodes in the same frame." sidebar_position: 2 -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/matrixscan/intro.md b/docs/sdks/net/ios/matrixscan/intro.md index 0d72393a4..21bf1df85 100644 --- a/docs/sdks/net/ios/matrixscan/intro.md +++ b/docs/sdks/net/ios/matrixscan/intro.md @@ -2,7 +2,7 @@ description: "MatrixScan detects, tracks, and highlights multiple barcodes in the same frame in your .NET iOS app." sidebar_position: 1 pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/parser/get-started.md b/docs/sdks/net/ios/parser/get-started.md index 08fc5e455..1801a70f3 100644 --- a/docs/sdks/net/ios/parser/get-started.md +++ b/docs/sdks/net/ios/parser/get-started.md @@ -3,7 +3,7 @@ description: "Use the Scandit parser in your .NET iOS app to turn barcode data s sidebar_position: 2 pagination_prev: null pagination_next: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/release-notes.md b/docs/sdks/net/ios/release-notes.md index 61a8bfc75..a14ca5223 100644 --- a/docs/sdks/net/ios/release-notes.md +++ b/docs/sdks/net/ios/release-notes.md @@ -5,7 +5,7 @@ displayed_sidebar: netIosSidebar hide_title: true title: Release Notes pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/single-scanning.md b/docs/sdks/net/ios/single-scanning.md index 22f337184..23b5bd96f 100644 --- a/docs/sdks/net/ios/single-scanning.md +++ b/docs/sdks/net/ios/single-scanning.md @@ -2,7 +2,7 @@ description: "Scan a single barcode at a time in your .NET iOS app with the Scandit Data Capture SDK." toc_max_heading_level: 4 pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/sparkscan/advanced.md b/docs/sdks/net/ios/sparkscan/advanced.md index fe265e530..506e66859 100644 --- a/docs/sdks/net/ios/sparkscan/advanced.md +++ b/docs/sdks/net/ios/sparkscan/advanced.md @@ -2,7 +2,7 @@ description: "Advanced SparkScan settings for .NET iOS: customize the UI, scanning behavior, and feedback beyond the defaults." sidebar_position: 3 pagination_next: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/sparkscan/get-started.md b/docs/sdks/net/ios/sparkscan/get-started.md index d316aeb21..1539abf94 100644 --- a/docs/sdks/net/ios/sparkscan/get-started.md +++ b/docs/sdks/net/ios/sparkscan/get-started.md @@ -1,7 +1,7 @@ --- description: "Add SparkScan to your .NET iOS app: create a data capture context, configure the mode, add the view, and handle scanned barcodes." sidebar_position: 2 -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/net/ios/sparkscan/intro.md b/docs/sdks/net/ios/sparkscan/intro.md index ccfdde90a..39f9bcf29 100644 --- a/docs/sdks/net/ios/sparkscan/intro.md +++ b/docs/sdks/net/ios/sparkscan/intro.md @@ -2,7 +2,7 @@ description: "SparkScan is a prebuilt scanning UI for fast, ergonomic barcode scanning that drops on top of any .NET iOS app." sidebar_position: 1 pagination_prev: null -framework: netIos +framework: net-ios keywords: - netIos --- diff --git a/docs/sdks/react-native/add-sdk.md b/docs/sdks/react-native/add-sdk.md index 1be838094..fedea715c 100644 --- a/docs/sdks/react-native/add-sdk.md +++ b/docs/sdks/react-native/add-sdk.md @@ -3,7 +3,7 @@ description: "Add the Scandit Data Capture SDK to your React Native project: dep sidebar_position: 1 toc_max_heading_level: 4 pagination_next: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/barcode-capture/configure-barcode-symbologies.md b/docs/sdks/react-native/barcode-capture/configure-barcode-symbologies.md index 0d9b45a95..5aaaf4879 100644 --- a/docs/sdks/react-native/barcode-capture/configure-barcode-symbologies.md +++ b/docs/sdks/react-native/barcode-capture/configure-barcode-symbologies.md @@ -3,7 +3,7 @@ description: "Learn about the available symbologies and the corresponding config sidebar_position: 3 pagination_next: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/barcode-capture/get-started.md b/docs/sdks/react-native/barcode-capture/get-started.md index cf2b565e9..ceee63c01 100644 --- a/docs/sdks/react-native/barcode-capture/get-started.md +++ b/docs/sdks/react-native/barcode-capture/get-started.md @@ -2,7 +2,7 @@ description: "Add Barcode Capture to your React Native app: configure the mode, set up the camera and view, and handle scanned barcodes." sidebar_position: 2 pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/barcode-selection/get-started.md b/docs/sdks/react-native/barcode-selection/get-started.md index 5c9a2b1ce..07063b689 100644 --- a/docs/sdks/react-native/barcode-selection/get-started.md +++ b/docs/sdks/react-native/barcode-selection/get-started.md @@ -3,7 +3,7 @@ description: "Add Barcode Selection to your React Native app so users can tap or sidebar_position: 2 pagination_next: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/barcode-selection/intro.md b/docs/sdks/react-native/barcode-selection/intro.md index cf82c55cc..4da37cb3a 100644 --- a/docs/sdks/react-native/barcode-selection/intro.md +++ b/docs/sdks/react-native/barcode-selection/intro.md @@ -3,7 +3,7 @@ description: "Barcode Selection lets users tap or aim to pick one barcode among sidebar_position: 1 pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/batch-scanning.md b/docs/sdks/react-native/batch-scanning.md index 8c87fa671..c0b6d7971 100644 --- a/docs/sdks/react-native/batch-scanning.md +++ b/docs/sdks/react-native/batch-scanning.md @@ -4,7 +4,7 @@ toc_max_heading_level: 4 pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/id-capture/advanced.md b/docs/sdks/react-native/id-capture/advanced.md index 9d8863d8f..935a15fe3 100644 --- a/docs/sdks/react-native/id-capture/advanced.md +++ b/docs/sdks/react-native/id-capture/advanced.md @@ -3,7 +3,7 @@ description: "Advanced ID Capture settings for React Native: configure document sidebar_position: 4 pagination_next: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/id-capture/get-started.md b/docs/sdks/react-native/id-capture/get-started.md index 486f0cb09..2ce9897a9 100644 --- a/docs/sdks/react-native/id-capture/get-started.md +++ b/docs/sdks/react-native/id-capture/get-started.md @@ -2,7 +2,7 @@ description: "Add ID Capture to your React Native app to scan and extract data from IDs such as driver's licenses, passports, and ID cards." sidebar_position: 2 -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/id-capture/intro.md b/docs/sdks/react-native/id-capture/intro.md index 02a2c044c..effc62f62 100644 --- a/docs/sdks/react-native/id-capture/intro.md +++ b/docs/sdks/react-native/id-capture/intro.md @@ -6,7 +6,7 @@ title: About ID Capture sidebar_position: 1 toc_max_heading_level: 4 pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/label-capture/label-definitions.md b/docs/sdks/react-native/label-capture/label-definitions.md index 429bca565..8f6c9fe7d 100644 --- a/docs/sdks/react-native/label-capture/label-definitions.md +++ b/docs/sdks/react-native/label-capture/label-definitions.md @@ -1,7 +1,7 @@ --- description: "Define label layouts for Smart Label Capture on React Native: the barcodes and text fields to extract." -framework: react +framework: react-native toc_max_heading_level: 4 keywords: - react diff --git a/docs/sdks/react-native/matrixscan-ar/get-started.md b/docs/sdks/react-native/matrixscan-ar/get-started.md index b4973f45a..de1386742 100644 --- a/docs/sdks/react-native/matrixscan-ar/get-started.md +++ b/docs/sdks/react-native/matrixscan-ar/get-started.md @@ -2,7 +2,7 @@ description: "Add MatrixScan AR to your React Native app: set up the view and the overlays that guide users to the right items." sidebar_position: 2 -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/matrixscan-ar/intro.md b/docs/sdks/react-native/matrixscan-ar/intro.md index cc031bfbf..7613f8c43 100644 --- a/docs/sdks/react-native/matrixscan-ar/intro.md +++ b/docs/sdks/react-native/matrixscan-ar/intro.md @@ -3,7 +3,7 @@ description: "MatrixScan AR adds augmented-reality overlays that highlight barco sidebar_position: 1 pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/matrixscan-count/advanced.md b/docs/sdks/react-native/matrixscan-count/advanced.md index 1b7828c3a..110e6c412 100644 --- a/docs/sdks/react-native/matrixscan-count/advanced.md +++ b/docs/sdks/react-native/matrixscan-count/advanced.md @@ -3,7 +3,7 @@ description: "Advanced MatrixScan Count settings for React Native to tune counti sidebar_position: 3 pagination_next: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/matrixscan-count/get-started.md b/docs/sdks/react-native/matrixscan-count/get-started.md index 41ff85ae0..846d7ba43 100644 --- a/docs/sdks/react-native/matrixscan-count/get-started.md +++ b/docs/sdks/react-native/matrixscan-count/get-started.md @@ -2,7 +2,7 @@ description: "Add MatrixScan Count to your React Native app: set up the mode, scan and count multiple barcodes, and handle the results." sidebar_position: 2 -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/matrixscan-count/intro.md b/docs/sdks/react-native/matrixscan-count/intro.md index 1fb9378b2..9725735c2 100644 --- a/docs/sdks/react-native/matrixscan-count/intro.md +++ b/docs/sdks/react-native/matrixscan-count/intro.md @@ -3,7 +3,7 @@ description: "MatrixScan Count scans and counts many barcodes at once in your Re sidebar_position: 1 pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/matrixscan-find/advanced.md b/docs/sdks/react-native/matrixscan-find/advanced.md index b6d861fda..78d238475 100644 --- a/docs/sdks/react-native/matrixscan-find/advanced.md +++ b/docs/sdks/react-native/matrixscan-find/advanced.md @@ -3,7 +3,7 @@ description: "Advanced MatrixScan Find settings for React Native: customize matc sidebar_position: 3 pagination_next: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/matrixscan-find/get-started.md b/docs/sdks/react-native/matrixscan-find/get-started.md index 047f413bb..ca291d386 100644 --- a/docs/sdks/react-native/matrixscan-find/get-started.md +++ b/docs/sdks/react-native/matrixscan-find/get-started.md @@ -2,7 +2,7 @@ description: "Add MatrixScan Find to your React Native app to search for and highlight specific items with the camera." sidebar_position: 2 -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/matrixscan-find/intro.md b/docs/sdks/react-native/matrixscan-find/intro.md index a2aa33e5d..1084a3a3f 100644 --- a/docs/sdks/react-native/matrixscan-find/intro.md +++ b/docs/sdks/react-native/matrixscan-find/intro.md @@ -3,7 +3,7 @@ description: "MatrixScan Find helps users locate specific items in your React Na sidebar_position: 1 pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/matrixscan-pick/advanced.md b/docs/sdks/react-native/matrixscan-pick/advanced.md index 952ee0656..0a8ed9e93 100644 --- a/docs/sdks/react-native/matrixscan-pick/advanced.md +++ b/docs/sdks/react-native/matrixscan-pick/advanced.md @@ -3,7 +3,7 @@ description: "Advanced MatrixScan Pick settings for React Native: customize pick sidebar_position: 3 pagination_next: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/matrixscan-pick/get-started.md b/docs/sdks/react-native/matrixscan-pick/get-started.md index f5c6a92e3..46ddd473c 100644 --- a/docs/sdks/react-native/matrixscan-pick/get-started.md +++ b/docs/sdks/react-native/matrixscan-pick/get-started.md @@ -2,7 +2,7 @@ description: "Add MatrixScan Pick to your React Native app to guide users through order picking with AR cues." sidebar_position: 2 -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/matrixscan-pick/intro.md b/docs/sdks/react-native/matrixscan-pick/intro.md index 1d3388827..fc06cac40 100644 --- a/docs/sdks/react-native/matrixscan-pick/intro.md +++ b/docs/sdks/react-native/matrixscan-pick/intro.md @@ -3,7 +3,7 @@ description: "MatrixScan Pick guides order picking in your React Native app with sidebar_position: 1 pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/matrixscan/advanced.md b/docs/sdks/react-native/matrixscan/advanced.md index 41451a77a..afd691c01 100644 --- a/docs/sdks/react-native/matrixscan/advanced.md +++ b/docs/sdks/react-native/matrixscan/advanced.md @@ -3,7 +3,7 @@ description: "Advanced MatrixScan settings for React Native: tune tracking, over sidebar_position: 3 pagination_next: null -framework: react +framework: react-native keywords: - react diff --git a/docs/sdks/react-native/matrixscan/get-started.md b/docs/sdks/react-native/matrixscan/get-started.md index 7c552551b..14ede8e82 100644 --- a/docs/sdks/react-native/matrixscan/get-started.md +++ b/docs/sdks/react-native/matrixscan/get-started.md @@ -1,7 +1,7 @@ --- description: "Add MatrixScan to your React Native app to detect, track, and highlight multiple barcodes in the same frame." -framework: react +framework: react-native keywords: - react diff --git a/docs/sdks/react-native/matrixscan/intro.md b/docs/sdks/react-native/matrixscan/intro.md index ab3d0972c..d69d1783a 100644 --- a/docs/sdks/react-native/matrixscan/intro.md +++ b/docs/sdks/react-native/matrixscan/intro.md @@ -3,7 +3,7 @@ description: "MatrixScan detects, tracks, and highlights multiple barcodes in th sidebar_position: 1 pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/parser/get-started.md b/docs/sdks/react-native/parser/get-started.md index cc45ea178..44b06d939 100644 --- a/docs/sdks/react-native/parser/get-started.md +++ b/docs/sdks/react-native/parser/get-started.md @@ -4,7 +4,7 @@ description: "Use the Scandit parser in React Native to turn barcode data string sidebar_position: 2 pagination_prev: null pagination_next: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/release-notes.md b/docs/sdks/react-native/release-notes.md index 6b837cfc6..4cf26f925 100644 --- a/docs/sdks/react-native/release-notes.md +++ b/docs/sdks/react-native/release-notes.md @@ -5,7 +5,7 @@ displayed_sidebar: reactnativeSidebar hide_title: true title: Release Notes pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/single-scanning.md b/docs/sdks/react-native/single-scanning.md index 7e8c7fa1b..1f1641bbb 100644 --- a/docs/sdks/react-native/single-scanning.md +++ b/docs/sdks/react-native/single-scanning.md @@ -3,7 +3,7 @@ description: "Scan a single barcode at a time in your React Native app with the toc_max_heading_level: 4 pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/sparkscan/advanced.md b/docs/sdks/react-native/sparkscan/advanced.md index 300921dce..dfd4b94b1 100644 --- a/docs/sdks/react-native/sparkscan/advanced.md +++ b/docs/sdks/react-native/sparkscan/advanced.md @@ -3,7 +3,7 @@ description: "Advanced SparkScan settings for React Native: customize the UI, sc sidebar_position: 3 pagination_next: null -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/sparkscan/get-started.md b/docs/sdks/react-native/sparkscan/get-started.md index 41424f175..8219eec22 100644 --- a/docs/sdks/react-native/sparkscan/get-started.md +++ b/docs/sdks/react-native/sparkscan/get-started.md @@ -2,7 +2,7 @@ description: "Add SparkScan to your React Native app: create a data capture context, configure the mode, add the view, and handle scanned barcodes." sidebar_position: 2 -framework: react +framework: react-native keywords: - react --- diff --git a/docs/sdks/react-native/sparkscan/intro.md b/docs/sdks/react-native/sparkscan/intro.md index 051919ae8..2969fa7b8 100644 --- a/docs/sdks/react-native/sparkscan/intro.md +++ b/docs/sdks/react-native/sparkscan/intro.md @@ -3,7 +3,7 @@ description: "SparkScan is a prebuilt scanning UI for fast, ergonomic barcode sc sidebar_position: 1 pagination_prev: null -framework: react +framework: react-native keywords: - react --- diff --git a/package.json b/package.json index 6eacbcc3f..16541118a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ "typecheck": "tsc", "prepare": "husky", "docs:gate": "node scripts/docs-gate/index.cjs", - "docs:gate:setup": "node scripts/setup-vale.cjs" + "docs:gate:setup": "node scripts/setup-vale.cjs", + "verify:frameworks": "node scripts/verify-frameworks.cjs", + "test:frameworks": "node scripts/test-frameworks.cjs" }, "dependencies": { "@docusaurus/core": "^3.4.0", diff --git a/scripts/docs-gate/index.cjs b/scripts/docs-gate/index.cjs index 4507243d2..43720439a 100644 --- a/scripts/docs-gate/index.cjs +++ b/scripts/docs-gate/index.cjs @@ -10,6 +10,8 @@ const { loadSchema, validateFile } = require("./frontmatter.cjs"); const { checkLinks } = require("./links.cjs"); const ROOT = process.cwd(); +// The ratchet base changedDocs() resolved, reused by frontmatterOnly(). +let lastRatchetBase = ""; function sh(cmd) { // Surface git's stderr (do not swallow it) so a failed ratchet lookup is @@ -45,12 +47,55 @@ function changedDocs() { try { out += "\n" + sh("git diff --name-only --diff-filter=ACMR -- docs"); } catch {} try { out += "\n" + sh("git diff --cached --name-only --diff-filter=ACMR -- docs"); } catch {} try { out += "\n" + sh("git ls-files --others --exclude-standard -- docs"); } catch {} + lastRatchetBase = base; // reused by frontmatterOnly() const files = [...new Set(out.split(/\r?\n/).filter(Boolean))]; return files.filter( (f) => /\.(md|mdx)$/i.test(f) && !path.basename(f).startsWith("_") && fs.existsSync(path.join(ROOT, f)) ); } +/** Everything after the frontmatter block, or the whole file if there is none. */ +function bodyOf(text) { + // git show hands back the repo blob with LF while the Windows working copy + // has CRLF; without this every file compares as changed and the skip never fires. + text = text.replace(/\r\n/g, "\n"); + if (!text.startsWith("---")) return text; + const end = text.indexOf("\n---", 3); + return end === -1 ? text : text.slice(end + 4); +} + +/** + * Files whose diff against the ratchet base touches frontmatter only. + * + * The ratchet checks a whole file as soon as a PR touches one line of it, which + * is right for prose someone is actually editing and wrong for a mechanical + * metadata pass: normalizing a `framework:` value across 117 pages dragged in + * 233 pre-existing Vale findings that the change neither caused nor altered. + * Nobody writes prose in frontmatter, so when the body is byte-identical to the + * base there is no prose to review, and the prose checks are skipped for that + * file. Schema and link checks still run on every changed file. + */ +function frontmatterOnly(files, base) { + const out = new Set(); + if (!base) return out; + for (const f of files) { + let before; + try { + before = sh(`git show ${base}:${f}`); + } catch { + continue; // new file - it is all new, check everything + } + let after; + try { + after = fs.readFileSync(path.join(ROOT, f), "utf8"); + } catch { + continue; + } + if (bodyOf(before).trim() === bodyOf(after).trim()) out.add(f); + } + return out; +} + function findVale() { const local = path.join(ROOT, ".vale", "bin", process.platform === "win32" ? "vale.exe" : "vale"); const cand = fs.existsSync(local) ? local : "vale"; @@ -111,17 +156,27 @@ function main() { if (files.length === 0) { console.log("docs-gate: no changed docs — nothing to check."); process.exit(0); } console.log(`docs-gate: checking ${files.length} changed doc(s)…\n`); + // Prose checks only look at files whose body actually changed. + const metaOnly = frontmatterOnly(files, lastRatchetBase); + const proseFiles = files.filter((f) => !metaOnly.has(f)); + if (metaOnly.size) { + console.log( + `docs-gate: ${metaOnly.size} file(s) changed frontmatter only - ` + + `skipping prose checks for them (body identical to base).\n`, + ); + } + const schema = loadSchema(path.join(ROOT, "docs-schema.yml")); let findings = []; for (const f of files) { findings.push(...validateFile(path.join(ROOT, f), schema).map((x) => ({ ...x, file: f }))); findings.push(...checkLinks(path.join(ROOT, f)).map((x) => ({ ...x, file: f }))); } - findings.push(...runCspell(files)); + if (proseFiles.length) findings.push(...runCspell(proseFiles)); const vale = findVale(); if (vale) { - findings.push(...runVale(files, vale)); + if (proseFiles.length) findings.push(...runVale(proseFiles, vale)); } else if (process.env.CI) { // In CI, a missing Vale must fail — otherwise the headline prose-style check // silently no-ops while the job stays green. Locally it's still advisory. diff --git a/scripts/test-frameworks.cjs b/scripts/test-frameworks.cjs new file mode 100644 index 000000000..a2823f742 --- /dev/null +++ b/scripts/test-frameworks.cjs @@ -0,0 +1,129 @@ +#!/usr/bin/env node +"use strict"; +/** + * Membership test for the framework maps. + * + * Five hand-written copies of the framework set were collapsed onto one + * registry (src/constants/frameworks.ts). The danger in that refactor is not a + * crash - it is a map quietly gaining or losing an entry, which changes what the + * site resolves without any error. `linux` missing from FRAMEWORK_MAPPING is + * exactly that bug, and it survived unnoticed for the life of the page. + * + * So this pins the expected membership of every derived map against what the + * hand-written maps contained before the refactor. It loads the real modules + * (transpiled through the TypeScript API, with @site/* stubbed) rather than + * re-deriving anything, so it fails if the registry changes shape. + */ +const fs = require("fs"); +const path = require("path"); +const assert = require("assert"); +const Module = require("module"); +const ts = require("typescript"); + +const ROOT = path.join(__dirname, ".."); + +const STUBS = { + "@site/src/constants/docsPaths": { + withCurrentDocsPath: (p) => p, + CURRENT_DOCS_PATH: "", + }, +}; + +const cache = new Map(); + +function loadTs(relPath) { + if (cache.has(relPath)) return cache.get(relPath); + const full = path.join(ROOT, relPath); + const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { + compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2019 }, + }).outputText; + + const exports = {}; + const localRequire = (id) => { + if (STUBS[id]) return STUBS[id]; + if (id.startsWith("@site/")) return loadTs(id.replace("@site/", "") + ".ts"); + if (id.startsWith(".")) { + const base = path.join(path.dirname(relPath), id); + return loadTs(base.split(path.sep).join("/") + ".ts"); + } + return require(id); + }; + cache.set(relPath, exports); + new Function("exports", "require", "module", "__filename", js)( + exports, localRequire, { exports }, full, + ); + return exports; +} + +const utils = loadTs("src/components/utils/frameworks.ts"); +const registry = loadTs("src/constants/frameworks.ts"); +const unreleased = loadTs("src/constants/unreleasedFrameworks.ts"); + +// Exactly what the hand-written maps held on origin/main, plus the `linux` +// entry added when the gate found it missing. +const EXPECTED_FRAMEWORK_MAPPING = { + ios: "iOS", android: "Android", web: "Web", "react-native": "React Native", + flutter: "Flutter", cordova: "Cordova", capacitor: "Capacitor", + kmp: "Kotlin Multiplatform", "net-ios": ".NET iOS", "net-android": ".NET Android", + titanium: "Titanium", linux: "Linux", +}; +const EXPECTED_QUERY_TO_PATH = { + ios: "ios", android: "android", web: "web", "react-native": "react-native", + flutter: "flutter", cordova: "cordova", capacitor: "capacitor", kmp: "kmp", + "net-ios": "net/ios", "net-android": "net/android", +}; +const EXPECTED_ALIASES = { react: "react-native", netios: "net-ios", netandroid: "net-android" }; + +let passed = 0; +const check = (label, fn) => { fn(); passed += 1; console.log(` ok ${label}`); }; +const sorted = (o) => Object.fromEntries(Object.entries(o).sort()); + +console.log("\nframework registry\n"); + +check("FRAMEWORK_MAPPING membership unchanged by the refactor", () => { + assert.deepStrictEqual(sorted(utils.FRAMEWORK_MAPPING), sorted(EXPECTED_FRAMEWORK_MAPPING)); +}); + +check("QUERY_FRAMEWORK_TO_PATH membership unchanged", () => { + assert.deepStrictEqual(sorted(utils.QUERY_FRAMEWORK_TO_PATH), sorted(EXPECTED_QUERY_TO_PATH)); +}); + +check("homepage aliases still resolve", () => { + for (const [raw, slug] of Object.entries(EXPECTED_ALIASES)) { + assert.strictEqual(utils.normalizeFrameworkQuery(raw), slug, `${raw} -> ${slug}`); + } +}); + +check("hosted is not an /sdks/ route", () => { + assert.ok(!("hosted" in utils.FRAMEWORK_MAPPING)); + assert.ok(!("hosted" in utils.QUERY_FRAMEWORK_TO_PATH)); +}); + +check("linux resolves a framework (the bug the gate found)", () => { + assert.strictEqual(utils.parseSdksRoute("/sdks/linux/barcode-capture/intro").framework, "Linux"); +}); + +check(".NET keeps its two-segment route", () => { + assert.strictEqual(utils.parseSdksRoute("/sdks/net/ios/sparkscan/intro").framework, ".NET iOS"); + assert.strictEqual(utils.QUERY_FRAMEWORK_TO_PATH["net-ios"], "net/ios"); +}); + +check("frameworks without Agent Skills are still hidden", () => { + for (const raw of ["xamarin", "xamarinios", "xamarinandroid", "xamarinforms", "titanium", "linux", "net"]) { + assert.strictEqual(utils.frameworkHasAgentSkills(raw), false, raw); + } + for (const raw of ["ios", "netios", "netandroid", "kmp"]) { + assert.strictEqual(utils.frameworkHasAgentSkills(raw), true, raw); + } +}); + +check("UNRELEASED_FRAMEWORK_SLUGS still derives to kmp", () => { + assert.deepStrictEqual(unreleased.UNRELEASED_FRAMEWORK_SLUGS, ["kmp"]); +}); + +check("every registry slug is unique", () => { + const slugs = registry.FRAMEWORK_SLUGS; + assert.strictEqual(new Set(slugs).size, slugs.length); +}); + +console.log(`\n${passed} passed\n`); diff --git a/scripts/verify-frameworks.cjs b/scripts/verify-frameworks.cjs new file mode 100644 index 000000000..2bf4bac32 --- /dev/null +++ b/scripts/verify-frameworks.cjs @@ -0,0 +1,179 @@ +#!/usr/bin/env node +"use strict"; +/** + * Framework identifier gate. + * + * docs-schema.yml is the single source of truth for framework slugs. The docs + * gate validates frontmatter against it, but only for files a PR changed - which + * is why 37 pages carrying `kmp` sat outside the enum for months without anyone + * seeing a single error. A ratchet cannot report what nobody touched. + * + * This runs over the whole corpus, and over the code maps as well, because the + * field and the maps that consume it drift independently: + * + * 1. CONTENT no page may set `framework` / `frameworks` outside the enum. + * 2. DRIFT no code map may key off a framework the enum does not define. + * This is the error that lets two maps disagree silently. + * 3. COVERAGE enum slugs absent from a code map are reported, so a gap is a + * known gap rather than a surprise at runtime. + * + * Usage: node scripts/verify-frameworks.cjs + */ + +const fs = require("fs"); +const path = require("path"); +const yaml = require("js-yaml"); + +const ROOT = path.join(__dirname, ".."); +const DOCS = path.join(ROOT, "docs"); + +// The registry is now the only hand-written list of framework slugs in the +// code; every map derives from it (src/constants/frameworks.ts). So this gate +// checks the registry against the schema enum rather than each map: if those +// two agree, every derived map agrees by construction. +const REGISTRY_FILE = "src/constants/frameworks.ts"; + +function enumSlugs() { + const schema = yaml.load( + fs.readFileSync(path.join(ROOT, "docs-schema.yml"), "utf8"), + ); + const singular = schema.properties.framework && schema.properties.framework.enum; + const plural = + schema.properties.frameworks && + schema.properties.frameworks.items && + schema.properties.frameworks.items.enum; + if (!singular) throw new Error("docs-schema.yml defines no `framework` enum"); + if (!plural) throw new Error("docs-schema.yml defines no `frameworks` enum"); + const a = JSON.stringify([...singular].sort()); + const b = JSON.stringify([...plural].sort()); + if (a !== b) { + throw new Error( + "`framework` and `frameworks` enums differ in docs-schema.yml. " + + "One page states one platform, another states several - the vocabulary " + + "must be the same set.", + ); + } + return new Set(singular); +} + +function walk(dir, out = []) { + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, e.name); + if (e.isDirectory()) walk(full, out); + else if (/\.mdx?$/i.test(e.name)) out.push(full); + } + return out; +} + +/** Values a page declares, from the frontmatter block only. */ +function declaredFrameworks(file) { + const text = fs.readFileSync(file, "utf8"); + if (!text.startsWith("---")) return []; + const end = text.indexOf("\n---", 3); + if (end === -1) return []; + const fm = text.slice(0, end); + const found = []; + + const singular = /^framework:[ \t]*(\S+)[ \t]*$/m.exec(fm); + if (singular) found.push({ field: "framework", value: singular[1] }); + + const plural = /^frameworks:[ \t]*\n((?:[ \t]*-[ \t]*\S+[ \t]*\n)+)/m.exec(fm); + if (plural) { + for (const line of plural[1].split("\n")) { + const m = /^[ \t]*-[ \t]*(\S+)/.exec(line); + if (m) found.push({ field: "frameworks", value: m[1] }); + } + } + return found; +} + +/** The `slug:` values in the FRAMEWORKS registry, or null if its shape changed. */ +function registryFrameworkSlugs() { + const src = fs.readFileSync(path.join(ROOT, REGISTRY_FILE), "utf8"); + const start = src.indexOf("export const FRAMEWORKS"); + if (start === -1) return null; + // Anchor on the assignment: the type annotation `FrameworkDef[]` puts an + // empty pair of brackets first, and reading those yields zero entries - a + // gate that checks nothing while reporting success. + const eq = src.indexOf("=", start); + if (eq === -1) return null; + const open = src.indexOf("[", eq); + if (open === -1) return null; + let depth = 0; + let end = -1; + for (let i = open; i < src.length; i += 1) { + if (src[i] === "[") depth += 1; + else if (src[i] === "]") { + depth -= 1; + if (depth === 0) { end = i; break; } + } + } + if (end === -1) return null; + const slugs = []; + const re = /slug:\s*"([^"]+)"/g; + let m; + while ((m = re.exec(src.slice(open + 1, end)))) slugs.push(m[1]); + return slugs; +} + +function main() { + const allowed = enumSlugs(); + const errors = []; + + // 1. CONTENT + const files = walk(DOCS); + let pagesWithField = 0; + for (const file of files) { + const decls = declaredFrameworks(file); + if (decls.length) pagesWithField += 1; + for (const { field, value } of decls) { + if (!allowed.has(value)) { + errors.push( + `${path.relative(ROOT, file).split(path.sep).join("/")}: ${field} "${value}" is not in the enum`, + ); + } + } + } + + // 2. DRIFT + 3. COVERAGE, checked against the registry the maps derive from. + const registrySlugs = registryFrameworkSlugs(); + if (!registrySlugs) { + errors.push( + `${REGISTRY_FILE}: could not read the FRAMEWORKS registry - its shape changed, ` + + `so this gate is no longer checking it`, + ); + } else if (registrySlugs.length === 0) { + errors.push(`${REGISTRY_FILE}: FRAMEWORKS parsed to zero entries`); + } else { + for (const slug of registrySlugs) { + if (!allowed.has(slug)) { + errors.push(`${REGISTRY_FILE}: registry defines "${slug}", docs-schema.yml does not`); + } + } + for (const slug of allowed) { + if (!registrySlugs.includes(slug)) { + errors.push( + `docs-schema.yml allows "${slug}", the registry does not define it - a page ` + + `may use it and every component will silently resolve nothing`, + ); + } + } + } + + console.log( + `\nframework gate: ${files.length} docs scanned, ${pagesWithField} declare a framework`, + ); + console.log(`enum (${allowed.size}): ${[...allowed].join(", ")}\n`); + + if (errors.length) { + console.error(`FAIL: ${errors.length} framework identifier problem(s).\n`); + for (const e of errors.slice(0, 40)) console.error(` ${e}`); + if (errors.length > 40) console.error(` ... and ${errors.length - 40} more`); + console.error(""); + process.exit(1); + } + + console.log("OK: every framework identifier in docs and code is in the enum.\n"); +} + +main(); diff --git a/src/components/SkillsCallout/index.tsx b/src/components/SkillsCallout/index.tsx index 264e263e7..92f600e0b 100644 --- a/src/components/SkillsCallout/index.tsx +++ b/src/components/SkillsCallout/index.tsx @@ -14,6 +14,7 @@ import { FRAMEWORK_CHANGE_EVENT, } from '../utils/frameworks'; import { withCurrentDocsPath } from '@site/src/constants/docsPaths'; +import { AGENT_SKILL_FRAMEWORKS } from '@site/src/constants/frameworks'; import { capturePostHogEvent } from './analytics'; import InstallCommand from './InstallCommand'; import styles from './styles.module.css'; @@ -43,32 +44,18 @@ interface ProductEntry { name: string; } -const FRAMEWORK_URL_PATH: Record = { - iOS: 'ios', - Android: 'android', - Web: 'web', - Cordova: 'cordova', - Capacitor: 'capacitor', - Flutter: 'flutter', - 'Kotlin Multiplatform': 'kmp', - 'React Native': 'react-native', - '.NET iOS': 'net/ios', - '.NET Android': 'net/android', -}; +// Both derived from the framework registry (src/constants/frameworks.ts). +// These used to be two hand-written maps keyed by display name, a third and +// fourth copy of the same set - which is how `linux` went missing from one map +// and nowhere else without anyone noticing. +const FRAMEWORK_URL_PATH: Record = Object.fromEntries( + AGENT_SKILL_FRAMEWORKS.map((f) => [f.display, f.routeSegment as string]), +); // Analytics-friendly slug for the framework, used as the data-skills-callout-framework attribute. -const FRAMEWORK_SLUG: Record = { - iOS: 'ios', - Android: 'android', - Web: 'web', - Cordova: 'cordova', - Capacitor: 'capacitor', - Flutter: 'flutter', - 'Kotlin Multiplatform': 'kmp', - 'React Native': 'react-native', - '.NET iOS': 'net-ios', - '.NET Android': 'net-android', -}; +const FRAMEWORK_SLUG: Record = Object.fromEntries( + AGENT_SKILL_FRAMEWORKS.map((f) => [f.display, f.slug]), +); // Resolves the framework for the shared callout, preferring the framework in // the current path (so "More info" keeps the reader on the framework they are diff --git a/src/components/utils/frameworks.ts b/src/components/utils/frameworks.ts index 244d236cf..447688e92 100644 --- a/src/components/utils/frameworks.ts +++ b/src/components/utils/frameworks.ts @@ -1,23 +1,20 @@ import { withCurrentDocsPath } from '@site/src/constants/docsPaths'; +import { + FRAMEWORKS, + ROUTED_FRAMEWORKS, + AGENT_SKILL_FRAMEWORKS, +} from '@site/src/constants/frameworks'; // localStorage key the homepage framework selector writes and the shared // Agent Skills banner reads back. Both sides must use this constant so the // contract can't drift (a rename would otherwise silently fall back to iOS). export const FRAMEWORK_STORAGE_KEY = 'framework'; -export const FRAMEWORK_MAPPING: { [urlSlug: string]: string } = { - 'ios': 'iOS', - 'android': 'Android', - 'cordova': 'Cordova', - 'react-native': 'React Native', - 'flutter': 'Flutter', - 'kmp': 'Kotlin Multiplatform', - 'capacitor': 'Capacitor', - 'titanium': 'Titanium', - 'web': 'Web', - 'net-ios': '.NET iOS', - 'net-android': '.NET Android', -}; +// Derived from the framework registry - see src/constants/frameworks.ts. +// `hosted` is absent because it is not an /sdks/ route. +export const FRAMEWORK_MAPPING: { [urlSlug: string]: string } = Object.fromEntries( + ROUTED_FRAMEWORKS.map((f) => [f.slug, f.display]), +); export const URL_PRODUCT_MAPPING: { [urlSlug: string]: string } = { 'label-capture': 'smart-label-capture', @@ -68,27 +65,21 @@ export function parseSdksRoute(pathname: string): SdksRouteInfo { } // Maps the ?framework= query slug used on the homepage to an agent-skills URL path. -export const QUERY_FRAMEWORK_TO_PATH: Record = { - ios: 'ios', - android: 'android', - web: 'web', - cordova: 'cordova', - capacitor: 'capacitor', - flutter: 'flutter', - kmp: 'kmp', - 'react-native': 'react-native', - 'net-ios': 'net/ios', - 'net-android': 'net/android', -}; +// Maps a framework slug to its agent-skills URL path. Only frameworks that +// actually have an Agent Skills page appear here - derived from the registry. +export const QUERY_FRAMEWORK_TO_PATH: Record = Object.fromEntries( + AGENT_SKILL_FRAMEWORKS.map((f) => [f.slug, f.routeSegment as string]), +); // The homepage framework selector uses its own identifiers (see frameworkCardsArr) // that differ from the QUERY_FRAMEWORK_TO_PATH keys. Map them so // ?framework=react / netIos / netAndroid resolve correctly. -const HOMEPAGE_FRAMEWORK_ALIASES: Record = { - react: 'react-native', - netios: 'net-ios', - netandroid: 'net-android', -}; +// The homepage framework selector uses its own identifiers (see +// frameworkCardsArr) that differ from the canonical slugs. Declared as +// `aliases` on each registry entry so a new spelling is added in one place. +const HOMEPAGE_FRAMEWORK_ALIASES: Record = Object.fromEntries( + FRAMEWORKS.flatMap((f) => (f.aliases || []).map((a) => [a, f.slug])), +); // Normalizes a raw ?framework= value to a QUERY_FRAMEWORK_TO_PATH key, or '' // when it maps to no agent-skills page. @@ -120,19 +111,25 @@ export function readSelectedFrameworkRaw(): string { } // Homepage selector slugs the shared product-picker banner hides for, because -// there is no Agent Skill to route to. Xamarin (and its variants), Titanium and -// Linux have no skills at all. Bare ".NET" is also hidden: it has no general -// skill — the reader must pick a platform — so the banner stays hidden until -// ".NET iOS" or ".NET Android" is selected (those slugs, netios/netandroid, are -// intentionally NOT in this set, so the banner returns for them). -const FRAMEWORKS_WITHOUT_AGENT_SKILLS = new Set([ +// there is no Agent Skill to route to. +// +// Two sources, deliberately: the frameworks we document but have no skill for +// come from the registry (so adding a skill flips one flag, not two lists), and +// the selector-only identifiers below are spellings that are not canonical +// frameworks at all - Xamarin is removed from 8.x and has no docs tree, and bare +// ".NET" has no general skill because the reader must pick a platform first +// (netios/netandroid are intentionally absent, so the banner returns for them). +const SELECTOR_ONLY_WITHOUT_AGENT_SKILLS = [ 'xamarin', 'xamarinios', 'xamarinandroid', 'xamarinforms', - 'titanium', - 'linux', 'net', +]; + +const FRAMEWORKS_WITHOUT_AGENT_SKILLS = new Set([ + ...FRAMEWORKS.filter((f) => !f.agentSkills).map((f) => f.slug), + ...SELECTOR_ONLY_WITHOUT_AGENT_SKILLS, ]); // True unless the selected framework has no Agent Skills at all. Unknown/empty diff --git a/src/constants/frameworks.ts b/src/constants/frameworks.ts new file mode 100644 index 000000000..dbafba6b3 --- /dev/null +++ b/src/constants/frameworks.ts @@ -0,0 +1,102 @@ +/** + * THE framework registry. Every framework identifier in this repo comes from + * here. + * + * Before this file the same set was written out five times - FRAMEWORK_MAPPING, + * QUERY_FRAMEWORK_TO_PATH and HOMEPAGE_FRAMEWORK_ALIASES in + * components/utils/frameworks.ts, plus FRAMEWORK_URL_PATH and FRAMEWORK_SLUG in + * SkillsCallout - each keyed slightly differently. Nothing tied them together, + * so they could disagree without anyone noticing, and they did: `linux` was + * missing from FRAMEWORK_MAPPING, so every /sdks/linux/ page resolved to no + * framework at all. Silently. For as long as the page had existed. + * + * `slug` must match the `framework` / `frameworks` enum in docs-schema.yml. + * `yarn verify:frameworks` fails the build if the two ever diverge, so this + * registry and the frontmatter vocabulary cannot drift apart again. + * + * Kept free of imports so docusaurus.config.ts can read it: the config is loaded + * by Node, where webpack's `@generated` alias does not resolve. + */ +export interface FrameworkDef { + /** Canonical slug: the docs-schema.yml enum value, and the /sdks// segment. */ + slug: string; + /** Human-readable name. Used as a map key by SkillsCallout, so it is load-bearing. */ + display: string; + /** + * Path under /sdks/. Usually the slug; .NET splits across two segments. + * `null` means the framework is not an /sdks/ route at all. + */ + routeSegment: string | null; + /** An Agent Skills page exists at /sdks//agent-skills. */ + agentSkills: boolean; + /** Documented only in the in-development docs version (no versioned snapshot). */ + unreleased?: boolean; + /** + * Other spellings that must resolve to this framework. The homepage selector + * uses its own identifiers, and `?framework=` carries them into the URL. + */ + aliases?: string[]; +} + +export const FRAMEWORKS: FrameworkDef[] = [ + { slug: "ios", display: "iOS", routeSegment: "ios", agentSkills: true }, + { slug: "android", display: "Android", routeSegment: "android", agentSkills: true }, + { slug: "web", display: "Web", routeSegment: "web", agentSkills: true }, + { + slug: "react-native", + display: "React Native", + routeSegment: "react-native", + agentSkills: true, + aliases: ["react"], + }, + { slug: "flutter", display: "Flutter", routeSegment: "flutter", agentSkills: true }, + { slug: "cordova", display: "Cordova", routeSegment: "cordova", agentSkills: true }, + { slug: "capacitor", display: "Capacitor", routeSegment: "capacitor", agentSkills: true }, + { + slug: "kmp", + display: "Kotlin Multiplatform", + routeSegment: "kmp", + agentSkills: true, + unreleased: true, + }, + { + slug: "net-ios", + display: ".NET iOS", + routeSegment: "net/ios", + agentSkills: true, + aliases: ["netios"], + }, + { + slug: "net-android", + display: ".NET Android", + routeSegment: "net/android", + agentSkills: true, + aliases: ["netandroid"], + }, + // Documented, but with no Agent Skills page to route a reader to. + { slug: "titanium", display: "Titanium", routeSegment: "titanium", agentSkills: false }, + { slug: "linux", display: "Linux", routeSegment: "linux", agentSkills: false }, + // Not an /sdks/ route: the hosted products live under /hosted//. + { slug: "hosted", display: "Hosted", routeSegment: null, agentSkills: false }, +]; + +/** Canonical slugs, in registry order. */ +export const FRAMEWORK_SLUGS: string[] = FRAMEWORKS.map((f) => f.slug); + +export const FRAMEWORK_BY_SLUG: Record = Object.fromEntries( + FRAMEWORKS.map((f) => [f.slug, f]), +); + +export const FRAMEWORK_BY_DISPLAY: Record = Object.fromEntries( + FRAMEWORKS.map((f) => [f.display, f]), +); + +/** Frameworks that are an /sdks/ route (everything except `hosted` today). */ +export const ROUTED_FRAMEWORKS: FrameworkDef[] = FRAMEWORKS.filter( + (f) => f.routeSegment !== null, +); + +/** Frameworks with an Agent Skills page. */ +export const AGENT_SKILL_FRAMEWORKS: FrameworkDef[] = FRAMEWORKS.filter( + (f) => f.agentSkills, +); diff --git a/src/constants/unreleasedFrameworks.ts b/src/constants/unreleasedFrameworks.ts index ab0c4b92a..0e04457d4 100644 --- a/src/constants/unreleasedFrameworks.ts +++ b/src/constants/unreleasedFrameworks.ts @@ -14,7 +14,11 @@ * Kept free of imports so docusaurus.config.ts can read it too: the config is * loaded by Node, where webpack's `@generated` alias does not resolve. */ -export const UNRELEASED_FRAMEWORK_SLUGS = ["kmp"]; +import { FRAMEWORKS } from "./frameworks"; + +export const UNRELEASED_FRAMEWORK_SLUGS = FRAMEWORKS.filter( + (f) => f.unreleased, +).map((f) => f.slug); /** True when `slug` is documented only in the current docs version. */ export function isUnreleasedFramework(