@hodeinavarro/odoo-rpc-ts 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +401 -0
  3. package/dist/client.d.ts +184 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +277 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/combinators/retryOnSessionExpired.d.ts +13 -0
  8. package/dist/combinators/retryOnSessionExpired.d.ts.map +1 -0
  9. package/dist/combinators/retryOnSessionExpired.js +15 -0
  10. package/dist/combinators/retryOnSessionExpired.js.map +1 -0
  11. package/dist/commands.d.ts +91 -0
  12. package/dist/commands.d.ts.map +1 -0
  13. package/dist/commands.js +69 -0
  14. package/dist/commands.js.map +1 -0
  15. package/dist/config.d.ts +32 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +60 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/domain.d.ts +30 -0
  20. package/dist/domain.d.ts.map +1 -0
  21. package/dist/domain.js +70 -0
  22. package/dist/domain.js.map +1 -0
  23. package/dist/errors/auth.d.ts +27 -0
  24. package/dist/errors/auth.d.ts.map +1 -0
  25. package/dist/errors/auth.js +8 -0
  26. package/dist/errors/auth.js.map +1 -0
  27. package/dist/errors/index.d.ts +30 -0
  28. package/dist/errors/index.d.ts.map +1 -0
  29. package/dist/errors/index.js +13 -0
  30. package/dist/errors/index.js.map +1 -0
  31. package/dist/errors/mapFault.d.ts +31 -0
  32. package/dist/errors/mapFault.d.ts.map +1 -0
  33. package/dist/errors/mapFault.js +48 -0
  34. package/dist/errors/mapFault.js.map +1 -0
  35. package/dist/errors/protocol.d.ts +22 -0
  36. package/dist/errors/protocol.d.ts.map +1 -0
  37. package/dist/errors/protocol.js +9 -0
  38. package/dist/errors/protocol.js.map +1 -0
  39. package/dist/errors/schema.d.ts +22 -0
  40. package/dist/errors/schema.d.ts.map +1 -0
  41. package/dist/errors/schema.js +14 -0
  42. package/dist/errors/schema.js.map +1 -0
  43. package/dist/errors/server.d.ts +109 -0
  44. package/dist/errors/server.d.ts.map +1 -0
  45. package/dist/errors/server.js +49 -0
  46. package/dist/errors/server.js.map +1 -0
  47. package/dist/errors/session.d.ts +16 -0
  48. package/dist/errors/session.d.ts.map +1 -0
  49. package/dist/errors/session.js +11 -0
  50. package/dist/errors/session.js.map +1 -0
  51. package/dist/errors/transport.d.ts +42 -0
  52. package/dist/errors/transport.d.ts.map +1 -0
  53. package/dist/errors/transport.js +49 -0
  54. package/dist/errors/transport.js.map +1 -0
  55. package/dist/index.d.ts +33 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +57 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/internal/platform.d.ts +12 -0
  60. package/dist/internal/platform.d.ts.map +1 -0
  61. package/dist/internal/platform.js +11 -0
  62. package/dist/internal/platform.js.map +1 -0
  63. package/dist/internal/singleFlight.d.ts +29 -0
  64. package/dist/internal/singleFlight.d.ts.map +1 -0
  65. package/dist/internal/singleFlight.js +41 -0
  66. package/dist/internal/singleFlight.js.map +1 -0
  67. package/dist/profiles.d.ts +179 -0
  68. package/dist/profiles.d.ts.map +1 -0
  69. package/dist/profiles.js +191 -0
  70. package/dist/profiles.js.map +1 -0
  71. package/dist/protocol/jsonrpc.d.ts +127 -0
  72. package/dist/protocol/jsonrpc.d.ts.map +1 -0
  73. package/dist/protocol/jsonrpc.js +117 -0
  74. package/dist/protocol/jsonrpc.js.map +1 -0
  75. package/dist/records/index.d.ts +14 -0
  76. package/dist/records/index.d.ts.map +1 -0
  77. package/dist/records/index.js +14 -0
  78. package/dist/records/index.js.map +1 -0
  79. package/dist/records/recordModel.d.ts +131 -0
  80. package/dist/records/recordModel.d.ts.map +1 -0
  81. package/dist/records/recordModel.js +101 -0
  82. package/dist/records/recordModel.js.map +1 -0
  83. package/dist/records/related.d.ts +42 -0
  84. package/dist/records/related.d.ts.map +1 -0
  85. package/dist/records/related.js +51 -0
  86. package/dist/records/related.js.map +1 -0
  87. package/dist/records/relations.d.ts +66 -0
  88. package/dist/records/relations.d.ts.map +1 -0
  89. package/dist/records/relations.js +119 -0
  90. package/dist/records/relations.js.map +1 -0
  91. package/dist/records/spec.d.ts +44 -0
  92. package/dist/records/spec.d.ts.map +1 -0
  93. package/dist/records/spec.js +63 -0
  94. package/dist/records/spec.js.map +1 -0
  95. package/dist/records/typed.d.ts +71 -0
  96. package/dist/records/typed.d.ts.map +1 -0
  97. package/dist/records/typed.js +65 -0
  98. package/dist/records/typed.js.map +1 -0
  99. package/dist/rpc.d.ts +90 -0
  100. package/dist/rpc.d.ts.map +1 -0
  101. package/dist/rpc.js +113 -0
  102. package/dist/rpc.js.map +1 -0
  103. package/dist/services/db.d.ts +128 -0
  104. package/dist/services/db.d.ts.map +1 -0
  105. package/dist/services/db.js +132 -0
  106. package/dist/services/db.js.map +1 -0
  107. package/dist/services/report.d.ts +48 -0
  108. package/dist/services/report.d.ts.map +1 -0
  109. package/dist/services/report.js +103 -0
  110. package/dist/services/report.js.map +1 -0
  111. package/dist/session/cookie.d.ts +114 -0
  112. package/dist/session/cookie.d.ts.map +1 -0
  113. package/dist/session/cookie.js +231 -0
  114. package/dist/session/cookie.js.map +1 -0
  115. package/dist/testing/fakeTransport.d.ts +40 -0
  116. package/dist/testing/fakeTransport.d.ts.map +1 -0
  117. package/dist/testing/fakeTransport.js +36 -0
  118. package/dist/testing/fakeTransport.js.map +1 -0
  119. package/dist/testing/index.d.ts +9 -0
  120. package/dist/testing/index.d.ts.map +1 -0
  121. package/dist/testing/index.js +9 -0
  122. package/dist/testing/index.js.map +1 -0
  123. package/dist/transport.d.ts +62 -0
  124. package/dist/transport.d.ts.map +1 -0
  125. package/dist/transport.js +9 -0
  126. package/dist/transport.js.map +1 -0
  127. package/dist/transports/json2.d.ts +41 -0
  128. package/dist/transports/json2.d.ts.map +1 -0
  129. package/dist/transports/json2.js +196 -0
  130. package/dist/transports/json2.js.map +1 -0
  131. package/dist/transports/jsonrpc.d.ts +41 -0
  132. package/dist/transports/jsonrpc.d.ts.map +1 -0
  133. package/dist/transports/jsonrpc.js +119 -0
  134. package/dist/transports/jsonrpc.js.map +1 -0
  135. package/dist/transports/web.d.ts +15 -0
  136. package/dist/transports/web.d.ts.map +1 -0
  137. package/dist/transports/web.js +62 -0
  138. package/dist/transports/web.js.map +1 -0
  139. package/dist/version-live.d.ts +41 -0
  140. package/dist/version-live.d.ts.map +1 -0
  141. package/dist/version-live.js +26 -0
  142. package/dist/version-live.js.map +1 -0
  143. package/dist/version.d.ts +78 -0
  144. package/dist/version.d.ts.map +1 -0
  145. package/dist/version.js +46 -0
  146. package/dist/version.js.map +1 -0
  147. package/docs/protocol-verification.md +43 -0
  148. package/package.json +71 -0
  149. package/src/client.ts +731 -0
  150. package/src/combinators/retryOnSessionExpired.ts +25 -0
  151. package/src/commands.ts +112 -0
  152. package/src/config.ts +110 -0
  153. package/src/domain.ts +91 -0
  154. package/src/errors/auth.ts +28 -0
  155. package/src/errors/index.ts +50 -0
  156. package/src/errors/mapFault.ts +92 -0
  157. package/src/errors/protocol.ts +19 -0
  158. package/src/errors/schema.ts +18 -0
  159. package/src/errors/server.ts +79 -0
  160. package/src/errors/session.ts +12 -0
  161. package/src/errors/transport.ts +74 -0
  162. package/src/index.ts +152 -0
  163. package/src/internal/platform.ts +22 -0
  164. package/src/internal/singleFlight.ts +80 -0
  165. package/src/profiles.ts +386 -0
  166. package/src/protocol/jsonrpc.ts +162 -0
  167. package/src/records/index.ts +49 -0
  168. package/src/records/recordModel.ts +211 -0
  169. package/src/records/related.ts +80 -0
  170. package/src/records/relations.ts +192 -0
  171. package/src/records/spec.ts +96 -0
  172. package/src/records/typed.ts +180 -0
  173. package/src/rpc.ts +219 -0
  174. package/src/services/db.ts +282 -0
  175. package/src/services/report.ts +165 -0
  176. package/src/session/cookie.ts +461 -0
  177. package/src/testing/fakeTransport.ts +84 -0
  178. package/src/testing/index.ts +9 -0
  179. package/src/transport.ts +72 -0
  180. package/src/transports/json2.ts +303 -0
  181. package/src/transports/jsonrpc.ts +218 -0
  182. package/src/transports/web.ts +96 -0
  183. package/src/version-live.ts +67 -0
  184. package/src/version.ts +102 -0
@@ -0,0 +1,191 @@
1
+ import { Context, Data, Effect, Layer, Option, Ref } from "effect";
2
+ /**
3
+ * A profile's storage operation failed at the consumer-supplied backend (keyring
4
+ * denied, IndexedDB transaction aborted, file unwritable, …). Wraps the opaque
5
+ * platform `cause`; upper layers branch on `_tag`, never on the message. A store
6
+ * failure proves only that *that* operation failed — infer nothing about the
7
+ * other backend's state (see the removal ordering in {@link make}).
8
+ */
9
+ export class ProfileStoreError extends Data.TaggedError("ProfileStoreError") {
10
+ }
11
+ /**
12
+ * A profile's metadata was found but its credential secret is not reachable
13
+ * through {@link SecretStore} (the secret was never stored, was removed out of
14
+ * band, or the named profile does not exist at all). Distinct from
15
+ * {@link ProfileStoreError}: the backend answered fine — the secret is simply
16
+ * absent, so the {@link OdooConfig} cannot be reconstructed.
17
+ */
18
+ export class ProfileSecretMissingError extends Data.TaggedError("ProfileSecretMissingError") {
19
+ }
20
+ /**
21
+ * Consumer-implemented secret vault, keyed by an opaque `account` string. The
22
+ * platform-portable seam: an OS keyring in Node, IndexedDB + WebCrypto in a
23
+ * browser, a `Ref`-backed map in tests. Secrets cross this boundary only as
24
+ * `Redacted` — never a bare string — so they cannot leak into logs or JSON.
25
+ */
26
+ export class SecretStore extends Context.Service()("odoo-rpc-ts/SecretStore") {
27
+ }
28
+ /**
29
+ * Consumer-implemented store for the secret-free {@link ProfileData} map, keyed
30
+ * by profile name. A JSON file on disk, `localStorage`, a config table — anything
31
+ * that round-trips a `Record<string, ProfileData>`. Never carries secrets.
32
+ */
33
+ export class ProfileStorage extends Context.Service()("odoo-rpc-ts/ProfileStorage") {
34
+ }
35
+ /** The keyring account for a profile's secret. One secret per (name, kind). */
36
+ const accountOf = (name, credentialKind) => `${name}:${credentialKind}`;
37
+ const credentialKindOf = (c) => c._tag === "ApiKey" ? "api-key" : "password";
38
+ const secretOf = (c) => c._tag === "ApiKey" ? c.apiKey : c.password;
39
+ /** Rebuild the live {@link OdooConfig} from stored metadata + a resolved secret. */
40
+ const reconstructConfig = (data, secret) => ({
41
+ url: new URL(data.url),
42
+ db: data.db,
43
+ credentials: data.credentialKind === "api-key"
44
+ ? { _tag: "ApiKey", username: data.username, apiKey: secret }
45
+ : { _tag: "Password", username: data.username, password: secret },
46
+ });
47
+ /**
48
+ * Named connection profiles over a {@link SecretStore} + {@link ProfileStorage}
49
+ * seam. The metadata (URL, db, username, protocol) is serializable and travels
50
+ * through `ProfileStorage`; the credential secret travels *only* through
51
+ * `SecretStore` as `Redacted`. Serializing what this service persists can never
52
+ * expose secret material.
53
+ */
54
+ export class Profiles extends Context.Service()("odoo-rpc-ts/Profiles") {
55
+ }
56
+ /** Hand-written {@link Profiles} layer over the two storage seams. */
57
+ export const layer = Layer.effect(Profiles, Effect.gen(function* () {
58
+ const secrets = yield* SecretStore;
59
+ const storage = yield* ProfileStorage;
60
+ /**
61
+ * Overwriting a profile with a DIFFERENT credentialKind must evict the
62
+ * superseded secret first, or it is orphaned in the keyring forever:
63
+ * removeProfile derives the account from the *current* metadata kind, so a
64
+ * password secret left behind by a password→session switch would be
65
+ * invisible and un-removable. Evict-before-write keeps the failure modes
66
+ * benign: if the subsequent new-secret write fails, the stored metadata
67
+ * still names the OLD kind whose secret is now gone — loadProfile then
68
+ * reports ProfileSecretMissingError rather than returning stale material.
69
+ */
70
+ const evictSuperseded = (name, newKind, existing) => {
71
+ const previous = existing[name];
72
+ return previous !== undefined && previous.credentialKind !== newKind
73
+ ? secrets.remove(accountOf(name, previous.credentialKind))
74
+ : Effect.void;
75
+ };
76
+ const saveProfile = (name, config, protocol) => Effect.gen(function* () {
77
+ const credentialKind = credentialKindOf(config.credentials);
78
+ const data = {
79
+ url: config.url.href,
80
+ db: config.db,
81
+ username: config.credentials.username,
82
+ credentialKind,
83
+ protocol,
84
+ };
85
+ const existing = yield* storage.load();
86
+ yield* evictSuperseded(name, credentialKind, existing);
87
+ // Secret before metadata: if metadata landed first and the secret write
88
+ // then failed, we'd persist a profile that can never be loaded.
89
+ yield* secrets.set(accountOf(name, credentialKind), secretOf(config.credentials));
90
+ yield* storage.save({ ...existing, [name]: data });
91
+ });
92
+ const saveSessionProfile = (name, url, sessionId) => Effect.gen(function* () {
93
+ const data = { url: url.href, credentialKind: "session", protocol: "web" };
94
+ const existing = yield* storage.load();
95
+ yield* evictSuperseded(name, "session", existing);
96
+ // Same ordering as saveProfile: secret (the cookie) before metadata.
97
+ yield* secrets.set(accountOf(name, "session"), sessionId);
98
+ yield* storage.save({ ...existing, [name]: data });
99
+ });
100
+ const loadProfile = (name) => Effect.gen(function* () {
101
+ const all = yield* storage.load();
102
+ const data = all[name];
103
+ if (data === undefined) {
104
+ // No metadata ⇒ no reachable secret either.
105
+ return yield* new ProfileSecretMissingError({ name, account: name });
106
+ }
107
+ // ProfileStorage is a consumer-supplied JSON round-trip — its output is
108
+ // NOT trusted. A corrupted/hand-edited record must fail in the typed
109
+ // channel (ProfileStoreError), never throw past it (new URL TypeError,
110
+ // or a credentials arm missing username/db sailing into OdooConfig).
111
+ const url = yield* Effect.try({
112
+ try: () => new URL(data.url),
113
+ catch: (cause) => new ProfileStoreError({ operation: "profile.decode", cause }),
114
+ });
115
+ if (data.credentialKind !== "session" &&
116
+ (typeof data.username !== "string" || typeof data.db !== "string")) {
117
+ return yield* new ProfileStoreError({
118
+ operation: "profile.decode",
119
+ cause: `stored profile "${name}" (${data.credentialKind}) lacks username/db`,
120
+ });
121
+ }
122
+ const account = accountOf(name, data.credentialKind);
123
+ const maybeSecret = yield* secrets.get(account);
124
+ if (Option.isNone(maybeSecret)) {
125
+ return yield* new ProfileSecretMissingError({ name, account });
126
+ }
127
+ if (data.credentialKind === "session") {
128
+ // NEVER fabricate an OdooConfig around a session cookie — there is
129
+ // no credential; the consumer feeds this to CookieSessionLive.fromExisting.
130
+ return {
131
+ _tag: "session",
132
+ url,
133
+ sessionId: maybeSecret.value,
134
+ protocol: data.protocol,
135
+ };
136
+ }
137
+ return {
138
+ _tag: "credentials",
139
+ config: reconstructConfig(data, maybeSecret.value),
140
+ protocol: data.protocol,
141
+ };
142
+ });
143
+ const listProfiles = () => storage.load();
144
+ const removeProfile = (name) => Effect.gen(function* () {
145
+ const all = yield* storage.load();
146
+ const data = all[name];
147
+ if (data === undefined) {
148
+ return; // Nothing to remove — idempotent.
149
+ }
150
+ // Ordering: remove the SECRET first, then the metadata. The dangerous
151
+ // half-state is an orphaned secret — a credential left in the keyring
152
+ // with no metadata pointing at it, invisible and un-removable. Deleting
153
+ // the secret first guarantees the credential is gone even if the
154
+ // subsequent metadata save fails. The reverse transient (metadata
155
+ // without a secret) is benign and self-heals: loadProfile reports
156
+ // ProfileSecretMissingError rather than handing back a broken config.
157
+ yield* secrets.remove(accountOf(name, data.credentialKind));
158
+ const { [name]: _removed, ...rest } = all;
159
+ yield* storage.save(rest);
160
+ });
161
+ return { saveProfile, saveSessionProfile, loadProfile, listProfiles, removeProfile };
162
+ }));
163
+ /**
164
+ * Ready-made, `Ref`-backed {@link SecretStore} for tests and single-process
165
+ * consumers. Each `.layer` build gets its own isolated map — no shared global
166
+ * state between tests.
167
+ */
168
+ export const InMemorySecretStore = {
169
+ layer: Layer.effect(SecretStore, Effect.gen(function* () {
170
+ const store = yield* Ref.make({});
171
+ return {
172
+ get: (account) => Ref.get(store).pipe(Effect.map((s) => Option.fromNullishOr(s[account]))),
173
+ set: (account, secret) => Ref.update(store, (s) => ({ ...s, [account]: secret })),
174
+ remove: (account) => Ref.update(store, ({ [account]: _removed, ...rest }) => rest),
175
+ };
176
+ })),
177
+ };
178
+ /**
179
+ * Ready-made, `Ref`-backed {@link ProfileStorage} for tests and single-process
180
+ * consumers. Isolated per `.layer` build, like {@link InMemorySecretStore}.
181
+ */
182
+ export const InMemoryProfileStorage = {
183
+ layer: Layer.effect(ProfileStorage, Effect.gen(function* () {
184
+ const store = yield* Ref.make({});
185
+ return {
186
+ load: () => Ref.get(store),
187
+ save: (profiles) => Ref.set(store, profiles),
188
+ };
189
+ })),
190
+ };
191
+ //# sourceMappingURL=profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../src/profiles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ import { Schema } from "effect";
2
+ import type { FaultCallSite } from "../errors/mapFault.js";
3
+ import { type OdooServerFault } from "../errors/server.js";
4
+ import type { OdooAuthenticationError } from "../errors/auth.js";
5
+ import { SessionExpiredError } from "../errors/session.js";
6
+ /**
7
+ * The JSON-RPC envelope shared by every route that speaks it (`/jsonrpc`,
8
+ * `/web/session/authenticate`, `/web/dataset/call_kw`). Deliberately
9
+ * transport-agnostic: `params` is opaque here so the same builder serves the
10
+ * `object`/`common` service calls and the web route's `{model, method, ...}`
11
+ * params alike.
12
+ *
13
+ * GOTCHA: Odoo ignores the JSON-RPC `method` member entirely — it dispatches on
14
+ * the URL and reads `params` by name. We still send the spec-mandated
15
+ * `"call"` so the envelope is valid JSON-RPC 2.0.
16
+ */
17
+ export interface JsonRpcRequest<P> {
18
+ readonly jsonrpc: "2.0";
19
+ readonly method: "call";
20
+ readonly params: P;
21
+ readonly id: number;
22
+ }
23
+ /** Next distinct JSON-RPC request id (log-correlation only; see gotcha above). */
24
+ export declare const nextRequestId: () => number;
25
+ /** Build a JSON-RPC 2.0 request envelope around arbitrary by-name `params`. */
26
+ export declare const buildRequest: <P>(params: P, id: number) => JsonRpcRequest<P>;
27
+ /**
28
+ * The `error.data` payload. Odoo guarantees only `name` (the dotted Python
29
+ * exception class); every other field can be absent depending on the fault and
30
+ * the server's `dev_mode`. Decoded leniently, then normalized to
31
+ * ""/[]/{} before it reaches {@link mapServerFault}.
32
+ */
33
+ export declare const JsonRpcErrorData: Schema.Struct<{
34
+ readonly name: Schema.String;
35
+ readonly debug: Schema.optional<Schema.String>;
36
+ readonly message: Schema.optional<Schema.String>;
37
+ readonly arguments: Schema.optional<Schema.$Array<Schema.Unknown>>;
38
+ readonly context: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
39
+ }>;
40
+ export type JsonRpcErrorData = typeof JsonRpcErrorData.Type;
41
+ /** The `error` member of a JSON-RPC error response. `data` may be absent. */
42
+ export declare const JsonRpcErrorPayload: Schema.Struct<{
43
+ readonly code: Schema.Number;
44
+ readonly message: Schema.String;
45
+ readonly data: Schema.optional<Schema.Struct<{
46
+ readonly name: Schema.String;
47
+ readonly debug: Schema.optional<Schema.String>;
48
+ readonly message: Schema.optional<Schema.String>;
49
+ readonly arguments: Schema.optional<Schema.$Array<Schema.Unknown>>;
50
+ readonly context: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
51
+ }>>;
52
+ }>;
53
+ export type JsonRpcErrorPayload = typeof JsonRpcErrorPayload.Type;
54
+ /** A JSON-RPC error response: `{jsonrpc, id?, error:{code, message, data?}}`. */
55
+ export declare const JsonRpcErrorResponse: Schema.Struct<{
56
+ readonly jsonrpc: Schema.Literal<"2.0">;
57
+ readonly id: Schema.optional<Schema.Union<readonly [Schema.Number, Schema.String, Schema.Null]>>;
58
+ readonly error: Schema.Struct<{
59
+ readonly code: Schema.Number;
60
+ readonly message: Schema.String;
61
+ readonly data: Schema.optional<Schema.Struct<{
62
+ readonly name: Schema.String;
63
+ readonly debug: Schema.optional<Schema.String>;
64
+ readonly message: Schema.optional<Schema.String>;
65
+ readonly arguments: Schema.optional<Schema.$Array<Schema.Unknown>>;
66
+ readonly context: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
67
+ }>>;
68
+ }>;
69
+ }>;
70
+ export type JsonRpcErrorResponse = typeof JsonRpcErrorResponse.Type;
71
+ export declare const JsonRpcSuccessResponse: Schema.Union<readonly [Schema.Struct<{
72
+ readonly jsonrpc: Schema.Literal<"2.0">;
73
+ readonly id: Schema.optional<Schema.Union<readonly [Schema.Number, Schema.String, Schema.Null]>>;
74
+ readonly result: Schema.Unknown;
75
+ readonly error: Schema.optionalKey<Schema.Never>;
76
+ }>, Schema.Struct<{
77
+ readonly jsonrpc: Schema.Literal<"2.0">;
78
+ readonly id: Schema.Union<readonly [Schema.Number, Schema.String, Schema.Null]>;
79
+ readonly result: Schema.withDecodingDefaultTypeKey<Schema.Unknown, never>;
80
+ readonly error: Schema.optionalKey<Schema.Never>;
81
+ }>]>;
82
+ export type JsonRpcSuccessResponse = typeof JsonRpcSuccessResponse.Type;
83
+ /**
84
+ * A whole JSON-RPC response. The error branch is tried first so a fault is
85
+ * never mistaken for a `result: undefined` success. A body that is neither
86
+ * shape (e.g. a proxy's HTML error page decoded as garbage) fails to decode →
87
+ * the caller raises `SchemaDriftError`.
88
+ */
89
+ export declare const JsonRpcResponse: Schema.Union<readonly [Schema.Struct<{
90
+ readonly jsonrpc: Schema.Literal<"2.0">;
91
+ readonly id: Schema.optional<Schema.Union<readonly [Schema.Number, Schema.String, Schema.Null]>>;
92
+ readonly error: Schema.Struct<{
93
+ readonly code: Schema.Number;
94
+ readonly message: Schema.String;
95
+ readonly data: Schema.optional<Schema.Struct<{
96
+ readonly name: Schema.String;
97
+ readonly debug: Schema.optional<Schema.String>;
98
+ readonly message: Schema.optional<Schema.String>;
99
+ readonly arguments: Schema.optional<Schema.$Array<Schema.Unknown>>;
100
+ readonly context: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
101
+ }>>;
102
+ }>;
103
+ }>, Schema.Union<readonly [Schema.Struct<{
104
+ readonly jsonrpc: Schema.Literal<"2.0">;
105
+ readonly id: Schema.optional<Schema.Union<readonly [Schema.Number, Schema.String, Schema.Null]>>;
106
+ readonly result: Schema.Unknown;
107
+ readonly error: Schema.optionalKey<Schema.Never>;
108
+ }>, Schema.Struct<{
109
+ readonly jsonrpc: Schema.Literal<"2.0">;
110
+ readonly id: Schema.Union<readonly [Schema.Number, Schema.String, Schema.Null]>;
111
+ readonly result: Schema.withDecodingDefaultTypeKey<Schema.Unknown, never>;
112
+ readonly error: Schema.optionalKey<Schema.Never>;
113
+ }>]>]>;
114
+ export type JsonRpcResponse = typeof JsonRpcResponse.Type;
115
+ /**
116
+ * The single JSON-RPC fault choke point. Both `/jsonrpc` and the web route feed
117
+ * their decoded `error` payload through here so every fault normalizes into the
118
+ * same tagged union.
119
+ *
120
+ * - code `100` → {@link SessionExpiredError} (Odoo's `SessionExpiredException`).
121
+ * - code `404` → {@link OdooServerError} (NotFound; the `data.name` is often a
122
+ * werkzeug class we do not map, so it lands on the base fault by design).
123
+ * - otherwise → {@link mapServerFault} on the normalized `data`; a missing
124
+ * `data` becomes name `"unknown"`, which falls back to {@link OdooServerError}.
125
+ */
126
+ export declare const mapJsonRpcError: (error: JsonRpcErrorPayload, site?: FaultCallSite) => SessionExpiredError | OdooServerFault | OdooAuthenticationError;
127
+ //# sourceMappingURL=jsonrpc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonrpc.d.ts","sourceRoot":"","sources":["../../src/protocol/jsonrpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAQD,kFAAkF;AAClF,eAAO,MAAM,aAAa,QAAO,MAAiC,CAAC;AAEnE,+EAA+E;AAC/E,eAAO,MAAM,YAAY,GAAI,CAAC,UAAU,CAAC,MAAM,MAAM,KAAG,cAAc,CAAC,CAAC,CAKtE,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;;;;;EAM3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,gBAAgB,CAAC,IAAI,CAAC;AAE5D,6EAA6E;AAC7E,eAAO,MAAM,mBAAmB;;;;;;;;;;EAI9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAC,IAAI,CAAC;AAMlE,iFAAiF;AACjF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;EAI/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,OAAO,oBAAoB,CAAC,IAAI,CAAC;AAwBpE,eAAO,MAAM,sBAAsB;;;;;;;;;;IAGjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,OAAO,sBAAsB,CAAC,IAAI,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;MAA+D,CAAC;AAC5F,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC,IAAI,CAAC;AAe1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,UACnB,mBAAmB,SACpB,aAAa,KAClB,mBAAmB,GAAG,eAAe,GAAG,uBAoB1C,CAAC"}
@@ -0,0 +1,117 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { mapServerFault } from "../errors/mapFault.js";
3
+ import { OdooServerError } from "../errors/server.js";
4
+ import { SessionExpiredError } from "../errors/session.js";
5
+ // GOTCHA: Odoo ignores the JSON-RPC `id` entirely — it never round-trips into
6
+ // dispatch. This process-wide monotonic counter exists only to keep concurrent
7
+ // requests distinguishable in logs/traces; module-level mutable state is
8
+ // acceptable precisely because nothing correctness-bearing reads it back.
9
+ let requestIdCounter = 0;
10
+ /** Next distinct JSON-RPC request id (log-correlation only; see gotcha above). */
11
+ export const nextRequestId = () => (requestIdCounter += 1);
12
+ /** Build a JSON-RPC 2.0 request envelope around arbitrary by-name `params`. */
13
+ export const buildRequest = (params, id) => ({
14
+ jsonrpc: "2.0",
15
+ method: "call",
16
+ params,
17
+ id,
18
+ });
19
+ /**
20
+ * The `error.data` payload. Odoo guarantees only `name` (the dotted Python
21
+ * exception class); every other field can be absent depending on the fault and
22
+ * the server's `dev_mode`. Decoded leniently, then normalized to
23
+ * ""/[]/{} before it reaches {@link mapServerFault}.
24
+ */
25
+ export const JsonRpcErrorData = Schema.Struct({
26
+ name: Schema.String,
27
+ debug: Schema.optional(Schema.String),
28
+ message: Schema.optional(Schema.String),
29
+ arguments: Schema.optional(Schema.Array(Schema.Unknown)),
30
+ context: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
31
+ });
32
+ /** The `error` member of a JSON-RPC error response. `data` may be absent. */
33
+ export const JsonRpcErrorPayload = Schema.Struct({
34
+ code: Schema.Number,
35
+ message: Schema.String,
36
+ data: Schema.optional(JsonRpcErrorData),
37
+ });
38
+ // `id` echoes what we sent, but a few Odoo error paths return `null` — accept
39
+ // any of the JSON-RPC-legal id shapes rather than drift on the envelope.
40
+ const JsonRpcId = Schema.Union([Schema.Number, Schema.String, Schema.Null]);
41
+ /** A JSON-RPC error response: `{jsonrpc, id?, error:{code, message, data?}}`. */
42
+ export const JsonRpcErrorResponse = Schema.Struct({
43
+ jsonrpc: Schema.Literal("2.0"),
44
+ id: Schema.optional(JsonRpcId),
45
+ error: JsonRpcErrorPayload,
46
+ });
47
+ /**
48
+ * A JSON-RPC success response: `{jsonrpc, id?, result?}` (result is opaque).
49
+ *
50
+ * Odoo 16 and 17 omit the member entirely when a Python method returns
51
+ * `None`. Decode that wire quirk to an explicit `undefined`, so consumers have
52
+ * one stable successful-response shape.
53
+ */
54
+ const JsonRpcResultSuccessResponse = Schema.Struct({
55
+ jsonrpc: Schema.Literal("2.0"),
56
+ id: Schema.optional(JsonRpcId),
57
+ result: Schema.Unknown,
58
+ error: Schema.optionalKey(Schema.Never),
59
+ });
60
+ const JsonRpcVoidSuccessResponse = Schema.Struct({
61
+ jsonrpc: Schema.Literal("2.0"),
62
+ id: JsonRpcId,
63
+ result: Schema.Unknown.pipe(Schema.withDecodingDefaultTypeKey(Effect.void)),
64
+ // Declare this forbidden member so malformed error envelopes cannot fall
65
+ // through to the missing-result success shape in the union below.
66
+ error: Schema.optionalKey(Schema.Never),
67
+ });
68
+ export const JsonRpcSuccessResponse = Schema.Union([
69
+ JsonRpcResultSuccessResponse,
70
+ JsonRpcVoidSuccessResponse,
71
+ ]);
72
+ /**
73
+ * A whole JSON-RPC response. The error branch is tried first so a fault is
74
+ * never mistaken for a `result: undefined` success. A body that is neither
75
+ * shape (e.g. a proxy's HTML error page decoded as garbage) fails to decode →
76
+ * the caller raises `SchemaDriftError`.
77
+ */
78
+ export const JsonRpcResponse = Schema.Union([JsonRpcErrorResponse, JsonRpcSuccessResponse]);
79
+ /** Normalize a lenient `error.data` (or its absence) into a {@link RawServerFault}. */
80
+ const normalizeFault = (data, fallbackMessage) => ({
81
+ name: data?.name ?? "unknown",
82
+ message: data?.message ?? fallbackMessage,
83
+ arguments: data?.arguments ?? [],
84
+ context: data?.context ?? {},
85
+ // exactOptionalPropertyTypes: attach `debug` only when actually present.
86
+ ...(data?.debug !== undefined ? { debug: data.debug } : {}),
87
+ });
88
+ /**
89
+ * The single JSON-RPC fault choke point. Both `/jsonrpc` and the web route feed
90
+ * their decoded `error` payload through here so every fault normalizes into the
91
+ * same tagged union.
92
+ *
93
+ * - code `100` → {@link SessionExpiredError} (Odoo's `SessionExpiredException`).
94
+ * - code `404` → {@link OdooServerError} (NotFound; the `data.name` is often a
95
+ * werkzeug class we do not map, so it lands on the base fault by design).
96
+ * - otherwise → {@link mapServerFault} on the normalized `data`; a missing
97
+ * `data` becomes name `"unknown"`, which falls back to {@link OdooServerError}.
98
+ */
99
+ export const mapJsonRpcError = (error, site = {}) => {
100
+ if (error.code === 100) {
101
+ return new SessionExpiredError({ message: error.message });
102
+ }
103
+ const raw = normalizeFault(error.data, error.message);
104
+ if (error.code === 404) {
105
+ return new OdooServerError({
106
+ name: raw.name,
107
+ message: raw.message,
108
+ arguments: raw.arguments,
109
+ context: raw.context,
110
+ ...(raw.debug !== undefined ? { debug: raw.debug } : {}),
111
+ ...(site.model !== undefined ? { model: site.model } : {}),
112
+ ...(site.method !== undefined ? { method: site.method } : {}),
113
+ });
114
+ }
115
+ return mapServerFault(raw, site);
116
+ };
117
+ //# sourceMappingURL=jsonrpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonrpc.js","sourceRoot":"","sources":["../../src/protocol/jsonrpc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Typed records — internal barrel for both per-protocol tiers of the records
3
+ * design: the CLASSIC tier (explicit traversal over the classic `[id, name]`
4
+ * pair protocol, 16+: relation/temporal decode schemas, pure join helpers,
5
+ * the `TypedRecordSet` snapshot) and the SPEC tier (declared prefetch over the
6
+ * 17+ `specification` protocol: `defineRecord` + the specification compiler).
7
+ * Re-exported to the public surface via `src/index.ts`.
8
+ */
9
+ export { Many2OneRef, Many2OneRefFromWire, Many2OneRefOrNull, Many2OneRefValue, OdooDate, OdooDateOrNull, OdooDateTime, OdooDateTimeOrNull, } from "./relations.js";
10
+ export { collectRefIds, makeRelatedMap, refId, type RefOrId, type RelatedMap, } from "./related.js";
11
+ export { make as makeTypedRecordSet, type HasId, type Many2OneRefField, type TypedRecordSet, } from "./typed.js";
12
+ export { defineRecord, Many2One, One2Many, type FieldInput, type FieldMeta, type Many2OneDecl, type One2ManyDecl, type RecordSpec, type RowEncoded, type RowType, } from "./recordModel.js";
13
+ export { compileSpecification, EmptyRecordSpecError, hasRelations, RecordSpecCycleError, } from "./spec.js";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/records/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,aAAa,EACb,cAAc,EACd,KAAK,EACL,KAAK,OAAO,EACZ,KAAK,UAAU,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,IAAI,IAAI,kBAAkB,EAC1B,KAAK,KAAK,EACV,KAAK,gBAAgB,EACrB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,OAAO,GACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,GACrB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Typed records — internal barrel for both per-protocol tiers of the records
3
+ * design: the CLASSIC tier (explicit traversal over the classic `[id, name]`
4
+ * pair protocol, 16+: relation/temporal decode schemas, pure join helpers,
5
+ * the `TypedRecordSet` snapshot) and the SPEC tier (declared prefetch over the
6
+ * 17+ `specification` protocol: `defineRecord` + the specification compiler).
7
+ * Re-exported to the public surface via `src/index.ts`.
8
+ */
9
+ export { Many2OneRef, Many2OneRefFromWire, Many2OneRefOrNull, Many2OneRefValue, OdooDate, OdooDateOrNull, OdooDateTime, OdooDateTimeOrNull, } from "./relations.js";
10
+ export { collectRefIds, makeRelatedMap, refId, } from "./related.js";
11
+ export { make as makeTypedRecordSet, } from "./typed.js";
12
+ export { defineRecord, Many2One, One2Many, } from "./recordModel.js";
13
+ export { compileSpecification, EmptyRecordSpecError, hasRelations, RecordSpecCycleError, } from "./spec.js";
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/records/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,aAAa,EACb,cAAc,EACd,KAAK,GAGN,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,IAAI,IAAI,kBAAkB,GAI3B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,GAQT,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,GACrB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Declared record models — the SPEC tier of the records design ("declared
3
+ * prefetch" over the 17+ `specification` protocol; the classic tier in
4
+ * `typed.ts`/`related.ts` speaks the 16+ `[id, name]`-pair protocol instead).
5
+ * A `RecordSpec`
6
+ * declares a model's field graph with `effect/Schema`: scalars decode directly,
7
+ * relations declared with {@link Many2One}/{@link One2Many} carry a nested child
8
+ * `RecordSpec`. From one declaration we derive BOTH:
9
+ *
10
+ * 1. a row {@link RecordSpec.schema} that strict-decodes the NESTED wire shape
11
+ * the 17+ `web_search_read`/`web_read` `specification` protocol returns
12
+ * (a many2one as a `{ id, ...declared }` dict or `false`; an x2many as a
13
+ * nested list), and
14
+ * 2. the field {@link RecordSpec.fields} metadata that {@link ./spec.ts} compiles
15
+ * into that `specification` payload.
16
+ *
17
+ * No I/O lives here — this is pure declaration. The single round trip that turns
18
+ * a spec into rows is in `OdooClient.searchTyped`/`readTyped`/`saveTyped`.
19
+ *
20
+ * MANY2ONE VALUE SHAPE (decision, records design 2026-07-10). A declared
21
+ * many2one decodes to a FLAT struct `{ id, ...declaredChildFields } | null`, not
22
+ * a `{ id, value }` wrapper. In TS, `| null` already carries "is it set?", and a
23
+ * flat struct exposes `.id` and every declared child field as directly-typed
24
+ * properties (`company.name`) with no `__getattr__`-style proxy and no extra
25
+ * `.value` hop — strictly better inference and ergonomics than a wrapper. This
26
+ * deliberately diverges from the Python design's `.value` recommendation, which
27
+ * existed to model presence separately from the record; TS does not need it.
28
+ */
29
+ import { Schema } from "effect";
30
+ import type { HasId } from "./typed.js";
31
+ /**
32
+ * Per-field compilation metadata: how {@link ./spec.ts} renders each field. The
33
+ * relation `child` is typed loosely (`RecordSpec<any, any>`) because `Schema` is
34
+ * invariant in its type parameter — a precisely-typed child spec is not
35
+ * assignable to a widened one, so the metadata graph is deliberately erased. The
36
+ * precise types live on {@link RecordSpec.schema} for decode; this metadata is
37
+ * only ever walked structurally by the pure compiler.
38
+ */
39
+ export type FieldMeta = {
40
+ readonly kind: "scalar";
41
+ } | {
42
+ readonly kind: "many2one";
43
+ readonly child: RecordSpec<any, any>;
44
+ } | {
45
+ readonly kind: "one2many";
46
+ readonly child: RecordSpec<any, any>;
47
+ readonly limit: number | undefined;
48
+ };
49
+ /**
50
+ * A declared record model: its Odoo model name, the row decode `schema` (the
51
+ * whole nested graph), the per-field compilation `fields`, and whether it
52
+ * declares any relation (the version gate keys on this). Build one with
53
+ * {@link defineRecord}.
54
+ */
55
+ export interface RecordSpec<A extends HasId, I = A> {
56
+ readonly model: string;
57
+ readonly schema: Schema.Codec<A, I>;
58
+ readonly fields: Readonly<Record<string, FieldMeta>>;
59
+ /** `true` iff at least one declared field is a many2one/one2many relation. */
60
+ readonly hasRelations: boolean;
61
+ }
62
+ /** A declared many2one relation field — nests a child {@link RecordSpec}. */
63
+ export interface Many2OneDecl<A extends HasId, I> {
64
+ readonly _decl: "many2one";
65
+ readonly spec: RecordSpec<A, I>;
66
+ }
67
+ /** A declared one2many/many2many relation field — nests a child {@link RecordSpec}. */
68
+ export interface One2ManyDecl<A extends HasId, I> {
69
+ readonly _decl: "one2many";
70
+ readonly spec: RecordSpec<A, I>;
71
+ readonly limit: number | undefined;
72
+ }
73
+ /**
74
+ * Declare a many2one field: `company_id: Many2One(Company)`. Decodes the nested
75
+ * `{ id, ...Company fields }` wire dict to `Company | null` (Odoo's `false`
76
+ * empty → `null`). Note it does NOT accept the classic `[id, name]` pair — under
77
+ * a `specification` the server returns a dict, so a leaked pair is drift, not a
78
+ * silent decode.
79
+ */
80
+ export declare const Many2One: <A extends HasId, I>(spec: RecordSpec<A, I>) => Many2OneDecl<A, I>;
81
+ /**
82
+ * Declare an x2many field: `child_ids: One2Many(Contact)` or
83
+ * `One2Many(Contact, { limit: 20 })`. Decodes the nested list to
84
+ * `ReadonlyArray<Contact>`; `limit`, when given, rides the compiled
85
+ * `specification` so the server bounds the co-recordset.
86
+ */
87
+ export declare const One2Many: <A extends HasId, I>(spec: RecordSpec<A, I>, options?: {
88
+ readonly limit?: number;
89
+ }) => One2ManyDecl<A, I>;
90
+ /**
91
+ * A field of a {@link defineRecord} declaration: a scalar schema or a relation.
92
+ * The relation members are `any`-parameterized because `Schema` (inside the
93
+ * nested child spec) is invariant — a precisely-typed `Many2One(Company)` is not
94
+ * assignable to a widened `Many2OneDecl<HasId, unknown>`. Field-level types are
95
+ * recovered structurally by {@link FieldType}/{@link FieldEncoded} via `infer`.
96
+ */
97
+ export type FieldInput = Schema.Top | Many2OneDecl<any, any> | One2ManyDecl<any, any>;
98
+ /** The decoded (`Type`) value a declared field yields. */
99
+ type FieldType<T> = T extends Many2OneDecl<infer A, infer _I> ? A | null : T extends One2ManyDecl<infer A, infer _I> ? ReadonlyArray<A> : T extends Schema.Top ? T["Type"] : never;
100
+ /** The wire (`Encoded`) value a declared field maps from. */
101
+ type FieldEncoded<T> = T extends Many2OneDecl<infer _A, infer I> ? I | false : T extends One2ManyDecl<infer _A, infer I> ? ReadonlyArray<I> : T extends Schema.Top ? T["Encoded"] : never;
102
+ /** The decoded row type of a declaration `F` — always carries `id: number`. */
103
+ export type RowType<F extends Record<string, FieldInput>> = {
104
+ readonly id: number;
105
+ } & {
106
+ readonly [K in keyof F]: FieldType<F[K]>;
107
+ };
108
+ /** The wire row type of a declaration `F`. */
109
+ export type RowEncoded<F extends Record<string, FieldInput>> = {
110
+ readonly id: number;
111
+ } & {
112
+ readonly [K in keyof F]: FieldEncoded<F[K]>;
113
+ };
114
+ /**
115
+ * Build a {@link RecordSpec} from a model name and a field declaration. `id`
116
+ * (`Schema.Number`) is added automatically and always compiled into the
117
+ * `specification`, so every declared record and every relation subtree carries
118
+ * its id — the key downstream joins and re-reads rely on.
119
+ *
120
+ * ```ts
121
+ * const Company = defineRecord("res.company", { name: Schema.String });
122
+ * const Partner = defineRecord("res.partner", {
123
+ * name: Schema.String,
124
+ * company_id: Many2One(Company),
125
+ * child_ids: One2Many(Partner_contact, { limit: 20 }),
126
+ * });
127
+ * ```
128
+ */
129
+ export declare const defineRecord: <F extends Record<string, FieldInput>>(model: string, fields: F) => RecordSpec<RowType<F>, RowEncoded<F>>;
130
+ export {};
131
+ //# sourceMappingURL=recordModel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recordModel.d.ts","sourceRoot":"","sources":["../../src/records/recordModel.ts"],"names":[],"mappings":"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"}