@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
package/dist/client.js ADDED
@@ -0,0 +1,277 @@
1
+ import { Context, Effect, Layer, Option, Schema } from "effect";
2
+ import { normalizeDomain } from "./domain.js";
3
+ import { ProtocolUnsupportedError } from "./errors/protocol.js";
4
+ import { SchemaDriftError } from "./errors/schema.js";
5
+ import { OdooMissingError } from "./errors/server.js";
6
+ import { compileSpecification, makeTypedRecordSet, } from "./records/index.js";
7
+ import { Rpc } from "./rpc.js";
8
+ import { VersionResolver } from "./version.js";
9
+ const UnknownRecord = Schema.Record(Schema.String, Schema.Unknown);
10
+ const RecordArray = Schema.Array(UnknownRecord);
11
+ const IdArray = Schema.Array(Schema.Number);
12
+ const FieldsGetResult = Schema.Record(Schema.String, UnknownRecord);
13
+ const TrueLiteral = Schema.Literal(true);
14
+ /** A `(id, display_name)` pair, as returned by `name_search`/`name_get`. */
15
+ const NamePair = Schema.Tuple([Schema.Number, Schema.String]);
16
+ const NamePairArray = Schema.Array(NamePair);
17
+ /** `check_object_reference` → `(model, res_id)`; `res_id` is `false` when the
18
+ * xml_id resolves but is not visible to the caller. */
19
+ const ObjectReference = Schema.Tuple([
20
+ Schema.String,
21
+ Schema.Union([Schema.Number, Schema.Literal(false)]),
22
+ ]);
23
+ /** `web_search_read` returns `{ length, records }`, not a bare list. */
24
+ const WebSearchReadResult = (row) => Schema.Struct({ length: Schema.Number, records: Schema.Array(row) });
25
+ /** Drop `undefined`-valued keys so we never wire an explicit `limit: null`. */
26
+ const compact = (obj) => {
27
+ const out = {};
28
+ for (const [key, value] of Object.entries(obj)) {
29
+ if (value !== undefined) {
30
+ out[key] = value;
31
+ }
32
+ }
33
+ return out;
34
+ };
35
+ const decode = (schema, context) => (raw) => Schema.decodeUnknownEffect(schema)(raw).pipe(Effect.mapError((cause) => new SchemaDriftError({ context, payload: raw, cause })));
36
+ /**
37
+ * Hand-typed, boundary-decoded high-level Odoo operations over {@link Rpc}.
38
+ * Every result is decoded through `effect/Schema`; a wire-shape mismatch fails
39
+ * with {@link SchemaDriftError} carrying the raw payload, never a silent cast.
40
+ */
41
+ export class OdooClient extends Context.Service()("odoo-rpc-ts/OdooClient") {
42
+ }
43
+ /** The {@link OdooClient} layer over an {@link Rpc} service. */
44
+ export const layer = Layer.effect(OdooClient, Effect.gen(function* () {
45
+ const rpc = yield* Rpc;
46
+ const withContext = (context) => context === undefined ? undefined : { context };
47
+ const searchRead = (model, options, schema) => {
48
+ // Rows decode FROM the wire shape (I) TO the domain shape (A), so
49
+ // transforming schemas (DateFromString, false->null, ...) are first-class.
50
+ const rowSchema = (schema ?? UnknownRecord);
51
+ const kwargs = compact({
52
+ domain: normalizeDomain(options?.domain ?? []),
53
+ fields: options?.fields,
54
+ limit: options?.limit,
55
+ offset: options?.offset,
56
+ order: options?.order,
57
+ });
58
+ return rpc
59
+ .callKw(model, "search_read", [], kwargs, withContext(options?.context))
60
+ .pipe(Effect.flatMap(decode(Schema.Array(rowSchema), `${model}.search_read`)));
61
+ };
62
+ const searchRecordsTyped = (model, options, schema) =>
63
+ // Reuse the search_read decode plumbing, then wrap the decoded rows in a
64
+ // snapshot bound to this Rpc seam so fetchRelated can batch a co-model read.
65
+ searchRead(model, options, schema).pipe(Effect.map((rows) => makeTypedRecordSet(rpc, model, rows)));
66
+ /**
67
+ * Resolve whether the `specification` path is available. A {@link VersionResolver}
68
+ * in scope wins (its capability is authoritative); absent, an explicit
69
+ * `serverMajor` gates; absent both, we DEFAULT to the spec path (17+
70
+ * assumed) — documented on {@link TypedOptions.serverMajor}, and a 16 server
71
+ * then surfaces the server's own fault through the choke point.
72
+ */
73
+ const resolveSupportsSpec = (serverMajor) => Effect.serviceOption(VersionResolver).pipe(Effect.flatMap((opt) => Option.match(opt, {
74
+ onSome: (vr) => vr.resolve.pipe(Effect.map((r) => ({
75
+ supports: r.capabilities.supportsWebReadSpec,
76
+ label: r.version.raw.join("."),
77
+ }))),
78
+ onNone: () => serverMajor === undefined
79
+ ? Effect.succeed({ supports: true, label: "unknown (assumed 17+)" })
80
+ : Effect.succeed({ supports: serverMajor >= 17, label: `${serverMajor}.x` }),
81
+ })));
82
+ /** `requireSpec` (saveTyped) forbids the degrade branch even for a
83
+ * relation-free spec — `web_save` is 17+ only. */
84
+ const gateSpec = (hasRelations, requireSpec, serverMajor) => resolveSupportsSpec(serverMajor).pipe(Effect.map(({ supports, label }) => {
85
+ if (supports) {
86
+ return { _tag: "spec" };
87
+ }
88
+ if (requireSpec || hasRelations) {
89
+ return { _tag: "unsupported", serverVersion: label };
90
+ }
91
+ return { _tag: "degrade" };
92
+ }));
93
+ const specUnsupported = (model, method, serverVersion) => new ProtocolUnsupportedError({
94
+ protocol: "web",
95
+ serverVersion,
96
+ message: `${model}.${method}: the web_read 'specification' protocol (declared-prefetch typed ` +
97
+ `records) requires Odoo 17+, but the server is ${serverVersion}. For a relation-free ` +
98
+ `read on 16 use searchTyped/readTyped (they degrade to search_read/read); for ` +
99
+ `relation traversal on 16 use searchRecordsTyped + fetchRelated (the classic tier).`,
100
+ });
101
+ const declaredFields = (record) => Object.keys(record.fields);
102
+ const searchTyped = (record, options) => gateSpec(record.hasRelations, false, options?.serverMajor).pipe(Effect.flatMap((gate) => {
103
+ if (gate._tag === "unsupported") {
104
+ return Effect.fail(specUnsupported(record.model, "web_search_read", gate.serverVersion));
105
+ }
106
+ if (gate._tag === "degrade") {
107
+ // Relation-free on < 17: plain search_read over the declared fields;
108
+ // the row schema decodes both wire shapes identically.
109
+ return searchRead(record.model, { ...options, fields: declaredFields(record) }, record.schema);
110
+ }
111
+ return Effect.sync(() => compileSpecification(record)).pipe(Effect.flatMap((specification) => {
112
+ const kwargs = compact({
113
+ specification,
114
+ domain: normalizeDomain(options?.domain ?? []),
115
+ limit: options?.limit,
116
+ offset: options?.offset,
117
+ order: options?.order,
118
+ });
119
+ return rpc.callKw(record.model, "web_search_read", [], kwargs, withContext(options?.context));
120
+ }), Effect.flatMap(decode(WebSearchReadResult(record.schema), `${record.model}.web_search_read`)), Effect.map((result) => result.records));
121
+ }));
122
+ const readTyped = (record, ids, options) => gateSpec(record.hasRelations, false, options?.serverMajor).pipe(Effect.flatMap((gate) => {
123
+ if (gate._tag === "unsupported") {
124
+ return Effect.fail(specUnsupported(record.model, "web_read", gate.serverVersion));
125
+ }
126
+ const seam = {
127
+ ids,
128
+ ...(options?.context !== undefined ? { context: options.context } : {}),
129
+ };
130
+ if (gate._tag === "degrade") {
131
+ return rpc
132
+ .callKw(record.model, "read", [], { fields: declaredFields(record) }, seam)
133
+ .pipe(Effect.flatMap(decode(Schema.Array(record.schema), `${record.model}.read`)));
134
+ }
135
+ return Effect.sync(() => compileSpecification(record)).pipe(Effect.flatMap((specification) => rpc.callKw(record.model, "web_read", [], { specification }, seam)), Effect.flatMap(decode(Schema.Array(record.schema), `${record.model}.web_read`)));
136
+ }));
137
+ const saveTyped = (record, ids, values, options) => gateSpec(record.hasRelations, true, options?.serverMajor).pipe(Effect.flatMap((gate) => {
138
+ if (gate._tag === "unsupported") {
139
+ return Effect.fail(specUnsupported(record.model, "web_save", gate.serverVersion));
140
+ }
141
+ const seam = {
142
+ ids,
143
+ ...(options?.context !== undefined ? { context: options.context } : {}),
144
+ };
145
+ return Effect.sync(() => compileSpecification(record)).pipe(Effect.flatMap((specification) =>
146
+ // vals is positional over execute_kw (call_kw reads args[0]) and
147
+ // keyword-only (`vals`) over JSON-2 — mirror create's dialect split.
148
+ rpc.dialect === "json2"
149
+ ? rpc.callKw(record.model, "web_save", [], { vals: values, specification }, seam)
150
+ : rpc.callKw(record.model, "web_save", [values], { specification }, seam)), Effect.flatMap(decode(Schema.Array(record.schema), `${record.model}.web_save`)));
151
+ }));
152
+ const search = (model, options) => {
153
+ const kwargs = compact({
154
+ domain: normalizeDomain(options?.domain ?? []),
155
+ limit: options?.limit,
156
+ offset: options?.offset,
157
+ order: options?.order,
158
+ });
159
+ return rpc
160
+ .callKw(model, "search", [], kwargs, withContext(options?.context))
161
+ .pipe(Effect.flatMap(decode(IdArray, `${model}.search`)));
162
+ };
163
+ // The record-targeting ops are protocol-agnostic: the browse ids ride on the
164
+ // transport seam's `ids` (positional on execute_kw, the `ids` body key on
165
+ // JSON-2), and every other argument is passed by its stable Python parameter
166
+ // name (`fields`, `vals`, `vals_list`, `domain`) — `call_kw` binds kwargs by
167
+ // name identically across Odoo 16–19 and all three transports.
168
+ const read = (model, ids, fields) => rpc
169
+ .callKw(model, "read", [], compact({ fields }), { ids })
170
+ .pipe(Effect.flatMap(decode(RecordArray, `${model}.read`)));
171
+ const create = (model, values) => {
172
+ const valsList = Array.isArray(values) ? values : [values];
173
+ // Dialect split (see TransportDialect): call_kw reads create's vals from
174
+ // args[0] unconditionally (16-19), while JSON-2 is keyword-only.
175
+ const call = rpc.dialect === "json2"
176
+ ? rpc.callKw(model, "create", [], { vals_list: valsList })
177
+ : rpc.callKw(model, "create", [valsList]);
178
+ return call.pipe(Effect.flatMap(decode(IdArray, `${model}.create`)));
179
+ };
180
+ const write = (model, ids, values) => {
181
+ // Dialect split, same reason as `create`: over execute_kw the vals MUST be
182
+ // positional — models override write(self, <any param name>) (e.g.
183
+ // project.task uses a different name), so a `vals=` kwarg breaks on real
184
+ // instances (verified live against Odoo 16, 2026-07-10). JSON-2 is
185
+ // keyword-only (the transport rejects positional args) and binds the core
186
+ // signature's `vals` (odoo/orm/models.py, 19.0; verified live 2026-07-10).
187
+ const call = rpc.dialect === "json2"
188
+ ? rpc.callKw(model, "write", [], { vals: values }, { ids })
189
+ : rpc.callKw(model, "write", [values], {}, { ids });
190
+ return call.pipe(Effect.flatMap(decode(TrueLiteral, `${model}.write`)));
191
+ };
192
+ const unlink = (model, ids) => rpc
193
+ .callKw(model, "unlink", [], {}, { ids })
194
+ .pipe(Effect.flatMap(decode(TrueLiteral, `${model}.unlink`)));
195
+ const fieldsGet = (model, options) => rpc
196
+ .callKw(model, "fields_get", [], compact({ attributes: options?.attributes }))
197
+ .pipe(Effect.flatMap(decode(FieldsGetResult, `${model}.fields_get`)));
198
+ const searchCount = (model, domain) => rpc
199
+ .callKw(model, "search_count", [], { domain: normalizeDomain(domain) })
200
+ .pipe(Effect.flatMap(decode(Schema.Number, `${model}.search_count`)));
201
+ const missingRef = (xmlId, source) => new OdooMissingError({
202
+ name: source?.name ?? "odoo.exceptions.MissingError",
203
+ message: source?.message ?? `No record found for external ID '${xmlId}'.`,
204
+ arguments: [xmlId],
205
+ context: source?.context ?? {},
206
+ ...(source?.debug !== undefined ? { debug: source.debug } : {}),
207
+ model: "ir.model.data",
208
+ method: "check_object_reference",
209
+ });
210
+ const ref = (xmlId) => {
211
+ const dot = xmlId.indexOf(".");
212
+ if (dot <= 0 || dot === xmlId.length - 1) {
213
+ return Effect.fail(missingRef(xmlId));
214
+ }
215
+ const module = xmlId.slice(0, dot);
216
+ const name = xmlId.slice(dot + 1);
217
+ // Dialect split: execute_kw sends (module, name) positionally; JSON-2 binds
218
+ // by parameter name (`module`, `xml_id`).
219
+ const call = rpc.dialect === "json2"
220
+ ? rpc.callKw("ir.model.data", "check_object_reference", [], { module, xml_id: name })
221
+ : rpc.callKw("ir.model.data", "check_object_reference", [module, name]);
222
+ return call.pipe(
223
+ // A truly-unknown xml_id makes the server raise ValueError (an unmapped
224
+ // fault → OdooServerError); remap it to a named OdooMissingError.
225
+ Effect.catchTag("OdooServerError", (e) => Effect.fail(missingRef(xmlId, e))), Effect.flatMap(decode(ObjectReference, `${xmlId} check_object_reference`)), Effect.flatMap(([model, resId]) => resId === false
226
+ ? Effect.fail(missingRef(xmlId))
227
+ : Effect.succeed([model, resId])));
228
+ };
229
+ const call = (model, method, options) => rpc.callKw(model, method, [], options?.kwargs ?? {}, compact({ ids: options?.ids, context: options?.context }));
230
+ const readGroup = (model, options) => {
231
+ const kwargs = compact({
232
+ domain: normalizeDomain(options.domain),
233
+ fields: options.fields,
234
+ groupby: options.groupby,
235
+ limit: options.limit,
236
+ offset: options.offset,
237
+ orderby: options.orderby,
238
+ lazy: options.lazy,
239
+ });
240
+ return rpc
241
+ .callKw(model, "read_group", [], kwargs, withContext(options.context))
242
+ .pipe(Effect.flatMap(decode(RecordArray, `${model}.read_group`)));
243
+ };
244
+ const nameSearch = (model, options) => {
245
+ const name = options?.name ?? "";
246
+ const operator = options?.operator ?? "ilike";
247
+ const limit = options?.limit ?? 100;
248
+ const domain = normalizeDomain(options?.args ?? []);
249
+ // Dialect split (see the interface WIRE FACT): positional over execute_kw
250
+ // (version-proof against the args→domain rename), by modern name on JSON-2.
251
+ const call_ = rpc.dialect === "json2"
252
+ ? rpc.callKw(model, "name_search", [], { name, domain, operator, limit })
253
+ : rpc.callKw(model, "name_search", [name, domain, operator, limit]);
254
+ return call_.pipe(Effect.flatMap(decode(NamePairArray, `${model}.name_search`)), Effect.map((pairs) => pairs.map(([id, label]) => [id, label])));
255
+ };
256
+ const nameGet = (model, ids) => rpc.callKw(model, "name_get", [], {}, { ids }).pipe(Effect.flatMap(decode(NamePairArray, `${model}.name_get`)), Effect.map((pairs) => pairs.map(([id, label]) => [id, label])));
257
+ return {
258
+ searchRead,
259
+ searchRecordsTyped,
260
+ searchTyped,
261
+ readTyped,
262
+ saveTyped,
263
+ search,
264
+ read,
265
+ create,
266
+ write,
267
+ unlink,
268
+ fieldsGet,
269
+ searchCount,
270
+ ref,
271
+ call,
272
+ readGroup,
273
+ nameSearch,
274
+ nameGet,
275
+ };
276
+ }));
277
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { Effect } from "effect";
2
+ import type { CookieLoginError, CookieSessionService } from "../session/cookie.js";
3
+ /**
4
+ * Opt-in recovery for a single `SessionExpiredError`: invalidate the cached
5
+ * session, re-authenticate once, then retry `effect` exactly once. A second
6
+ * failure — including another `SessionExpiredError` — propagates untouched.
7
+ *
8
+ * Deliberately policy-free: no backoff, no retry count beyond one. Auto-relogin
9
+ * is NOT wired into the transport (AGENTS.md decision); callers opt in here so
10
+ * the session-expiry signal stays observable by default.
11
+ */
12
+ export declare const retryOnSessionExpired: <A, E, R>(effect: Effect.Effect<A, E, R>, session: CookieSessionService) => Effect.Effect<A, E | CookieLoginError, R>;
13
+ //# sourceMappingURL=retryOnSessionExpired.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retryOnSessionExpired.d.ts","sourceRoot":"","sources":["../../src/combinators/retryOnSessionExpired.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEnF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,UACnC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,WACrB,oBAAoB,KAC5B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,gBAAgB,EAAE,CAAC,CASxC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { Effect } from "effect";
2
+ /**
3
+ * Opt-in recovery for a single `SessionExpiredError`: invalidate the cached
4
+ * session, re-authenticate once, then retry `effect` exactly once. A second
5
+ * failure — including another `SessionExpiredError` — propagates untouched.
6
+ *
7
+ * Deliberately policy-free: no backoff, no retry count beyond one. Auto-relogin
8
+ * is NOT wired into the transport (AGENTS.md decision); callers opt in here so
9
+ * the session-expiry signal stays observable by default.
10
+ */
11
+ export const retryOnSessionExpired = (effect, session) => Effect.catchIf(effect, (error) => typeof error === "object" &&
12
+ error !== null &&
13
+ "_tag" in error &&
14
+ error._tag === "SessionExpiredError", () => session.invalidate.pipe(Effect.andThen(session.login), Effect.andThen(effect)));
15
+ //# sourceMappingURL=retryOnSessionExpired.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retryOnSessionExpired.js","sourceRoot":"","sources":["../../src/combinators/retryOnSessionExpired.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,MAA8B,EAC9B,OAA6B,EACc,EAAE,CAC7C,MAAM,CAAC,OAAO,CACZ,MAAM,EACN,CAAC,KAAK,EAAiE,EAAE,CACvE,OAAO,KAAK,KAAK,QAAQ;IACzB,KAAK,KAAK,IAAI;IACd,MAAM,IAAI,KAAK;IACd,KAAoC,CAAC,IAAI,KAAK,qBAAqB,EACtE,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CACrF,CAAC"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Typed constructors for Odoo's x2many write commands — the special 3-element
3
+ * triples an {@link https://www.odoo.com/documentation | One2many}/Many2many
4
+ * field expects inside a `create`/`write` `values` payload.
5
+ *
6
+ * Each command is `[code, id, payload]`: the first element is the integer that
7
+ * identifies the command, the second is either the target record id (update,
8
+ * delete, unlink, link) or `0`, and the third is either the `values` to write
9
+ * (create, update), the new `ids` list (set), or `0`. Straight port of Odoo's
10
+ * `Command` namespace (`odoo/fields.py` 16.x, `odoo/orm/commands.py` 19.x — the
11
+ * emitted triples are identical across 16–19).
12
+ *
13
+ * GOTCHA: over RPC only the literal triples travel — never the Python `Command`
14
+ * enum members or constant names. These constructors return exactly those
15
+ * literal triples, so a value built here is wire-ready as-is.
16
+ */
17
+ /** Field-name → value map written by the create/update commands. */
18
+ export type CommandValues = {
19
+ readonly [field: string]: unknown;
20
+ };
21
+ /** `create(values)` → `[0, 0, values]` — create a comodel record and link it. */
22
+ export type CreateCommand = readonly [0, 0, CommandValues];
23
+ /** `update(id, values)` → `[1, id, values]` — write `values` on the related record. */
24
+ export type UpdateCommand = readonly [1, number, CommandValues];
25
+ /** `delete(id)` → `[2, id, 0]` — unlink and delete the related record from the db. */
26
+ export type DeleteCommand = readonly [2, number, 0];
27
+ /** `unlink(id)` → `[3, id, 0]` — drop the relation, keep (or cascade) the record. */
28
+ export type UnlinkCommand = readonly [3, number, 0];
29
+ /** `link(id)` → `[4, id, 0]` — add a relation to an existing record. */
30
+ export type LinkCommand = readonly [4, number, 0];
31
+ /** `clear()` → `[5, 0, 0]` — unlink every related record. */
32
+ export type ClearCommand = readonly [5, 0, 0];
33
+ /** `set(ids)` → `[6, 0, ids]` — replace all relations with exactly `ids`. */
34
+ export type SetCommand = readonly [6, 0, ReadonlyArray<number>];
35
+ /** Any single x2many write command triple. */
36
+ export type CommandTuple = CreateCommand | UpdateCommand | DeleteCommand | UnlinkCommand | LinkCommand | ClearCommand | SetCommand;
37
+ /** A list of x2many commands, as it sits inside a `values` payload. */
38
+ export type X2ManyCommands = ReadonlyArray<CommandTuple>;
39
+ /**
40
+ * The `Command` namespace — one constructor per x2many command. Grouped as a
41
+ * frozen object of standalone functions (no `enum`/`namespace`, per
42
+ * `erasableSyntaxOnly`); each carries the wire encoding in its return type so
43
+ * the literal command code survives inference.
44
+ */
45
+ export declare const Command: {
46
+ /**
47
+ * Create new comodel records from `values` and link them to `self`.
48
+ *
49
+ * Wire: `[0, 0, values]`.
50
+ */
51
+ readonly create: (values: CommandValues) => CreateCommand;
52
+ /**
53
+ * Write `values` on the related record `id`.
54
+ *
55
+ * Wire: `[1, id, values]`.
56
+ */
57
+ readonly update: (id: number, values: CommandValues) => UpdateCommand;
58
+ /**
59
+ * Remove the related record `id` from the database and drop its relation.
60
+ *
61
+ * Wire: `[2, id, 0]`.
62
+ */
63
+ readonly delete: (id: number) => DeleteCommand;
64
+ /**
65
+ * Remove the relation to record `id` without deleting it (One2many may
66
+ * cascade-delete when the inverse is `ondelete='cascade'`).
67
+ *
68
+ * Wire: `[3, id, 0]`.
69
+ */
70
+ readonly unlink: (id: number) => UnlinkCommand;
71
+ /**
72
+ * Add a relation between `self` and the existing record `id`.
73
+ *
74
+ * Wire: `[4, id, 0]`.
75
+ */
76
+ readonly link: (id: number) => LinkCommand;
77
+ /**
78
+ * Remove all relations (equivalent to `unlink` on every related record).
79
+ *
80
+ * Wire: `[5, 0, 0]`.
81
+ */
82
+ readonly clear: () => ClearCommand;
83
+ /**
84
+ * Replace the current relations with exactly `ids` (unlink removed, link
85
+ * added).
86
+ *
87
+ * Wire: `[6, 0, ids]`.
88
+ */
89
+ readonly set: (ids: ReadonlyArray<number>) => SetCommand;
90
+ };
91
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,oEAAoE;AACpE,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAElE,iFAAiF;AACjF,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC;AAE3D,uFAAuF;AACvF,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;AAEhE,sFAAsF;AACtF,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAEpD,qFAAqF;AACrF,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAEpD,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAElD,6DAA6D;AAC7D,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AAE9C,6EAA6E;AAC7E,MAAM,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;AAEhE,8CAA8C;AAC9C,MAAM,MAAM,YAAY,GACpB,aAAa,GACb,aAAa,GACb,aAAa,GACb,aAAa,GACb,WAAW,GACX,YAAY,GACZ,UAAU,CAAC;AAEf,uEAAuE;AACvE,MAAM,MAAM,cAAc,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,OAAO;IAClB;;;;OAIG;aACH,MAAM,WAAW,aAAa,KAAG,aAAa;IAE9C;;;;OAIG;aACH,MAAM,OAAO,MAAM,UAAU,aAAa,KAAG,aAAa;IAE1D;;;;OAIG;aACH,MAAM,OAAO,MAAM,KAAG,aAAa;IAEnC;;;;;OAKG;aACH,MAAM,OAAO,MAAM,KAAG,aAAa;IAEnC;;;;OAIG;aACH,IAAI,OAAO,MAAM,KAAG,WAAW;IAE/B;;;;OAIG;aACH,KAAK,QAAM,YAAY;IAEvB;;;;;OAKG;aACH,GAAG,QAAQ,aAAa,CAAC,MAAM,CAAC,KAAG,UAAU;CACrC,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Typed constructors for Odoo's x2many write commands — the special 3-element
3
+ * triples an {@link https://www.odoo.com/documentation | One2many}/Many2many
4
+ * field expects inside a `create`/`write` `values` payload.
5
+ *
6
+ * Each command is `[code, id, payload]`: the first element is the integer that
7
+ * identifies the command, the second is either the target record id (update,
8
+ * delete, unlink, link) or `0`, and the third is either the `values` to write
9
+ * (create, update), the new `ids` list (set), or `0`. Straight port of Odoo's
10
+ * `Command` namespace (`odoo/fields.py` 16.x, `odoo/orm/commands.py` 19.x — the
11
+ * emitted triples are identical across 16–19).
12
+ *
13
+ * GOTCHA: over RPC only the literal triples travel — never the Python `Command`
14
+ * enum members or constant names. These constructors return exactly those
15
+ * literal triples, so a value built here is wire-ready as-is.
16
+ */
17
+ /**
18
+ * The `Command` namespace — one constructor per x2many command. Grouped as a
19
+ * frozen object of standalone functions (no `enum`/`namespace`, per
20
+ * `erasableSyntaxOnly`); each carries the wire encoding in its return type so
21
+ * the literal command code survives inference.
22
+ */
23
+ export const Command = {
24
+ /**
25
+ * Create new comodel records from `values` and link them to `self`.
26
+ *
27
+ * Wire: `[0, 0, values]`.
28
+ */
29
+ create: (values) => [0, 0, values],
30
+ /**
31
+ * Write `values` on the related record `id`.
32
+ *
33
+ * Wire: `[1, id, values]`.
34
+ */
35
+ update: (id, values) => [1, id, values],
36
+ /**
37
+ * Remove the related record `id` from the database and drop its relation.
38
+ *
39
+ * Wire: `[2, id, 0]`.
40
+ */
41
+ delete: (id) => [2, id, 0],
42
+ /**
43
+ * Remove the relation to record `id` without deleting it (One2many may
44
+ * cascade-delete when the inverse is `ondelete='cascade'`).
45
+ *
46
+ * Wire: `[3, id, 0]`.
47
+ */
48
+ unlink: (id) => [3, id, 0],
49
+ /**
50
+ * Add a relation between `self` and the existing record `id`.
51
+ *
52
+ * Wire: `[4, id, 0]`.
53
+ */
54
+ link: (id) => [4, id, 0],
55
+ /**
56
+ * Remove all relations (equivalent to `unlink` on every related record).
57
+ *
58
+ * Wire: `[5, 0, 0]`.
59
+ */
60
+ clear: () => [5, 0, 0],
61
+ /**
62
+ * Replace the current relations with exactly `ids` (unlink removed, link
63
+ * added).
64
+ *
65
+ * Wire: `[6, 0, ids]`.
66
+ */
67
+ set: (ids) => [6, 0, ids],
68
+ };
69
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAuCH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB;;;;OAIG;IACH,MAAM,EAAE,CAAC,MAAqB,EAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAEhE;;;;OAIG;IACH,MAAM,EAAE,CAAC,EAAU,EAAE,MAAqB,EAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC;IAE7E;;;;OAIG;IACH,MAAM,EAAE,CAAC,EAAU,EAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAEjD;;;;;OAKG;IACH,MAAM,EAAE,CAAC,EAAU,EAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAEjD;;;;OAIG;IACH,IAAI,EAAE,CAAC,EAAU,EAAe,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAE7C;;;;OAIG;IACH,KAAK,EAAE,GAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAEpC;;;;;OAKG;IACH,GAAG,EAAE,CAAC,GAA0B,EAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC;CACpD,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { Config, type Redacted } from "effect";
2
+ /**
3
+ * How the client authenticates. A tagged union so downstream auth layers
4
+ * branch on `_tag` rather than probing which secret is present. Both secrets
5
+ * are `Redacted` — never logged, never serialized.
6
+ *
7
+ * - `ApiKey` — server-to-server; the API key is sent as the password on the
8
+ * web/JSON-RPC routes, or as a bearer token on JSON-2.
9
+ * - `Password` — a login password (only valid for accounts without 2FA).
10
+ */
11
+ export type OdooCredentials = {
12
+ readonly _tag: "ApiKey";
13
+ readonly username: string;
14
+ readonly apiKey: Redacted.Redacted<string>;
15
+ } | {
16
+ readonly _tag: "Password";
17
+ readonly username: string;
18
+ readonly password: Redacted.Redacted<string>;
19
+ };
20
+ /** Fully-resolved client configuration. No field has a default: zero assumptions. */
21
+ export interface OdooConfig {
22
+ readonly url: URL;
23
+ readonly db: string;
24
+ readonly credentials: OdooCredentials;
25
+ }
26
+ /**
27
+ * The client configuration, read from environment (or any `ConfigProvider`)
28
+ * under the `ODOO_` prefix: `ODOO_URL`, `ODOO_DB`, `ODOO_USERNAME`, and one of
29
+ * `ODOO_API_KEY` / `ODOO_PASSWORD`.
30
+ */
31
+ export declare const OdooConfig: Config.Config<OdooConfig>;
32
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAuC,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEpF;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GACvB;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC5C,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC9C,CAAC;AAEN,qFAAqF;AACrF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;CACvC;AAwED;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAInB,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,60 @@
1
+ import { Config, Effect, Option, Schema, SchemaIssue } from "effect";
2
+ /**
3
+ * A config validation failure carrying a caller-selected diagnostic value.
4
+ * Secret-bearing inputs must pass a fixed redacted placeholder. v4's
5
+ * `ConfigError` wraps a `SchemaError` (data found but invalid) — the analogue
6
+ * of v3's `ConfigError.InvalidData`.
7
+ */
8
+ const invalidData = (value, message) => new Config.ConfigError(new Schema.SchemaError(new SchemaIssue.InvalidValue(Option.some(value), { message })));
9
+ /**
10
+ * Parse and validate the `ODOO_URL` value: must be a well-formed absolute URL
11
+ * without userinfo, query parameters, or a fragment, and must be `https`
12
+ * unless it targets localhost / a loopback address. TLS is never silently
13
+ * disabled.
14
+ */
15
+ const parseUrl = (raw) => {
16
+ let url;
17
+ try {
18
+ url = new URL(raw);
19
+ }
20
+ catch {
21
+ return Effect.fail(invalidData("<redacted>", "ODOO_URL is not a valid absolute URL"));
22
+ }
23
+ if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") {
24
+ return Effect.fail(invalidData("<redacted>", "ODOO_URL must not contain userinfo, query parameters, or a fragment"));
25
+ }
26
+ const isLoopback = url.hostname === "localhost" ||
27
+ url.hostname === "127.0.0.1" ||
28
+ url.hostname === "[::1]" ||
29
+ url.hostname === "::1";
30
+ if (url.protocol !== "https:" && !isLoopback) {
31
+ return Effect.fail(invalidData("<redacted>", "ODOO_URL must use https unless it targets a loopback host"));
32
+ }
33
+ return Effect.succeed(url);
34
+ };
35
+ const urlConfig = Config.string("URL").pipe(Config.mapOrFail(parseUrl));
36
+ const apiKeyCredentials = Config.all({
37
+ username: Config.string("USERNAME"),
38
+ apiKey: Config.redacted("API_KEY"),
39
+ }).pipe(Config.map((c) => ({ _tag: "ApiKey", ...c })));
40
+ const passwordCredentials = Config.all({
41
+ username: Config.string("USERNAME"),
42
+ password: Config.redacted("PASSWORD"),
43
+ }).pipe(Config.map((c) => ({ _tag: "Password", ...c })));
44
+ /**
45
+ * Prefer an API key when `ODOO_API_KEY` is present; otherwise fall back to a
46
+ * login password (`ODOO_PASSWORD`). If neither secret is set, config resolution
47
+ * fails loudly — there is no anonymous default.
48
+ */
49
+ const credentialsConfig = apiKeyCredentials.pipe(Config.orElse(() => passwordCredentials));
50
+ /**
51
+ * The client configuration, read from environment (or any `ConfigProvider`)
52
+ * under the `ODOO_` prefix: `ODOO_URL`, `ODOO_DB`, `ODOO_USERNAME`, and one of
53
+ * `ODOO_API_KEY` / `ODOO_PASSWORD`.
54
+ */
55
+ export const OdooConfig = Config.all({
56
+ url: urlConfig,
57
+ db: Config.string("DB"),
58
+ credentials: credentialsConfig,
59
+ }).pipe(Config.nested("ODOO"));
60
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}