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
101 changes: 48 additions & 53 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,64 +1,59 @@
version: 2

# The app, Worker and release tooling ship together. Batch routine updates so
# one merge runs the complete CI matrix and triggers one release.
multi-ecosystem-groups:
routine-dependencies:
schedule:
interval: weekly

# Major upgrades are planned manually, outside the routine release batch.
# Security updates remain independent of the weekly version-update schedule.
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
patterns: ["*"]
multi-ecosystem-group: routine-dependencies
ignore:
- dependency-name: "*"
update-types: [version-update:semver-major]

- package-ecosystem: bundler
directory: /
schedule:
interval: weekly
patterns: ["*"]
multi-ecosystem-group: routine-dependencies
ignore:
- dependency-name: "*"
update-types: [version-update:semver-major]

# The Worker. Better Auth and Wrangler both move fast, and CI installs
# exactly what pnpm-lock.yaml says (`--frozen-lockfile`), so nothing notices a
# version going stale until something breaks. The `npm` ecosystem is also
# Dependabot's name for pnpm; it reads and updates pnpm-lock.yaml.
# Dependabot calls pnpm's ecosystem `npm`. Include the service worker too;
# its build dependencies were previously outside Dependabot's coverage.
- package-ecosystem: npm
directory: /server
schedule:
interval: weekly
groups:
# Packages released in lockstep, each set one pull request so they never
# disagree: drizzle-kit 1.x generates for exactly its drizzle-orm, and
# the drizzle adapter is part of Better Auth's release, as is its CLI.
# Listed before `toolchain`, which would otherwise claim the dev halves.
drizzle:
patterns:
- drizzle-orm
- drizzle-kit
better-auth:
patterns:
- better-auth
- "@better-auth/*"
- auth
# One pull request for the toolchain, which moves constantly and whose
# updates are only ever interesting together: biome, vitest and the test
# pool have to agree with each other, and wrangler decides what the
# generated Env types look like.
toolchain:
dependency-type: development
# Every other runtime dependency is deliberately ungrouped and arrives on
# its own. Hono owns routing and Zod owns validation, and each is worth
# reading a changelog for on its own rather than in a batch of five --
# which is also why the two lockstep groups above are kept apart.
directories:
- /server
- /service_worker
patterns: ["*"]
multi-ecosystem-group: routine-dependencies
ignore:
- dependency-name: "*"
update-types: [version-update:semver-major]
# Keep the database toolchain on the reviewed rc.4 until its next
# prerelease has been assessed explicitly. Stable 1.0 remains eligible.
- dependency-name: drizzle-orm
versions: [">=1.0.0-rc.5 <1.0.0"]
- dependency-name: drizzle-kit
versions: [">=1.0.0-rc.5 <1.0.0"]

# The Flutter app. Separate from the Worker so a Dart bump and a Node bump
# are never the same pull request; they are reviewed by reading different
# things and break in different jobs.
- package-ecosystem: pub
directory: /
schedule:
interval: weekly
groups:
codegen:
# build_runner and the generators it drives are version-locked to each
# other in practice: bumping freezed without build_runner produces an
# analyzer error in generated code rather than a clean failure.
patterns:
- build_runner
- build_*
- freezed*
- json_serializable
- riverpod_generator
- drift_dev
directories:
- /
- /packages/opensplit_api
patterns: ["*"]
multi-ecosystem-group: routine-dependencies
ignore:
- dependency-name: "*"
update-types: [version-update:semver-major]
# This patch's dbus requirement conflicts with stable notifications.
# Revisit with the notification and Android toolchain upgrade.
- dependency-name: connectivity_plus
versions: ["7.3.2"]
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ What changed in each version of the app, newest first. The format is
[Semantic Versioning](https://semver.org/), and the file is kept by
[cider](https://pub.dev/packages/cider): see "Versions" in docs/runbook.md.

## [Unreleased]
### Fixed
- Improved browser database transaction reliability.

### Security
- Updated sign-in protections.

## [3.1.0] - 2026-10-11
### Added
- Amount and percentage splits, and multiple payers, start evenly and share the remainder as you edit. Saved splits keep their values.
Expand Down Expand Up @@ -90,6 +97,7 @@ What changed in each version of the app, newest first. The format is
### Added
- The first build, to Play internal testing.

[Unreleased]: https://github.com/eigeninteractive/opensplit/compare/v3.1.0...HEAD
[3.1.0]: https://github.com/eigeninteractive/opensplit/compare/v3.0.0...v3.1.0
[3.0.0]: https://github.com/eigeninteractive/opensplit/compare/v2.0.0...v3.0.0
[2.0.0]: https://github.com/eigeninteractive/opensplit/compare/v1.0.0...v2.0.0
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
source "https://rubygems.org"

gem "fastlane", "2.240.0"
gem "fastlane", "2.240.1"
47 changes: 25 additions & 22 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ GEM
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.4.0)
aws-partitions (1.1287.0)
aws-partitions (1.1290.0)
aws-sdk-core (3.257.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
Expand All @@ -31,6 +31,7 @@ GEM
base64 (0.3.0)
benchmark (0.5.0)
bigdecimal (4.1.3)
cgi (0.5.2)
claide (1.1.0)
colored (1.2)
colored2 (3.1.2)
Expand All @@ -40,7 +41,7 @@ GEM
declarative (0.0.20)
digest-crc (0.7.0)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.6.20260907)
domain_name (0.6.20260921)
dotenv (2.8.1)
emoji_regex (3.2.3)
erb (6.0.7)
Expand All @@ -62,7 +63,7 @@ GEM
faraday-retry (2.4.0)
faraday (~> 2.0)
fastimage (2.4.1)
fastlane (2.240.0)
fastlane (2.240.1)
CFPropertyList (>= 2.3, < 5.0.0)
abbrev (~> 0.1)
addressable (>= 2.9.0, < 3.0.0)
Expand All @@ -72,6 +73,7 @@ GEM
base64 (~> 0.2)
benchmark (>= 0.1.0)
bundler (>= 2.4.0, < 5.0.0)
cgi (~> 0.4)
colored (~> 1.2)
commander (~> 4.6)
csv (~> 3.3)
Expand All @@ -86,7 +88,7 @@ GEM
fastimage (>= 2.1.0, < 3.0.0)
fastlane-sirp (>= 1.1.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.99.0)
google-apis-androidpublisher_v3 (~> 0.99)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-env (>= 1.6.0, < 2.4.0)
google-cloud-storage (~> 1.31)
Expand All @@ -105,7 +107,7 @@ GEM
ostruct (>= 0.1.0)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 3.4.0, < 4.0.0)
security (= 0.2.0)
security (~> 0.3)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (~> 4)
Expand All @@ -117,7 +119,7 @@ GEM
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-sirp (1.1.0)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.99.0)
google-apis-androidpublisher_v3 (0.108.0)
google-apis-core (>= 0.15.0, < 2.a)
google-apis-core (1.2.5)
addressable (~> 2.9)
Expand Down Expand Up @@ -205,15 +207,15 @@ GEM
tsort
reline (0.7.0)
io-console (~> 0.5)
representable (3.2.0)
representable (3.3.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (4.2.0)
rexml (3.4.4)
rouge (3.28.0)
rubyzip (3.6.0)
security (0.2.0)
rubyzip (3.7.0)
security (0.3.0)
signet (0.22.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
Expand All @@ -231,9 +233,9 @@ GEM
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
uber (0.1.0)
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.2.0)
unicode-display_width (3.3.0)
unicode-emoji (~> 4.3)
unicode-emoji (4.3.0)
uri (1.1.1)
word_wrap (1.0.0)
xcodeproj (1.28.1)
Expand All @@ -256,7 +258,7 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
fastlane (= 2.240.0)
fastlane (= 2.240.1)

CHECKSUMS
CFPropertyList (3.0.8) sha256=2c99d0d980536d3d7ab252f7bd59ac8be50fbdd1ff487c98c949bb66bb114261
Expand All @@ -265,7 +267,7 @@ CHECKSUMS
artifactory (3.0.17) sha256=3023d5c964c31674090d655a516f38ca75665c15084140c08b7f2841131af263
atomos (0.1.3) sha256=7d43b22f2454a36bace5532d30785b06de3711399cb1c6bf932573eda536789f
aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b
aws-partitions (1.1287.0) sha256=76f4c73011654553566c5db5bbef0f24738bb016e923910e8b1b3f9cb7d989ca
aws-partitions (1.1290.0) sha256=44613571eda66decddca594c10c9cee354ce819842b5724ca4fb5115e2f06811
aws-sdk-core (3.257.0) sha256=a92ad19c7ff668cec6688ee3200ee562da80647a3bbeed9fda02e8b0ca6492c6
aws-sdk-kms (1.132.0) sha256=094b0097bb3be9d5c1ac87e971ca7f5aebb746801dfb5f0f576480bf98dca69c
aws-sdk-s3 (1.232.1) sha256=124440f3a44fbc28cc3a2dec6eaeeea2a236bfc33772005010a19baa62654870
Expand All @@ -274,14 +276,15 @@ CHECKSUMS
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
cgi (0.5.2) sha256=61ca30298171190fd4fa0d8018e57ada456eae9b7a2b78526debf7f0a0e6f8bb
claide (1.1.0) sha256=6d3c5c089dde904d96aa30e73306d0d4bd444b1accb9b3125ce14a3c0183f82e
colored (1.2) sha256=9d82b47ac589ce7f6cab64b1f194a2009e9fd00c326a5357321f44afab2c1d2c
colored2 (3.1.2) sha256=b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a
commander (4.6.0) sha256=7d1ddc3fccae60cc906b4131b916107e2ef0108858f485fdda30610c0f2913d9
csv (3.3.6) sha256=aba61e7e507a66f03d45cb1f3c4b6359861c3504038b422962875dce099e4456
declarative (0.0.20) sha256=8021dd6cb17ab2b61233c56903d3f5a259c5cf43c80ff332d447d395b17d9ff9
digest-crc (0.7.0) sha256=64adc23a26a241044cbe6732477ca1b3c281d79e2240bcff275a37a5a0d78c07
domain_name (0.6.20260907) sha256=5517a7f82c4464a61fc17133c3f6a02833faa13533c16c07e8f73b11d872cb03
domain_name (0.6.20260921) sha256=b335949f69fae603fed0ae88c4e09a51e9d8e3ceef4c73a6cfde44eb5c8dee6d
dotenv (2.8.1) sha256=c5944793349ae03c432e1780a2ca929d60b88c7d14d52d630db0508c3a8a17d8
emoji_regex (3.2.3) sha256=ecd8be856b7691406c6bf3bb3a5e55d6ed683ffab98b4aa531bb90e1ddcc564b
erb (6.0.7) sha256=c5ca6dc25b0ef974a44dc8f59fe847577122483b1968a38dec305c60bf91ee92
Expand All @@ -293,10 +296,10 @@ CHECKSUMS
faraday-net_http (3.4.4) sha256=0e78af151747ed1b00f33e25973b4bc220d7f16c00c39676817c8b12331eb588
faraday-retry (2.4.0) sha256=7b79c48fb7e56526faf247b12d94a680071ff40c9fda7cf1ec1549439ad11ebe
fastimage (2.4.1) sha256=c64bebd46b6fd8943ab70c1e6e85ff728f970f2e48f92ecd249b6bc3a540ad20
fastlane (2.240.0) sha256=9f3ead3cfd7f1fbf5c6acc3fe3ed398b5ce44b194e548a94fc4e0f2186c59c0b
fastlane (2.240.1) sha256=cf792bc99ec6af6574ea416cda6530f519c8b4b3a46b1a3b52488ebf1696aa5c
fastlane-sirp (1.1.0) sha256=10bc94f9682efd8e1badfb31452a76dd8981f1f3a33717c765fde6d75b54d847
gh_inspector (1.1.3) sha256=04cca7171b87164e053aa43147971d3b7f500fcb58177698886b48a9fc4a1939
google-apis-androidpublisher_v3 (0.99.0) sha256=a0452fdd99cb7672cc95cac07429305f8aaee54c22e4875224cf675b1ab59729
google-apis-androidpublisher_v3 (0.108.0) sha256=11a8b1885dce62ef28d8b7f16225d67e60c77b5baf16310a31d33fb1dc3c2d4e
google-apis-core (1.2.5) sha256=e21562b5627a0fc6a0c3165e429c3afed0f6a628eaa514e83f7204dda1adff69
google-apis-iamcredentials_v1 (0.28.0) sha256=0a92ffe6cc39c569554af2a77a25dfc61519ed8bbb64ab04cffdd352dc5ef106
google-apis-playcustomapp_v1 (0.18.0) sha256=44b277b9dee4a59ac5e9d98be1485edc5e382d2f9d73c79ae8908a455786a254
Expand Down Expand Up @@ -337,12 +340,12 @@ CHECKSUMS
rbs (4.2.0) sha256=51f7b886dcc05bc09e10b901daa6a81829f6adc03101d6ca9ea4aac6103e0674
rdoc (8.0.0) sha256=03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469
reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
representable (3.2.0) sha256=cc29bf7eebc31653586849371a43ffe36c60b54b0a6365b5f7d95ec34d1ebace
representable (3.3.0) sha256=22beca7d07104d8dfe30667eb267756c764c8bdc6c0f1481922502ecc4f0d8cf
retriable (4.2.0) sha256=90c3b257472a1c02f2a3dfa64dd35a420f7a624cef1a5981e20fdac5730f8cdc
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
rouge (3.28.0) sha256=0d6de482c7624000d92697772ab14e48dca35629f8ddf3f4b21c99183fd70e20
rubyzip (3.6.0) sha256=268994d44d62282d1cfd99bf10eae48d7267199158ad7ea3e1fee2da9458b695
security (0.2.0) sha256=1f1694d2071f2f3b8b6595342fd72df40b6d0e15a4431a772c06c2f02b253255
rubyzip (3.7.0) sha256=65c19294da75297a939006f3516deacc33185fbd721ff1954f7a231db6d3e121
security (0.3.0) sha256=bc9d06718258a96c8552ce4044bfe69144491b10d3403d74c4753a03a720ecf0
signet (0.22.0) sha256=b76d495ccb07ad35dbc89f3e920665a9d8ed717141955034005d7843dcfe4780
simctl (1.6.10) sha256=b99077f4d13ad81eace9f86bf5ba4df1b0b893a4d1b368bd3ed59b5b27f9236b
terminal-notifier (2.0.0) sha256=7a0d2b2212ab9835c07f4b2e22a94cff64149dba1eed203c04835f7991078cea
Expand All @@ -353,8 +356,8 @@ CHECKSUMS
tty-screen (0.8.2) sha256=c090652115beae764336c28802d633f204fb84da93c6a968aa5d8e319e819b50
tty-spinner (0.9.3) sha256=0e036f047b4ffb61f2aa45f5a770ec00b4d04130531558a94bfc5b192b570542
uber (0.1.0) sha256=5beeb407ff807b5db994f82fa9ee07cfceaa561dad8af20be880bc67eba935dc
unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
unicode-display_width (3.3.0) sha256=4b7aa66a4b11db50f6f7e98411215cd0dd10eaecb665d36eec5bcacb3fa0b613
unicode-emoji (4.3.0) sha256=11c02fa73290378c066bb0562cd4c87d8e1b706fbbe0059ca12746a4244de8ce
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
word_wrap (1.0.0) sha256=f556d4224c812e371000f12a6ee8102e0daa724a314c3f246afaad76d82accc7
xcodeproj (1.28.1) sha256=6f12670f00739d9817ca27ac89d6ef01cc86050e22a0bc08a3131487e5b5cddc
Expand Down
10 changes: 4 additions & 6 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,10 @@ plugins:
riverpod_lint:
version: ^3.1.8

# Rules turned off, and this map rather than `// ignore:` comments because
# ignores do not work on plugin diagnostics. The analyzer parses them -- it
# will even tell you an ignore is redundant -- and then reports the
# diagnostic anyway, which is worth knowing before spending an afternoon
# adding them. It is all or nothing per rule, so each of these is a
# project-wide decision rather than a local exemption.
# Project-wide rule decisions belong here. Local exemptions use the
# qualified diagnostic, for example:
# // ignore: riverpod_lint/scoped_providers_should_specify_dependencies
# The earlier plugin-ignore bug was fixed upstream in dart-lang/sdk#62131.
diagnostics:
# Off because it cannot be satisfied here, not because it is unwelcome.
#
Expand Down
2 changes: 1 addition & 1 deletion biome.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// One config at the root, so the CLI from server/ (`pnpm lint`, which CI runs)
// and an editor opened here read the same rules.
{
"$schema": "https://biomejs.dev/schemas/2.5.14/schema.json",
"$schema": "https://biomejs.dev/schemas/2.5.15/schema.json",
"root": true,
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
"files": {
Expand Down
15 changes: 9 additions & 6 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ Drizzle tables ──drizzle-orm/zod──▶ Zod rows, and request bodies picke
`unknownDefaultOpenApi`.
- **An event carries its payload in a typed field per shape** — `entry`,
`member`, `group`, `link`, exactly one set, chosen by `kind` — rather than one
untyped `payload`. The generator cannot read `oneOf`; separate nullable
untyped `payload`. The configured Dart-Dio/json_serializable generator has
no verified working `oneOf` path for these models; separate nullable
fields give the app a generated class for each without it. The object stores
them the same way, so the table *is* the wire row.
- **The push message is a contract type** (`PushData`), registered as a
Expand Down Expand Up @@ -183,11 +184,13 @@ as nullable before any route uses them.

### Required, nullable, optional

**Every body field is required, and `null` is the only way to say "none".** A
generated Dart client cannot send "absent" as distinct from null (it drops a
null optional field), so an optional field would have three states on the
server and two on the device. Every write is therefore a `PUT` of the whole
row at the id the device minted — `PUT /groups/{g}`, `/members/{m}`,
**Every body field is required, and `null` is the only way to say "none".**
With our current generator configuration, null optional fields are omitted,
so an optional field would have three states on the server and two on the
device. Dart-Dio's `useOptional` option can preserve that distinction with an
`Optional<T>` wrapper, but this API deliberately uses whole-row writes for
simple retry semantics. Every write is a `PUT` of the whole row at the id
the device minted — `PUT /groups/{g}`, `/members/{m}`,
`/entries/{e}` — which creates it or replaces it, so a retry is the same
request. Soft deletion is a field of that row (`deletedAt` set or null; the
server stamps its own time), not a separate verb. Optional exists only for
Expand Down
Loading