Commit graph

70 commits

Author SHA1 Message Date
ca5b3cf599 docs: add test plan for full API surface
Some checks failed
ci / test (push) Has been cancelled
ci / test (pull_request) Has been cancelled
Claude-Session: https://claude.ai/code/session_01T4vgXtQLs2wfNwn6B1m4hm
2026-07-05 08:49:41 -03:00
ce772b369e docs: alamedya migration guide v2 + typed-contract README/moduledoc refresh 2026-07-05 08:32:10 -03:00
eabc87288d feat(api): lazy pagination streams (history, members, bans, audit log) 2026-07-05 08:21:50 -03:00
b9f1ced136 feat(permissions): documented overwrite-resolution algorithm on Guild 2026-07-05 08:15:07 -03:00
8070f11aa2 feat(ergonomics): allowed_mentions config default with field-wise merge; Embed builder 2026-07-05 08:09:56 -03:00
99208f2212 feat(ergonomics): reply/2, mentions + String.Chars, created_at, format_dt 2026-07-05 08:03:35 -03:00
8a80957faf feat(ergonomics): Messageable protocol + Api.send/2 with lazy DM cache 2026-07-05 07:50:59 -03:00
5169107563 style: format gateway_integration_test and slash_test
Claude-Session: https://claude.ai/code/session_01T4vgXtQLs2wfNwn6B1m4hm
2026-07-05 07:41:35 -03:00
c3c483017d feat(cache): fill/1 hydration for envelope structs 2026-07-05 07:40:23 -03:00
07ca39e5a7 feat(slash): typed %Interaction{} dispatch + struct-tolerant commands/0 2026-07-05 07:35:28 -03:00
cdca1f13ce feat(dispatcher): typed handler delivery with raw fallback; Prefix on %Message{} 2026-07-05 07:30:04 -03:00
ceab408ce9 feat(cache): merge_map partial updates, struct-aware cascade purge, typed message fold-in 2026-07-05 07:19:11 -03:00
1c35130775 feat(cache): structs keyed by integer snowflakes; dispatcher decodes once 2026-07-05 07:14:26 -03:00
bd911d6205 fix(api/endpoint): stop silently discarding non-map form bodies
stringify_values/1 had a catch-all that coerced any non-map payload to
%{}, silently dropping all form fields on the files: :form path. Narrow
the catch-all to nil only (the legitimate payload || %{} case); any other
non-map now raises FunctionClauseError instead of vanishing on the wire.

Add prepare_body/3 form-field tests covering the map, nil, and
non-map-raises cases.
2026-07-05 06:56:57 -03:00
0a84650025 feat(coverage): mix dexcord.coverage gate with pinned OpenAPI spec + CI workflow
Adds `Dexcord.Api.Coverage`, a testable core that diffs the compiled
endpoint route table against a pinned snapshot of Discord's official
OpenAPI spec (`priv/discord_openapi.json`). The spec is a coverage
CHECKLIST, not codegen input: it is refreshed only by the deliberate
`mix dexcord.coverage.refresh`, so upstream churn never breaks CI
spontaneously.

- `mix dexcord.coverage` compiles then exits non-zero on any in-scope
  endpoint missing from the declared surface (AC1.8/AC1.9).
- `mix dexcord.coverage.refresh` pins the spec via :httpc + castore,
  adding inets/ssl to the code path since Mix prunes them.
- `priv/coverage_allowlist.exs` records out-of-scope spec routes with a
  comment per family; reconciled against the real pin (Social SDK
  lobbies/partner-sdk, OIDC, application-by-id, scheduled-event
  exceptions, join requests, thread search, monetization).
- `.gitea/workflows/ci.yml` runs the suite + gate (Actions must be
  enabled on the Gitea remote).
- ratelimit_test.exs gains AC1.10 spot-checks proving the new Phase 5
  route shapes collapse to bounded, id-collapsed keys.
2026-07-05 06:46:09 -03:00
6aec66f5ad feat(api): commands, emojis/stickers, moderation, users, misc rollout — full surface 2026-07-05 06:34:45 -03:00
36a4bef4c6 feat(api): webhooks + invites + full interactions rollout 2026-07-05 06:28:31 -03:00
6830abd7e3 feat(api): guilds + members + roles endpoint rollout 2026-07-05 06:25:59 -03:00
217361de42 feat(api): full channels + messages endpoint rollout 2026-07-05 06:23:09 -03:00
f746e7a30d feat(api): plain form-field multipart variant for sticker uploads 2026-07-05 06:16:35 -03:00
9e738e95f3 fix(api): deterministic facade generation + returns:nil decode; address review
- [Critical] Facade delegate set is now derived from static, compile-time-stable
  data (each group's __endpoints__/0 route table) instead of the group's
  module_info(:exports). module_info/1 is an auto-generated function that the
  Elixir compiler tracer does not record as a compile-time dependency, so no
  compile-order edge was established between Dexcord.Api and the group modules;
  under parallel/incremental compilation the export table was read incompletely,
  silently dropping delegates (e.g. Dexcord.Api.get_gateway_bot/0 undefined after
  a routine recompile). Arities are derived from segment param count + body flag
  (each head's opts \\ [] yields base and base+1), with get_channel_messages/3
  named explicitly as sugar. Verified: delegate always present in the compiled
  beam; 8x incremental-recompile loop of facade + gateway_integration all green.

- [Important] api_facade_test.exs is now deterministic: it asserts the facade's
  exported set equals the same statically-derived expected set the generator
  uses (no module_info), and ensures Dexcord.Api + groups are loaded before
  function_exported? probes (which do not auto-load), removing a false-negative
  async load race. Historically-load-bearing arity assertions kept.

- [Minor] decode_response/2 now normalizes returns:nil endpoints to {:ok, nil}
  even when the server sends a non-204 JSON body, instead of leaking the raw
  decoded map through the passthrough clause. Added a unit test.
2026-07-05 06:01:56 -03:00
09c9f2b65a feat(ratelimit): collapse invite codes in route keys; cover new route shapes 2026-07-05 05:35:24 -03:00
234dbd3dea refactor(api): existing endpoints reborn as DSL declarations behind a generated facade
Redeclare the full existing endpoint surface with the Dexcord.Api.Endpoint
DSL across six group modules (Channels, Messages, Users, Interactions,
Commands, Misc) and delete the old named endpoint functions plus their
private helpers (message_query/maybe_param/maybe_put) from Dexcord.Api,
keeping request/4 and the rest of the request pipeline untouched (AC1.7).

Dexcord.Api re-exports every group endpoint head as a generated defdelegate
driven by module_info(:exports), so Dexcord.Api.<endpoint> keeps working for
all 9 internal call sites and external callers. create_dm is registered by
hand (spec + snowflake sugar) because the generated create_dm(body, opts \\ [])
head would collide with create_dm/1.

Endpoints are now typed: get_current_user -> Dexcord.User, create/edit_message
-> Dexcord.Message, get_channel/start_thread -> Dexcord.Channel,
get_current_application -> Dexcord.ApplicationInfo, bulk_overwrite_* ->
[Dexcord.ApplicationCommand], etc. get_gateway_bot and
create_interaction_response stay :raw. Patch registrar to match
%Dexcord.ApplicationInfo{} and widen Config.put_application_id/1 to accept
integer ids.

Behavior changes (documented in tests):
- get_channel_messages/2 no longer collapses multiple anchors with a
  before>after>around precedence; it forwards every declared query key, so
  callers pass exactly one anchor (the /3 locator arity still enforces this).
- start_thread_with_message extra fields now travel in the body argument
  rather than a trailing opts list.
- *_id path params are validated via Snowflake.cast/1; integration-test
  fixtures updated from non-numeric placeholders to numeric snowflake ids.

Add test/dexcord/api_facade_test.exs asserting the facade cannot silently drop
any group endpoint export.

Claude-Session: https://claude.ai/code/session_01T4vgXtQLs2wfNwn6B1m4hm
2026-07-05 05:32:54 -03:00
45aa60de3d feat(api): endpoint/4 declaration macro with compiled route table 2026-07-05 05:10:19 -03:00
8dd3c99129 feat(api): endpoint runtime engine — paths, query, bodies, reason, decode 2026-07-05 05:07:30 -03:00
c852d2a453 feat(api): multipart payload_json + files[n] body support in request pipeline 2026-07-05 05:02:56 -03:00
e1b6c01e72 feat(events): decode table covering all 67 documented dispatch events 2026-07-05 04:53:12 -03:00
576bfa1205 feat(events): envelope structs with hydrate slots for all non-full-object events 2026-07-05 04:51:12 -03:00
9c8c8de498 feat(model): webhook/invite/audit/automod/scheduled/poll/application/team + retypes 2026-07-05 04:45:45 -03:00
490ad53a54 feat(model): ApplicationCommand family with recursive options 2026-07-05 04:40:51 -03:00
93057e349a feat(model): Interaction family with polymorphic data + resolved partials 2026-07-05 04:37:19 -03:00
bedf200a64 feat(model): component structs + factory (incl. Components V2 + modal set) 2026-07-05 04:31:56 -03:00
11a8e15b45 feat(model): interaction/component/audit/automod/event enum + flags tables 2026-07-05 04:27:25 -03:00
ae1f312e3b feat(struct): defoverridable generated fns + {:id_map, t} dictionary type 2026-07-05 04:24:02 -03:00
227f6d8422 feat(model): Guild family, VoiceState, Presence, golden GUILD_CREATE fixture 2026-07-05 04:10:47 -03:00
3f52d82e6c feat(model): Message + reference/snapshot/call satellites, golden message fixtures 2026-07-05 04:05:29 -03:00
b278e3eeaa feat(model): channel structs + factory dispatch on wire type 2026-07-05 04:01:55 -03:00
c9c4a3c445 feat(model): Member/PartialMember via shared field group, thread + forum satellites 2026-07-05 03:57:11 -03:00
0c22758260 feat(model): Role, RoleColors, RoleTags (presence booleans), Overwrite 2026-07-05 03:54:56 -03:00
e065b4d6c7 feat(model): Attachment, Embed family, Reaction, AllowedMentions 2026-07-05 03:54:08 -03:00
5570609adb feat(model): User, AvatarDecorationData, PrimaryGuild, Emoji, PartialEmoji, Sticker, StickerItem 2026-07-05 03:52:52 -03:00
76e3248d96 feat(model): core enum and flags tables (channel/message/user/permissions/...) 2026-07-05 03:49:59 -03:00
202be23ba0 feat(struct): :number type, string-wire flags, wire_string opt, include_fields 2026-07-05 03:46:10 -03:00
a00e455c58 fix(struct): address code review feedback
- Formatting: add field/hydrate/discord_struct to locals_without_parens
  in .formatter.exs so the no-paren data-DSL style is the formatted form;
  mix format then wrapped the long codec encode_value/2 clause. Both
  previously-unformatted files now satisfy mix format --check-formatted.
- Empty-struct guard: __before_compile__ now raises ArgumentError when a
  module does 'use Dexcord.Struct' with no field/hydrate declarations,
  instead of silently generating a bare defstruct []. Added a compile-time
  test mirroring the existing Code.compile_string validation tests.
2026-07-05 03:37:48 -03:00
d0962cc28a feat(struct): to_map encode + merge_map partial-update semantics 2026-07-05 03:26:01 -03:00
e9ebf34f32 feat(struct): tolerant from_map decode engine — zero atoms, never raises 2026-07-05 03:24:30 -03:00
1bf9b84518 feat(struct): discord_struct DSL — defstruct, @type t, field table generation 2026-07-05 03:23:18 -03:00
ed084ef5a3 feat(flags): bitfield DSL with raw-int storage and named views 2026-07-05 03:17:20 -03:00
022d157d65 feat(enum): open integer enum DSL with {:unknown, n} lossless round-trip 2026-07-05 03:13:31 -03:00
2163ff5b2f feat(snowflake): integer-only t(), cast/cast!/dump, is_snowflake guard, fixed to_unix 2026-07-05 03:09:43 -03:00