Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .claude/docs/QUICK_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,35 @@ for try await record in database.records(matching: query) {

---

## Known Endpoint Discrepancies

### APNs token endpoints route under `/device/`, not `/database/` (#382)

Apple's archived CloudKit Web Services REST reference
([CreateTokens](https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/CloudKitWebServicesReference/CreateTokens.html) /
[RegisterTokens](https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/CloudKitWebServicesReference/RegisterTokens.html))
documents the APNs token endpoints under the `/database/` path. **The live
service does not route them there** — only `OPTIONS` is allowed and a `POST`
returns `405 Method Not Allowed` (`Allow: OPTIONS`).

| | Path |
|--|------|
| **Documented (archived REST reference)** | `POST /database/{version}/{container}/{environment}/{database}/tokens/{create,register}` |
| **What actually works (matches CloudKit JS)** | `POST /device/{version}/{container}/{environment}/tokens/{create,register}` |

CloudKit JS calls `setApiModuleName("device")` for token requests, producing
the `/device/...` path, which returns `200` with the expected
`{ apnsEnvironment, apnsToken, webcourierURL }` body. Note the `/device/` path
is **container-scoped** — there is **no `{database}` segment**, unlike most
other endpoints.

MistKit deliberately diverges from Apple's published REST docs here:
`openapi.yaml` already documents the working `/device/...` paths and rationale.
This note exists so the divergence isn't re-discovered. See issue
[#382](https://github.com/brightdigit/MistKit/issues/382) for the canonical record.

---

## Development Checklist

### Before implementing an endpoint:
Expand Down
1 change: 1 addition & 0 deletions .codefactor.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
exclude:
- "Scripts/mermaid-to-pptx.py"
- "Examples/MistDemo/Sources/MistDemoKit/Resources/js/**"
- "Sources/MistKitOpenAPI/**"
4 changes: 1 addition & 3 deletions .github/workflows/MistDemo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ jobs:
type: ${{ matrix.type }}
wasmtime-version: "40.0.2"
wasm-swift-flags: >-
-Xswiftc -disable-batch-mode
-Xcc -D_WASI_EMULATED_SIGNAL
-Xcc -D_WASI_EMULATED_MMAN
-Xlinker -lwasi-emulated-signal
Expand Down Expand Up @@ -187,7 +188,6 @@ jobs:
swap-storage: true
- uses: brightdigit/swift-build@v1
with:
scheme: ${{ env.PACKAGE_NAME }}
type: android
android-swift-version: ${{ matrix.swift.version }}
android-api-level: ${{ matrix.android-api-level }}
Expand Down Expand Up @@ -225,7 +225,6 @@ jobs:
id: build
uses: brightdigit/swift-build@v1
with:
scheme: ${{ env.PACKAGE_NAME }}-Package
type: ${{ matrix.type }}
xcode: ${{ matrix.xcode }}
deviceName: ${{ matrix.deviceName }}
Expand Down Expand Up @@ -298,7 +297,6 @@ jobs:
id: build
uses: brightdigit/swift-build@v1
with:
scheme: ${{ env.PACKAGE_NAME }}-Package
type: ${{ matrix.type }}
xcode: ${{ matrix.xcode }}
deviceName: ${{ matrix.deviceName }}
Expand Down
14 changes: 12 additions & 2 deletions .github/workflows/MistKit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,18 @@ jobs:
build: 6.2-RELEASE
- version: swift-6.3-release
build: 6.3-RELEASE
exclude:
# windows-2025's default MSVC 14.51 STL requires Clang 20+, but Swift 6.1/6.2
# ship Clang 19 (STL1000 error via swift-crypto's CCryptoBoringSSL). Those run on
# windows-2022 (older 14.4x toolset); only Swift 6.3 (Clang 20) builds on windows-2025.
- runs-on: windows-2025
swift:
version: swift-6.1-release
build: 6.1-RELEASE
- runs-on: windows-2025
swift:
version: swift-6.2-release
build: 6.2-RELEASE
steps:
- uses: actions/checkout@v6
- uses: brightdigit/swift-build@v1
Expand Down Expand Up @@ -215,7 +227,6 @@ jobs:
id: build
uses: brightdigit/swift-build@v1
with:
scheme: ${{ env.PACKAGE_NAME }}-Package
type: ${{ matrix.type }}
xcode: ${{ matrix.xcode }}
deviceName: ${{ matrix.deviceName }}
Expand Down Expand Up @@ -292,7 +303,6 @@ jobs:
id: build
uses: brightdigit/swift-build@v1
with:
scheme: ${{ env.PACKAGE_NAME }}-Package
type: ${{ matrix.type }}
xcode: ${{ matrix.xcode }}
deviceName: ${{ matrix.deviceName }}
Expand Down
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ Object/array-shaped values (`REFERENCE`, `ASSET`, `LOCATION`, `LIST`) and `STRIN

**Response type recovery (issue #375):** The generated `value` `oneOf` is *undiscriminated* — the decoder tries cases first-match-wins (`String → Int64 → Double → Bytes → Date`), so a whole-millisecond `TIMESTAMP` decodes as `Int64Value` and a base64 `BYTES` string decodes as `StringValue`. The response conversion therefore honors an explicit `type` *over* the decoded case (`makeTypedScalar` in `FieldValue+Components+Scalar.swift`). For the genuinely-ambiguous scalars whose correct interpretation differs from inference it produces the typed value directly: `TIMESTAMP`/`DOUBLE` from any numeric case, `BYTES` from any string case. `INT64`/`STRING` validate the category then defer to inference (which already yields them, and for `INT64` avoids truncating a fractional number). When `type` is absent it falls back to first-match-wins inference (`makeInferredScalar`), which is lossy for the ambiguous scalars (BYTES→`.string`, whole-number TIMESTAMP→`.int64`).

When a scalar `type` *contradicts* the value's category — a numeric type (`TIMESTAMP`/`DOUBLE`/`INT64`) over a non-number, or a string type (`STRING`/`BYTES`) over a non-string — the response is internally inconsistent and the conversion **throws** `ConversionError.typeValueMismatch` (via `requireNumeric`/`requireString`) rather than coercing to the value's shape. This matches the codebase's existing fail-loud `unmappableFieldValue` philosophy. The strict check is scoped to **scalar** type tags; complex/list declared types (`REFERENCE`/`ASSET`/`LOCATION`/`LIST`) are left to the value's self-describing structure and are not validated against the tag.
When a scalar `type` *contradicts* the value's category — a numeric type (`TIMESTAMP`/`DOUBLE`/`INT64`) over a non-number, or a string type (`STRING`/`BYTES`) over a non-string — the response is internally inconsistent and the conversion **throws** `ConversionError.typeValueMismatch` (via `requireNumeric`/`requireString`) rather than coercing to the value's shape. This matches the codebase's existing fail-loud `unmappableFieldValue` philosophy.

**Complex/list contradiction validation (issue #376):** the same fail-loud check now extends to the complex/list response `type` tags. A declared `REFERENCE`/`ASSET`/`ASSETID`/`LOCATION`/`LIST` whose decoded value isn't the matching `oneOf` case (`ReferenceValue`/`AssetValue`/`LocationValue`/`ListValue`) **throws** `ConversionError.typeValueMismatch` instead of silently coercing to the value's shape (`makeTypedComplex` in `FieldValue+Components.swift`, gated by the `ExpectedComplexValue` mapping). `ASSETID` shares `AssetValue` with `ASSET`; the `LIST` tag is validated only at the container level (element types stay lenient). Untagged responses are unaffected — they still resolve purely from the value's self-describing structure via `makeComplexFieldValue`, so well-formed responses never start failing.

**Why Separate Request/Response Types?**
- CloudKit API has asymmetric behavior: requests tag type only when ambiguous, responses may always include it
Expand Down
10 changes: 0 additions & 10 deletions Examples/BushelCloud/.github/actions/cloudkit-sync/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,6 @@ inputs:
description: 'MistKit ref to check out when falling back to a fresh build'
required: false
default: 'v1.0.0-beta.2'
configkeykit-branch:
description: 'ConfigKeyKit ref to check out when falling back to a fresh build'
required: false
default: 'main'

runs:
using: "composite"
Expand All @@ -159,12 +155,6 @@ runs:
with:
branch: ${{ inputs.mistkit-branch }}

- name: Setup ConfigKeyKit
if: steps.download-binary.outcome != 'success'
uses: brightdigit/ConfigKeyKit/.github/actions/setup-configkeykit@main
with:
branch: ${{ inputs.configkeykit-branch }}

- name: Build binary (fallback if artifact unavailable)
if: steps.download-binary.outcome != 'success'
shell: bash
Expand Down
18 changes: 0 additions & 18 deletions Examples/BushelCloud/.github/workflows/BushelCloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ concurrency:
env:
PACKAGE_NAME: BushelCloud
MISTKIT_BRANCH: v1.0.0-beta.2
CONFIGKEYKIT_BRANCH: main

jobs:
configure:
Expand Down Expand Up @@ -90,11 +89,6 @@ jobs:
with:
branch: ${{ env.MISTKIT_BRANCH }}

- name: Setup ConfigKeyKit
uses: brightdigit/ConfigKeyKit/.github/actions/setup-configkeykit@main
with:
branch: ${{ env.CONFIGKEYKIT_BRANCH }}

- uses: brightdigit/swift-build@v1
id: build
with:
Expand Down Expand Up @@ -184,16 +178,10 @@ jobs:
with:
branch: ${{ env.MISTKIT_BRANCH }}

- name: Setup ConfigKeyKit
uses: brightdigit/ConfigKeyKit/.github/actions/setup-configkeykit@main
with:
branch: ${{ env.CONFIGKEYKIT_BRANCH }}

- name: Build and Test
id: build
uses: brightdigit/swift-build@v1
with:
scheme: ${{ env.PACKAGE_NAME }}-Package
type: ${{ matrix.type }}
xcode: ${{ matrix.xcode }}
deviceName: ${{ matrix.deviceName }}
Expand Down Expand Up @@ -254,16 +242,10 @@ jobs:
with:
branch: ${{ env.MISTKIT_BRANCH }}

- name: Setup ConfigKeyKit
uses: brightdigit/ConfigKeyKit/.github/actions/setup-configkeykit@main
with:
branch: ${{ env.CONFIGKEYKIT_BRANCH }}

- name: Build and Test
id: build
uses: brightdigit/swift-build@v1
with:
scheme: ${{ env.PACKAGE_NAME }}-Package
type: ${{ matrix.type }}
xcode: ${{ matrix.xcode }}
deviceName: ${{ matrix.deviceName }}
Expand Down
5 changes: 0 additions & 5 deletions Examples/BushelCloud/.github/workflows/bushel-cloud-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,6 @@ jobs:
with:
branch: v1.0.0-beta.2

- name: Setup ConfigKeyKit
uses: brightdigit/ConfigKeyKit/.github/actions/setup-configkeykit@main
with:
branch: main

- name: Verify Swift version
run: |
swift --version
Expand Down
5 changes: 0 additions & 5 deletions Examples/BushelCloud/.github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,6 @@ jobs:
with:
branch: v1.0.0-beta.2

- name: Setup ConfigKeyKit
uses: brightdigit/ConfigKeyKit/.github/actions/setup-configkeykit@main
with:
branch: main

# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- run: |
Expand Down
4 changes: 2 additions & 2 deletions Examples/BushelCloud/.gitrepo
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[subrepo]
remote = git@github.com:brightdigit/BushelCloud.git
branch = mistkit
commit = 1ab45e4fe8420bc52c3c27f3740b770e49e36b9b
parent = fe0a6ae37cc09970570aaa5f1eacef948a81b177
commit = b0c3985fbc0f1d25fc5ebf0c48ffc123549d0f28
parent = 5edf7036eabfea004b8f74b8caab58884401662c
method = merge
cmdver = 0.4.9
11 changes: 10 additions & 1 deletion Examples/BushelCloud/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Examples/BushelCloud/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ let package = Package(
],
dependencies: [
.package(name: "MistKit", path: "../.."),
.package(name: "ConfigKeyKit", path: "../../Packages/ConfigKeyKit"),
.package(url: "https://github.com/brightdigit/ConfigKeyKit.git", from: "1.0.0-beta.2"),
.package(url: "https://github.com/brightdigit/BushelKit.git", from: "3.0.0-alpha.2"),
.package(url: "https://github.com/brightdigit/IPSWDownloads.git", from: "1.0.0"),
.package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.6.0"),
Expand Down
1 change: 1 addition & 0 deletions Examples/BushelCloud/Scripts/lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ if [ -z "$FORMAT_ONLY" ]; then
fi

$PACKAGE_DIR/Scripts/header.sh -d $PACKAGE_DIR/Sources -c "Leo Dion" -o "BrightDigit" -p "BushelCloud"
$PACKAGE_DIR/Scripts/header.sh -d $PACKAGE_DIR/Tests -c "Leo Dion" -o "BrightDigit" -p "BushelCloud"

# Generated files now automatically include ignore directives via OpenAPI generator configuration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// BushelCloud
//
// Created by Leo Dion.
// Copyright © 2025 BrightDigit.
// Copyright © 2026 BrightDigit.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// BushelCloud
//
// Created by Leo Dion.
// Copyright © 2025 BrightDigit.
// Copyright © 2026 BrightDigit.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,29 @@
// ConfigurationLoaderTests.swift
// BushelCloud
//
// Comprehensive tests for ConfigurationLoader
// Created by Leo Dion.
// Copyright © 2026 BrightDigit.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//

internal import Configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,28 @@
// BushelCloud
//
// Created by Leo Dion.
// Copyright © 2025 BrightDigit.
// Copyright © 2026 BrightDigit.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//

internal import Foundation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// BushelCloud
//
// Created by Leo Dion.
// Copyright © 2025 BrightDigit.
// Copyright © 2026 BrightDigit.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// BushelCloud
//
// Created by Leo Dion.
// Copyright © 2025 BrightDigit.
// Copyright © 2026 BrightDigit.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// BushelCloud
//
// Created by Leo Dion.
// Copyright © 2025 BrightDigit.
// Copyright © 2026 BrightDigit.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// BushelCloud
//
// Created by Leo Dion.
// Copyright © 2025 BrightDigit.
// Copyright © 2026 BrightDigit.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// BushelCloud
//
// Created by Leo Dion.
// Copyright © 2025 BrightDigit.
// Copyright © 2026 BrightDigit.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
Expand Down
Loading